互斥技術----原子變量和自旋鎖

這一章節想要你們學習的就是在多進程或者多線程下:如何不衝突的訪問同一個文件或者是同一段共享資源:node

有以下幾個機制須要你們來學習:linux

  原子變量:vim

    普通變量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 }

插曲:中斷除了正在運行的指令不能夠打斷,其餘任何過程均可以打斷:在不可搶佔的內核的一段代碼:在以前屏蔽中斷,操做以後再打開中斷,這樣中間的一段代碼是不可能被其餘任何狀況打斷了.學習

以上就是原子變量理解過程的一些原理講解:atom

下面咱們來看下如何運用原子變量:(與自旋鎖在同一個代碼中)spa

下面幾個機制來限制共享資源的訪問:線程

  自旋鎖: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\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

 

  信號量: 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,讀完出來時判斷是否有寫着來過,即再次讀出來的數值變成奇數,說明寫着來過,從新讀取。  

相關文章
相關標籤/搜索