成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Linux設備驅動中的并發控制

系統 Linux
并發指的是多個執行單元同時、并行被執行,而并發的執行單元對共享資源的訪問則很容易導致競態。

[[208856]]

并發指的是多個執行單元同時、并行被執行,而并發的執行單元對共享資源的訪問則很容易導致競態。

linux內核中主要競態

1.多對稱處理器的多個CPU

2.單CPU內進程與搶占它的進程

3.中斷(硬中斷、軟中斷、Tasklet、下半部)與進程之間

訪問共享內存資源的代碼區稱為“臨界區”,臨界區需要被以某種互斥機制加以保護,中斷屏蔽、原子操作、自旋鎖和信號量等,是linux設備驅動中可采用的互斥途徑。

這幾個互斥的介紹:

1.中斷屏蔽,這個主要用于單CPU,中斷屏蔽將使得中斷和進程之間的并發不再發生。

使用方法:

  1. local_irq_disable();//屏蔽中斷 
  2. ... 
  3. ... 
  4. 臨界區 
  5. ... 
  6. local_irq_enable();//開中斷 

 

由于linux的異步IO、進程調度等很多重要的操作都依賴于中斷,中斷對于內核的運行非常重要,在屏蔽中斷期間所有的中斷都無法處理,因此長時間的屏蔽中斷很危險,有可能導致數據丟失甚至系統崩潰。所以這個不作為重點討論。

**********************************************************************************************************************************************************

2.原子操作,原子操作是一系列的不能被打斷的操作

linux內核提供了一系列的函數來實現內核中的原子操作,這些函數分為2類,分別針對位和整型變量進行原子操作。

實現原子操作的步驟:

1).定義原子變量并設置變量值

  1. void atomic_set(atomic_t *v , int i); //設置原子變量值為i 
  2.  
  3. atomic_t v = ATOMIC_INIT(0); //定義原子變量v,初始化為0 

 

2).獲取原子變量的值

  1. atomic_read(atomic_t *v); 

3).原子變量加減操作

  1. void atomic_add(int i,atomic_t *v);//原子變量加i 
  2.  
  3. void atomic_sub(int i ,atomic_t *v);//原子變量減i 

 

4).原子變量自增/自減

  1. void atomic_inc(atomic_t *v);//自增1 
  2.  
  3. void atomic_dec(atomic_t *v);//自減1 

 

5).操作并測試:對原子變量執行自增、自減后(沒有加)測試其是否為0,如果為0返回true,否則返回false。

  1. int atomic_inc_and_test(atomic_t *v); 
  2.  
  3. int atomic_dec_and_test(atomic_t *v); 
  4.  
  5. int atomic_sub_and_test(int i ,atomic_t *v); 

 

6).操作并返回

  1. int atomic_add_return(int i , atomic_t *v); 
  2.  
  3. int atomic_sub_return(int i , atomic_t *v); 
  4.  
  5. int atomic_inc_return(atomic_t * v); 
  6.  
  7. int atomic_dec_return(atomic_t * v); 

 

**********************************************************************************************************************************************************

3.自旋鎖

自旋鎖是一個忙鎖,它在一個小的循環內不斷的重復測試并設置的操作。

自旋鎖保護臨界區的特點:臨界區要小,并且臨界區內不能有導致睡眠的操作,否則可能引起系統崩潰。自旋鎖可能導致系統死鎖,引發這個問題最常見的情況是遞歸使用一個自旋鎖。

自旋鎖的操作步驟:

1).定義自旋鎖

  1. spinlock_t lock; 

2).初始化自旋鎖

  1. spin_lock_init(lock); //這是個宏,它用于動態初始化自旋鎖lock; 

3).獲得自旋鎖

  1. spin_lock(lock);//該宏用于加鎖,如果能夠立即獲得鎖,它就能馬上返回,否則,他將自旋在那里,直到該自旋鎖的保持者釋放。 
  2.  
  3. spin_trylock(lock);//能夠獲得,則返回真,否則返回假,實際上是不在原地打轉而已。 

 

4).釋放自旋鎖

  1. spin_unlock(lock); 

與上面的兩個配對使用。

例子:

  1. spinlock_t lock; 
  2.  
  3. spin_lock_init(&lock); 
  4.  
  5. spin_lock(&lock); //獲取自旋鎖,保護臨界區。。。。臨界區 
  6.  
  7. spin_unlock(&lock);//釋放自旋鎖 

 

