zoukankan      html  css  js  c++  java
  • 互斥技术----原子变量和自旋锁

    这一章节想要大家学习的就是在多进程或者多线程下:如何不冲突的访问同一个文件或者是同一段共享资源:

    有如下几个机制需要大家来学习:

      原子变量:

        普通变量count++:看起来是一句话:实际是三个步骤:第一:首先要把这个变量在内存当中取到CPU:第二:把这个变量进行++;第三:把这个变量的值送回内存:所以这是分了三个步骤:每一个步骤都有可能被打断,所以对这个值的操作不原子.

        原子:即一气呵成:一旦成功,则所有过程都成功,一旦失败,所有过程都失败.所以原子变量并不是不可被打断的.

        原子变量count++:也是分成三个步骤:前两个步骤与普通变量一样:第三个步骤:在进行往内存写的时候,会检测是否在我取出之后这个变量被重新写入过,如果被写入过,则重新把这个变量读取出来进行++,然后在写入.(这个功能实现的方式是在下面这个atomic.h中的一段内嵌汇编实现的)

    vim arch/arm/include/asm/atomic.h这个头文件是原子变量相关的代码,下面列出一个例子
     39 static inline void atomic_add(int i, atomic_t *v)
     40 {
     41     unsigned long tmp;
     42     int result;
     43 
     44     __asm__ __volatile__("@ atomic_add
    "
     45 "1: ldrex   %0, [%3]
    "
     46 "   add %0, %0, %4
    "
     47 "   strex   %1, %0, [%3]
    "
     48 "   teq %1, #0
    "
     49 "   bne 1b"
     50     : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
     51     : "r" (&v->counter), "Ir" (i)
     52     : "cc");
     53 }

    插曲:中断除了正在运行的指令不可以打断,其他任何过程都可以打断:在不可抢占的内核的一段代码:在之前屏蔽中断,操作之后再打开中断,这样中间的一段代码是不可能被其他任何情况打断了.

    以上就是原子变量理解过程的一些原理讲解:

    下面我们来看下如何运用原子变量:(与自旋锁在同一个代码中)

    下面几个机制来限制共享资源的访问:

      自旋锁:include/linux/spinlock.h

        spinlock_t

        使用原则:无论下面那种情况:在进行共享资源的操作时,都要使用自旋锁.因为方便移植.

        自旋锁:即一直等待,强不到锁就一直等待(忙等),占着CPU不放:举个例子:你比较内急,但是洗手间有人在使用,你就一直在门口等待.什么事都干不了.哈哈!

          自旋锁不睡眠:所以可以在中断上下文使用,当然也可以在进程上下文:如果一直强不到锁,那么程序的效率将会很低.

          注意:

              1):自死锁:(即:自己加了一把锁,然后又要加这把锁,导致自己等待自己释放,这样就产生了自死锁)。

              2)ABBA死锁:两个进程一人抢到一把锁,但是要求抢到两把锁才能进行操作,所以两个进程一直等待对方释放,因此就会出现这样的锁:解决方法就是设定规则:必须先抢到A在强B,才能进行操作

              3)带着锁不能睡眠.

              4)在函数或者程序返回之前一定要解锁:

              5)自旋锁是建议性锁,你不加锁当然也可以操作....哈哈..气人不....只是资源让你给破坏了

        1.如果是单核CPU,且内核不支持抢占.自旋锁无效:

        2.单核CPU,内核支持抢占:加锁==禁止抢占:解锁==使能抢占:这种情况,也可以直接用动态内核抢占方式.

        3.多核CPU:多核CPU产生并发的情况有两种,第一种是多个进程对临界资源的访问,第二种是中断对临界资源的访问。

      自旋锁例子:

      

      1 #include <linux/init.h>
      2 #include <linux/module.h>
      3 #include <linux/sched.h>
      4 #include <linux/cdev.h>
      5 #include <linux/gpio.h>
      6 #include <linux/kdev_t.h>
      7 #include <linux/slab.h>
      8 #include <linux/fs.h>
      9 #include <asm/uaccess.h>
     10 #include <asm/atomic.h>
     11 #include <linux/spinlock.h>
     12 #include <linux/interrupt.h>
     13 #include <mach/gpio.h>
     14 
     15 struct mydev_st {
     16     char buf[256];
     17     dev_t no;
     18     struct cdev dev;
     19 
     20     atomic_t count;
     21 
     22     spinlock_t lock;
     23 
     24     int irq;
     25 };
     26 // /dev/test/mydev
     27 int my_open(struct inode *no, struct file *fp)
     28 {
     29     struct mydev_st *m;
     30 
     31     m = container_of(no->i_cdev, struct mydev_st, dev);
     32     memset(m->buf, 0, 256);
     33     //m->count.couner++;
     34     atomic_add(1, &m->count);
     35     fp->private_data = m;
     36 
     37     //printk("%s
    ", __FUNCTION__);
     38     return 0;
     39 }
     40 
     41 int my_close(struct inode *no, struct file *fp)
     42 {
     43     struct mydev_st *m;
     44 
     45     m = container_of(no->i_cdev, struct mydev_st, dev);
     46     atomic_sub(1, &m->count);
     47 
     48     //printk("%s
    ", __FUNCTION__);
     49     return 0;
     50 }
     51 
     52 ssize_t my_read(struct file *fp, char __user *buffer, size_t count, loff_t *off)
     53 {
     54     struct mydev_st *m;
     55     int ret;
     56     unsigned long flags;
     57 
     58     m = fp->private_data;
     59 
     60     count = min((int)count, 256);
     61 
     62     //spin_lock(&m->lock);  
     63     spin_lock_irqsave(&m->lock, flags);
     64     ret = copy_to_user(buffer, m->buf, count);
     65     if (ret) {
     66         ret = -EFAULT;
     67         //spin_unlock(&m->lock);
     68         spin_unlock_irqrestore(&m->lock, flags);
     69         goto copy_error;
     70     }
     71 
     72 //  spin_unlock(&m->lock);
     73     spin_unlock_irqrestore(&m->lock, flags);
     74     //printk("%s
    ", __FUNCTION__);
     75     return count;
     76 copy_error:
     77     return ret;
     78 }
     79 
     80 ssize_t my_write(struct file *fp, const char __user *buffer, size_t count, loff_t *off)
     81 {
     82     //printk("%s
    ", __FUNCTION__);
     83 
     84     int ret;
     85     unsigned long flags;
     86 
     87     struct mydev_st *m;
     88 
     89     m = fp->private_data;
     90 
     91     count = min((int)count, 256);
     92 
     93     //spin_lock(&m->lock);
     94     //防止中断打断:加锁并禁止中断
     95     spin_lock_irqsave(&m->lock, flags);
     96     ret = copy_from_user(m->buf, buffer, count);
     97     if (ret) {
     98         ret = -EFAULT;
     99         //spin_unlock(&m->lock);
    100         spin_unlock_irqrestore(&m->lock, flags);
    101         goto copy_error;
    102     }
    103     //解锁并使能中断
    104     spin_unlock_irqrestore(&m->lock, flags);
    105     //spin_unlock(&m->lock);
    106 
    107     return count;
    108 copy_error:
    109     return ret;
    110 }
    111 
    112 long my_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
    113 {
    114     printk("%s %u %ld
    ", __FUNCTION__, cmd, arg);
    115 
    116     return 0;
    117 }
    118 
    119 irqreturn_t irq_handler(int irq, void *data)
    120 {
    121     struct mydev_st *m;
    122 
    123     m = data;
    124 
    125     //spin_lock(&m->lock);
    126 
    127     memcpy(m->buf, "nihao", 5);
    128 
    129     //spin_unlock(&m->lock);
    130 
    131     return IRQ_HANDLED;
    132 }
    133 
    134 struct file_operations my_ops = {
    135     .open = my_open,
    136     .release = my_close,
    137     .write = my_write,
    138     .read = my_read,
    139     .unlocked_ioctl = my_ioctl,
    140 };
    141 
    142 struct mydev_st *mydev;
    143 
    144 //当模块安装的时候执行
    145 static __init int test_init(void)
    146 {
    147     int ret;
    148 
    149     mydev = kzalloc(sizeof(*mydev), GFP_KERNEL);
    150     if (!mydev) {
    151         ret = -ENOMEM;
    152         goto alloc_dev_error;
    153     }
    154 
    155     mydev->count.counter = 0;
    156     spin_lock_init(&mydev->lock);
    157 
    158     ret = alloc_chrdev_region(&mydev->no, 1, 1, "mydev");
    159     if (ret < 0) {
    160         goto alloc_no_error;
    161     }
    162 
    163     cdev_init(&mydev->dev, &my_ops);
    164     ret = cdev_add(&mydev->dev, mydev->no, 1);
    165     if (ret < 0) {
    166         goto cdev_add_error;
    167     }
    168 
    169     mydev->irq = gpio_to_irq(EXYNOS4_GPX3(2));
    170     ret = request_irq(mydev->irq, irq_handler, IRQF_TRIGGER_FALLING, "haha", mydev);
    171     /* if errer */
    172 
    173     printk("major=%d minor=%d
    ", MAJOR(mydev->no), MINOR(mydev->no));
    174 
    175     return 0;
    176 cdev_add_error:
    177     unregister_chrdev_region(mydev->no, 1);
    178 alloc_no_error:
    179     kfree(mydev);
    180 alloc_dev_error:
    181     return ret;
    182 }
    183 
    184 //当模块卸载的时候执行
    185 static __exit void test_exit(void)
    186 {
    187     free_irq(mydev->irq, mydev);
    188     cdev_del(&mydev->dev);
    189     unregister_chrdev_region(mydev->no, 1);
    190     kfree(mydev);
    191 }
    192 
    193 module_init(test_init);
    194 module_exit(test_exit);
    195 
    196 MODULE_LICENSE("GPL");
      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <sys/types.h>
      4 #include <fcntl.h>
      5 #include <string.h>
      6 #include <sys/ioctl.h>
      7 
      8 #define DEV "/dev/test/mydev"
      9 
     10 int main(void)
     11 {
     12     int fd;
     13     char buf[20];
     14     int ret;
     15 
     16     fd = open(DEV, O_RDWR);
     17     if (fd < 0) {
     18         perror("open");
     19         exit(1);
     20     }
     21 
     22     write(fd, "123", 3);
     23     ret = read(fd, buf, sizeof(buf));
     24     write(1, buf, ret);
     25 
     26     ioctl(fd, 1, 1);
     27 
     28     close(fd);
     29 
     30     return 0;
     31 }
      1 LINUX_SRC :=/var/ftp/opt/linux-3.5
      2 
      3 obj-m += module.o
      4 module-objs = cdev_test2.o
      5 
      6 #obj-m += module_test1.o
      7 all:
      8     make -C $(LINUX_SRC) M=`pwd` modules
      9 clean:
     10     make -C $(LINUX_SRC) M=`pwd` modules clean

      信号量: include/linux/semaphore.h

        信号量:也是内核处理并发访问共享资源的一种方式。

        信号量的特点:获取不到信号量,则睡眠等待,获取信号量的函数有

           down():睡眠不可被打断

           down_interruptiabl():睡眠可以被中断打断

          down_killable():能够杀死进程的信号,可以打断睡眠。

          down_trylock():试图获取信号量,如果获取不到,也不睡眠,会出错返回。

          up():释放信号量:

          sema_init():初始化信号量

          struct semaphore :定义信号量

      互斥量:include/linux/mutex.h

        一般有了互斥量,基本不使用信号量了。所以互斥量相对用的多一些。

        struct mutex

        mutex_init

        mutex_lock

        mutex_trylock

        mutex_lock_interruptible

        mutex_lock_killable

        mutex_unlock

        意思同上:

      读写锁:include/linux/rwlock.h

        如果加了读锁,可以重复加,

        加写锁的条件:既没有读者也没有写者。

        加了写锁情况下:不能再加读锁,也不能在加写锁。

        只加了读锁的情况下:还是可以加读锁的。不可以加写锁

        饿死写者:指的是:写者想要写,就必须等待没有读者的情况下,但是读者有肯能源源不断的在读,多个读者,那么就会产生写着一直等待。这中情况适用于读者居多,很好写的情况。

        使用的函数:

          rwlock_t

          rwlock_init

          read_lock

      读写信号量:include/linux/rwsemaphore.h

        原理同读写锁:

      写这优先:seq锁

        多个写着不可共存,

        多个读者可以共存

        有读者时,写着可写

        读者每次读都要检测写着是否来过,do {read_seq     。。。。。} while(read_tryseq)

        其中read_seq是读出来偶数才继续执行,否则循环read_seq,读完出来时判断是否有写着来过,即再次读出来的数值变成奇数,说明写着来过,重新读取。  

      

    有时候,不小心知道了一些事,才发现自己所在乎的事是那么可笑。
  • 相关阅读:
    android apk瘦身之 图片压缩 tinypng
    java 1.7 新io 实践 NIO2
    Still unable to dial persistent://blog.csdn.net:80 after 3 attempts
    dex2oat 加载多次
    android stadio open recent 在同一窗口打开
    &运算符的应用
    MethodTrace 生成的trace文件为空
    MethodTrace 生成的trace文件为空
    error: unknown host service 的详细解决办法
    error: unknown host service 的详细解决办法
  • 原文地址:https://www.cnblogs.com/axjlxy/p/9048784.html
Copyright © 2011-2022 走看看