原子变量和自旋锁

Posted 爱新觉罗玄烨

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原子变量和自旋锁相关的知识,希望对你有一定的参考价值。

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

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

  原子变量:

    普通变量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\n"
 45 "1: ldrex   %0, [%3]\n"
 46 "   add %0, %0, %4\n"
 47 "   strex   %1, %0, [%3]\n"
 48 "   teq %1, #0\n"
 49 "   bne 1b"
 50     : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
 51     : "r" (&v->counter), "Ir" (i)
 52     : "cc");
 53 }

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

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

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

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

  自旋锁:

    spinlock_t

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

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

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

      注意:

          1):自死锁:

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

          3)带着锁不能睡眠.

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

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

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

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

    3.多核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\n", __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\n", __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\n", __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\n", __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\n", __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\n", 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

 

  信号量:

  互斥量:

  完成量:

  seq锁:

以上是关于原子变量和自旋锁的主要内容,如果未能解决你的问题,请参考以下文章

原子类与自旋锁原理初探

原子类与自旋锁原理初探

自旋锁互斥锁信号量原子操作条件变量在不同开源框架的应用

互斥锁自旋锁读写锁和条件变量

用Java原子变量的CAS方法实现一个自旋锁

互斥锁,自旋锁,原子操作原理和实现