自旋鎖不關心鎖定的臨界區究竟是如何執行的。不管是讀操作還是寫操作,實際上,對共享資源進行讀取的時候是應該可以允許多個執行單元同時訪問的,那么這樣的話,自旋鎖就有了弊端。于是便衍生出來一個讀寫鎖。

它保留了自旋的特性,但在對操作上面可以允許有多個單元進程同時操作。當然,讀和寫的時候不能同時進行。

現在又有問題了,如果我第一個進程寫共享資源,第二個進程讀的話,一旦寫了,那么就讀不到了,可能寫的東西比較多,但是第二個進程讀很小,那么能不能第一個進程寫的同時,我第二個進程讀呢?

當然可以,那么引出了順序鎖的概念。都是一樣的操作。

**********************************************************************************************************************************************************

4.信號量

是用于保護臨界區的一種常用的方法,它的使用與自旋鎖差不多,但是它不在原地打轉,當獲取不到信號量時候,進程會進入休眠等待狀態。

主要操作:

1).定義sem信號量

  1. struct semaphore sem; 

2).初始化信號量

  1. void sema_init(struct semaphore *sem, int val); 

初始化信號量,并設置sem的值為val

初始化的時候還可以這樣用,init_MUTEX(sem),這是個宏 #define init_MUTEX(sem) sema_init(sem , 1)

init_MUTEX_LOCKED(sem),這是個宏 #define init_MUTEX_LOCKED(sem) sema_init(sem , 0)

3).獲得信號量

  1. void down(struct semaphore * sem); 

該函數用于獲得信號量sem,他會導致睡眠,所以不能在中斷中使用。

  1. int down_interruptible(struct semaphore* sem); 

與上面功能類似,因為down進入睡眠狀態的進程不能被信號打斷,而它能被信號打斷,信號也會導致該函數返回。

  1. int down_trylock(struct semaphore * sem); 

4).釋放信號量

  1. void up(struct semaphore * sem); 

該函數用于釋放信號量,同時喚醒等待者。

信號量一般這樣使用:

  1. DECLARE_MUTEX(sem); 
  2.  
  3. down(&sem); 
  4.  
  5. .....臨界區 
  6.  
  7. up(&sem); 

 

linux自旋鎖和信號量采用的“獲取鎖-訪問臨界區-釋放鎖”的方式。

*************************************************************************************************************************

5.互斥體

互斥體和信號量基本上差不多。不介紹了。

總結:

并發和競態廣泛存在,這幾個機制是解決問題的好方法,中斷屏蔽很少單獨使用,原子操作只能針對整數進行,因此,自旋鎖和信號量應用最為廣泛。

自旋鎖會導致死循環,鎖定期間不允許阻塞,因此要求鎖定的臨界區要小。信號量允許臨界區阻塞,可以適用于臨界區較大的情況。讀寫自旋鎖和讀寫信號量是放寬了條件的自旋鎖和信號量,他們允許多個進程并發的讀取共享空間。

一個fifo的綜合例子

  1. /*====================================================================== 
  2.     A globalfifo driver as an example of char device drivers   
  3.     This example is to introduce poll,blocking and non-blocking access 
  4.        
  5.     The initial developer of the original code is Baohua Song 
  6.     <author@linuxdriver.cn>. All Rights Reserved. 
  7. ======================================================================*/#include <linux/module.h>#include <linux/types.h>#include <linux/fs.h>#include <linux/errno.h>#include <linux/mm.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/cdev.h>#include <asm/io.h>#include <asm/system.h>#include <asm/uaccess.h>#include <linux/poll.h>#include <linux/time.h>#include <linux/timer.h>#include <linux/kernel.h>#include <linux/spinlock.h>#include <linux/interrupt.h>#define GLOBALFIFO_SIZE 0x1000 /*全局fifo最大4K字節*/#define FIFO_CLEAR 0x1  /*清0全局內存的長度*/#define GLOBALFIFO_MAJOR 250    /*預設的globalfifo的主設備號*/static int globalfifo_major = GLOBALFIFO_MAJOR;/*globalfifo設備結構體*/struct globalfifo_dev                                      
  8. {                                                         
  9.   struct cdev cdev; /*cdev結構體*/                        
  10.   unsigned int current_len;    /*fifo有效數據長度*/ 
  11.   unsigned char mem[GLOBALFIFO_SIZE]; /*全局內存*/         
  12.   struct semaphore sem; /*并發控制用的信號量*/            
  13.   wait_queue_head_t r_wait; /*阻塞讀用的等待隊列頭*/      
  14.   wait_queue_head_t w_wait; /*阻塞寫用的等待隊列頭*/      
  15.   struct tasklet_struct tlet; 
  16. };struct globalfifo_dev *globalfifo_devp; /*設備結構體指針*//*文件打開函數*/int globalfifo_open(struct inode *inode, struct file *filp) 
  17. {  /*將設備結構體指針賦值給文件私有數據指針*/ 
  18.   filp->private_data = globalfifo_devp;  return 0; 
  19. }/*文件釋放函數*/int globalfifo_release(struct inode *inode, struct file *filp) 
  20. {  return 0; 
  21. }/* ioctl設備控制函數 */static int globalfifo_ioctl(struct inode *inodep, struct file *filp, unsigned  int cmd, unsigned long arg) 
  22. {  struct globalfifo_dev *dev = filp->private_data;/*獲得設備結構體指針*/ 
  23.  
  24.   switch (cmd) 
  25.   {    case FIFO_CLEAR: 
  26.      down(&dev->sem); //獲得信號量      
  27.       dev->current_len = 0; 
  28.       memset(dev->mem,0,GLOBALFIFO_SIZE); 
  29.       up(&dev->sem); //釋放信號量          
  30.       printk(KERN_INFO "globalfifo is set to zero\n");       
  31.       break;    default:      return  - EINVAL; 
  32.   }  return 0; 
  33. }static unsigned int globalfifo_poll(struct file *filp, poll_table *wait) 
  34.   unsigned int mask = 0;  struct globalfifo_dev *dev = filp->private_data; /*獲得設備結構體指針*/ 
  35.    
  36.   down(&dev->sem); 
  37.    
  38.   poll_wait(filp, &dev->r_wait, wait); 
  39.   poll_wait(filp, &dev->w_wait, wait);   
  40.   /*fifo非空*/ 
  41.   if (dev->current_len != 0) 
  42.   { 
  43.     mask |= POLLIN | POLLRDNORM; /*標示數據可獲得*/ 
  44.   }  /*fifo非滿*/ 
  45.   if (dev->current_len != GLOBALFIFO_SIZE) 
  46.   { 
  47.     mask |= POLLOUT | POLLWRNORM; /*標示數據可寫入*/ 
  48.   } 
  49.       
  50.   up(&dev->sem);  return mask; 
  51. }/*globalfifo讀函數*/static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count
  52.   loff_t *ppos) 
  53. {  int ret;  struct globalfifo_dev *dev = filp->private_data; //獲得設備結構體指針 
  54.   DECLARE_WAITQUEUE(wait, current); //定義等待隊列 
  55.   down(&dev->sem); //獲得信號量 
  56.   add_wait_queue(&dev->r_wait, &wait); //進入讀等待隊列頭 
  57.  
  58.   /* 等待FIFO非空 */ 
  59.   if (dev->current_len == 0) 
  60.   {    if (filp->f_flags &O_NONBLOCK) 
  61.     { 
  62.       ret =  - EAGAIN;      goto out
  63.     }  
  64.     __set_current_state(TASK_INTERRUPTIBLE); //改變進程狀態為睡眠 
  65.     up(&dev->sem); 
  66.  
  67.     schedule(); //調度其他進程執行 
  68.     if (signal_pending(current))    //如果是因為信號喚醒    { 
  69.       ret =  - ERESTARTSYS;      goto out2; 
  70.     } 
  71.  
  72.     down(&dev->sem); 
  73.   }  /* 拷貝到用戶空間 */ 
  74.   if (count > dev->current_len) 
  75.     count = dev->current_len;  if (copy_to_user(buf, dev->mem, count)) 
  76.   { 
  77.     ret =  - EFAULT;    goto out
  78.   }  else 
  79.   { 
  80.     memcpy(dev->mem, dev->mem + count, dev->current_len - count); //fifo數據前移 
  81.     dev->current_len -= count; //有效數據長度減少 
  82.     printk(KERN_INFO "read %d bytes(s),current_len:%d\n"count, dev->current_len); 
  83.       
  84.     wake_up_interruptible(&dev->w_wait); //喚醒寫等待隊列     
  85.     ret = count
  86.   }  out: up(&dev->sem); //釋放信號量 
  87.   out2:remove_wait_queue(&dev->w_wait, &wait); //從附屬的等待隊列頭移除  set_current_state(TASK_RUNNING);  return ret; 
  88. }/*globalfifo寫操作*/static ssize_t globalfifo_write(struct file *filp, const char __user *buf, 
  89.   size_t count, loff_t *ppos) 
  90. {  struct globalfifo_dev *dev = filp->private_data; //獲得設備結構體指針 
  91.   int ret; 
  92.   DECLARE_WAITQUEUE(wait, current); //定義等待隊列 
  93.   down(&dev->sem); //獲取信號量 
  94.   add_wait_queue(&dev->w_wait, &wait); //進入寫等待隊列頭 
  95.  
  96.   /* 等待FIFO非滿 */ 
  97.   if (dev->current_len == GLOBALFIFO_SIZE) 
  98.   {    if (filp->f_flags &O_NONBLOCK)    //如果是非阻塞訪問    { 
  99.       ret =  - EAGAIN;      goto out
  100.     }  
  101.     __set_current_state(TASK_INTERRUPTIBLE); //改變進程狀態為睡眠 
  102.     up(&dev->sem); 
  103.  
  104.     schedule(); //調度其他進程執行 
  105.     if (signal_pending(current))    //如果是因為信號喚醒    { 
  106.       ret =  - ERESTARTSYS;      goto out2; 
  107.     } 
  108.  
  109.     down(&dev->sem); //獲得信號量  }  /*從用戶空間拷貝到內核空間*/ 
  110.   if (count > GLOBALFIFO_SIZE - dev->current_len) 
  111.     count = GLOBALFIFO_SIZE - dev->current_len;  if (copy_from_user(dev->mem + dev->current_len, buf, count)) 
  112.   { 
  113.     ret =  - EFAULT;    goto out
  114.   }  else 
  115.   { 
  116.     dev->current_len += count
  117.     printk(KERN_INFO "written %d bytes(s),current_len:%d\n"count, dev      ->current_len); 
  118.  
  119.     wake_up_interruptible(&dev->r_wait); //喚醒讀等待隊列     
  120.     ret = count
  121.   } 
  122.  
  123.  
  124. tasklet_schedule(&dev->tlet); 
  125. printk("in write jiffies=%ld\n",jiffies);  out: up(&dev->sem); //釋放信號量 
  126.   out2:remove_wait_queue(&dev->w_wait, &wait); //從附屬的等待隊列頭移除  set_current_state(TASK_RUNNING);  return ret; 
  127. }/*文件操作結構體*/static const struct file_operations globalfifo_fops ={ 
  128.   .owner = THIS_MODULE, 
  129.   .read = globalfifo_read, 
  130.   .write = globalfifo_write, 
  131.   .ioctl = globalfifo_ioctl, 
  132.   .poll = globalfifo_poll, 
  133.   .open = globalfifo_open, 
  134.   .release = globalfifo_release, 
  135. };/*初始化并注冊cdev*/static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index
  136. {  int err, devno = MKDEV(globalfifo_major, index); 
  137.  
  138.   cdev_init(&dev->cdev, &globalfifo_fops); 
  139.   dev->cdev.owner = THIS_MODULE; 
  140.   dev->cdev.ops = &globalfifo_fops; 
  141.   err = cdev_add(&dev->cdev, devno, 1);  if (err) 
  142.     printk(KERN_NOTICE "Error %d adding LED%d", err, index); 
  143. }void jit_tasklet_fn(unsigned long arg) 
  144.  printk("in jit_tasklet_fn  jiffies=%ld\n",jiffies); 
  145. }/*設備驅動模塊加載函數*/int globalfifo_init(void) 
  146. {  int ret; 
  147.   dev_t devno = MKDEV(globalfifo_major, 0);  /* 申請設備號*/ 
  148.   if (globalfifo_major) 
  149.     ret = register_chrdev_region(devno, 1, "globalfifo");  else  /* 動態申請設備號 */ 
  150.  
  151.   { 
  152.     ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo"); 
  153.     globalfifo_major = MAJOR(devno); 
  154.   }  if (ret < 0)    return ret;  /* 動態申請設備結構體的內存*/ 
  155.   globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);  if (!globalfifo_devp)    /*申請失敗*/ 
  156.   { 
  157.     ret =  - ENOMEM;    goto fail_malloc; 
  158.   } 
  159.  
  160.   memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev)); 
  161.  
  162.   globalfifo_setup_cdev(globalfifo_devp, 0); 
  163.  
  164.   init_MUTEX(&globalfifo_devp->sem);   /*初始化信號量*/ 
  165.   init_waitqueue_head(&globalfifo_devp->r_wait); /*初始化讀等待隊列頭*/ 
  166.   init_waitqueue_head(&globalfifo_devp->w_wait); /*初始化寫等待隊列頭*/ 
  167.  /* register the tasklet */ 
  168.  tasklet_init(&globalfifo_devp->tlet, jit_tasklet_fn, (unsigned long)globalfifo_devp);  return 0; 
  169.  
  170.   fail_malloc: unregister_chrdev_region(devno, 1);  return ret; 
  171. }/*模塊卸載函數*/void globalfifo_exit(void) 
  172.   cdev_del(&globalfifo_devp->cdev);   /*注銷cdev*/ 
  173.   kfree(globalfifo_devp);     /*釋放設備結構體內存*/ 
  174.   unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*釋放設備號*/} 
  175.  
  176. MODULE_AUTHOR("Song Baohua"); 
  177. MODULE_LICENSE("Dual BSD/GPL"); 
  178.  
  179. module_param(globalfifo_major, int, S_IRUGO); 
  180.  
  181. module_init(globalfifo_init); 
  182. module_exit(globalfifo_exit); 

 

責任編輯:龐桂玉 來源: 嵌入式Linux中文站
相關推薦

2017-02-28 17:46:15

Linux驅動技術并發控制

2023-05-15 08:58:41

塊設備驅動Linux

2023-05-12 07:27:24

Linux內核網絡設備驅動

2020-12-03 08:59:06

Linux設備驅動

2017-02-10 15:32:47

2021-11-29 07:55:45

Linux GPIO Linux 系統

2017-11-16 14:46:58

Linuxplatform總線驅動設備

2009-02-09 10:06:03

并發控制Web應用悲觀鎖

2022-05-10 08:49:46

設備驅動Linux

2016-12-15 14:55:31

Linux定時延時

2009-12-23 13:17:36

Linux設備驅動

2011-01-10 18:21:38

linux編寫程序

2021-04-07 06:00:18

JavaScript 前端并發控制

2021-04-12 12:00:13

Linux運維Linux系統

2009-12-07 09:39:04

Linux設備驅動硬件通信

2022-05-26 00:48:55

Linux內核硬件

2010-05-07 10:55:37

Windows 7驅動設備

2010-05-10 15:53:24

Unix系統

2017-08-21 10:56:55

MySQL并發控制

2022-01-17 11:50:38

Linux CPULinux 系統
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩精品一区二区三区在线播放 | 国产精品一区二区久久 | 国产欧美精品区一区二区三区 | av网站观看 | 亚洲精品久久久久中文字幕欢迎你 | 日韩中文字幕一区二区三区 | www.亚洲一区二区三区 | 久久久青草婷婷精品综合日韩 | 欧美第一页 | 男女视频在线观看 | 黄网站在线播放 | 国产精品178页 | www.中文字幕.com | 国产色婷婷精品综合在线手机播放 | 国产精品美女久久久久aⅴ国产馆 | 久久久国产一区二区 | 久久精品女人天堂av | 91久久精品日日躁夜夜躁欧美 | 国产a视频 | 精品一区二区三区四区在线 | 日韩一区二区在线视频 | 日本五月婷婷 | 国产在线视频网 | 亚洲一区二区三区四区在线观看 | 久久精品国产久精国产 | 精品国产黄色片 | 91精品一区| 精品国产综合 | 亚洲综合在线网 | 久久福利电影 | av午夜电影| 99久久婷婷国产综合精品首页 | 精品国产精品国产偷麻豆 | 欧美国产日韩一区 | 狠狠综合网| 99精品国产一区二区三区 | 四虎永久免费影院 | www.4567| 成年人在线观看视频 | 国产高清免费视频 | 亚洲精品一区二区三区四区高清 |