久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

linux設備驅(qū)動中的并發(fā)控制

 raymoon_sure 2014-04-16

并發(fā)指的是多個執(zhí)行單元同時,、并行被執(zhí)行,,而并發(fā)的執(zhí)行單元對共享資源的訪問則很容易導致競態(tài)

linux內(nèi)核中主要競態(tài)
1.多對稱處理器的多個CPU  2.單CPU內(nèi)進程與搶占它的進程 3.中斷(硬中斷,、軟中斷,、Tasklet,、下半部)與進程之間
訪問共享內(nèi)存資源的代碼區(qū)稱為“臨界區(qū)”,,臨界區(qū)需要被以某種互斥機制加以保護,,中斷屏蔽、原子操作,、自旋鎖和信號量等
是linux設備驅(qū)動中可采用的互斥途徑,。

這幾個互斥的介紹:

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

local_irq_disable();//屏蔽中斷
...
...
臨界區(qū)
...
local_irq_enable();//開中斷
由于linux的異步IO、進程調(diào)度等很多重要的操作都依賴于中斷,,中斷對于內(nèi)核的運行非常重要,,在屏蔽中斷期間所有的中斷都無法處理,
因此長時間的屏蔽中斷很危險,,有可能導致數(shù)據(jù)丟失甚至系統(tǒng)崩潰,。所以這個不作為重點討論。

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

2.原子操作,,原子操作是一系列的不能被打斷的操作,。linux內(nèi)核提供了一系列的函數(shù)來實現(xiàn)內(nèi)核中的原子操作,這些函數(shù)分為2類,,分別針對位和整型變量
進行原子操作,。
實現(xiàn)原子操作的步驟:

1.定義原子變量并設置變量值
void atomic_set(atomic_t *v , int i); //設置原子變量值為i
atomic_t v = ATOMIC_INIT(0); //定義原子變量v,初始化為0
2.獲取原子變量的值
atomic_read(atomic_t *v);
3.原子變量加減操作
void atomic_add(int i,atomic_t *v);//原子變量加i
void atomic_sub(int i ,atomic_t *v);//原子變量減i
4.原子變量自增/自減
void atomic_inc(atomic_t *v);//自增1
void atomic_dec(atomic_t *v);//自減1
5.操作并測試:對原子變量執(zhí)行自增,、自減后(沒有加)測試其是否為0,,如果為0返回true,否則返回false,。
int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i ,atomic_t *v);
6.操作并返回
int atomic_add_return(int i , atomic_t *v);
int atomic_sub_return(int i , atomic_t *v);
int atomic_inc_return(atomic_t * v);
int atomic_dec_return(atomic_t * v);

**********************************************************************************************************************************************************
3.自旋鎖
自旋鎖是一個忙鎖,,它在一個小的循環(huán)內(nèi)不斷的重復測試并設置的操作。
自旋鎖保護臨界區(qū)的特點:臨界區(qū)要小,,并且臨界區(qū)內(nèi)不能有導致睡眠的操作,,否則可能引起系統(tǒng)崩潰,。自旋鎖可能導致系統(tǒng)死鎖,
引發(fā)這個問題最常見的情況是遞歸使用一個自旋鎖,。

自旋鎖的操作步驟:

1.定義自旋鎖
spinlock_t lock;
2.初始化自旋鎖
spin_lock_init(lock);這是個宏,,它用于動態(tài)初始化自旋鎖lock;
3.獲得自旋鎖
spin_lock(lock);該宏用于加鎖,,如果能夠立即獲得鎖,,它就能馬上返回,否則,,他將自旋在那里,,直到該自旋鎖的保持者釋放。
spin_trylock(lock);能夠獲得,,則返回真,,否則返回假,實際上是不在原地打轉(zhuǎn)而已,。
4.釋放自旋鎖
spin_unlock(lock);
與上面的兩個配對使用,。
例子:

spinlock_t lock;
spin_lock_init(&lock);
spin_lock(&lock);  //獲取自旋鎖,保護臨界區(qū)

,。,。。,。臨界區(qū)

spin_unlock(&lock);//釋放自旋鎖

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

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

現(xiàn)在又有問題了,,如果我第一個進程寫共享資源,第二個進程讀的話,,一旦寫了,,那么就讀不到了,可能寫的東西比較多,,但是第二個進程讀很小,,那么
能不能第一個進程寫的同時,,我第二個進程讀呢?
當然可以,,那么引出了順序鎖的概念,。都是一樣的操作。


**********************************************************************************************************************************************************
4.信號量
是用于保護臨界區(qū)的一種常用的方法,,它的使用與自旋鎖差不多,,但是它不在原地打轉(zhuǎn),當獲取不到信號量時候,,進程會進入休眠等待狀態(tài),。

主要操作:

1.定義sem信號量
struct semaphore sem;
2.初始化信號量
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.獲得信號量
void down(struct semaphore * sem);
該函數(shù)用于獲得信號量sem,,他會導致睡眠,所以不能在中斷中使用,。
int down_interruptible(struct semaphore* sem);
與上面功能類似,,因為down進入睡眠狀態(tài)的進程不能被信號打斷,而它能被信號打斷,,信號也會導致該函數(shù)返回,。
int down_trylock(struct semaphore * sem);

4.釋放信號量
void up(struct semaphore * sem);
該函數(shù)用于釋放信號量,同時喚醒等待者,。
信號量一般這樣使用:

DECLARE_MUTEX(sem);

down(&sem);

.....臨界區(qū)

up(&sem);

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


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

5.互斥體


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


總結(jié):并發(fā)和競態(tài)廣泛存在,,這幾個機制是解決問題的好方法,中斷屏蔽很少單獨使用,,原子操作只能針對整數(shù)進行,,因此,自旋鎖和信號量應用最為廣泛,。
自旋鎖會導致死循環(huán),,鎖定期間不允許阻塞,因此要求鎖定的臨界區(qū)要小,。信號量允許臨界區(qū)阻塞,,可以適用于臨界區(qū)較大的情況。讀寫自旋鎖和讀寫信號量是
放寬了條件的自旋鎖和信號量,,他們允許多個進程并發(fā)的讀取共享空間,。

 

一個fifo的綜合例子

復制代碼
/*======================================================================
    A globalfifo driver as an example of char device drivers  
    This example is to introduce poll,blocking and non-blocking access
      
    The initial developer of the original code is Baohua Song
    <[email protected]>. All Rights Reserved.
======================================================================*/
 #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字節(jié)*/ 
#define FIFO_CLEAR 0x1  /*清0全局內(nèi)存的長度*/ 
#define GLOBALFIFO_MAJOR 250    /*預設的globalfifo的主設備號*/ 
static int globalfifo_major = GLOBALFIFO_MAJOR; /*globalfifo設備結(jié)構(gòu)體*/ 
struct globalfifo_dev                                     
{ struct cdev cdev; /*cdev結(jié)構(gòu)體*/ 
unsigned int current_len; /*fifo有效數(shù)據(jù)長度*/
 unsigned char mem[GLOBALFIFO_SIZE]; /*全局內(nèi)存*/ 
struct semaphore sem; /*并發(fā)控制用的信號量*/
 wait_queue_head_t r_wait; /*阻塞讀用的等待隊列頭*/
 wait_queue_head_t w_wait; /*阻塞寫用的等待隊列頭*/ 
struct tasklet_struct tlet;
}; struct globalfifo_dev *globalfifo_devp; /*設備結(jié)構(gòu)體指針*/ /*文件打開函數(shù)*/ 
int globalfifo_open(struct inode *inode, struct file *filp)
{ /*將設備結(jié)構(gòu)體指針賦值給文件私有數(shù)據(jù)指針*/ 
filp->private_data = globalfifo_devp;
 return 0;
} /*文件釋放函數(shù)*/ 
int globalfifo_release(struct inode *inode, struct file *filp)
{ return 0;
} /* ioctl設備控制函數(shù) */ 
static int globalfifo_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg)
{ struct globalfifo_dev *dev = filp->private_data;/*獲得設備結(jié)構(gòu)體指針*/ 
switch (cmd)
  { case FIFO_CLEAR:
     down(&dev->sem); //獲得信號量  
     dev->current_len = 0;
      memset(dev->mem,0,GLOBALFIFO_SIZE);
      up(&dev->sem); //釋放信號量  
printk(KERN_INFO "globalfifo is set to zero\n"); break; default: return - EINVAL;
  } return 0;
} static unsigned int globalfifo_poll(struct file *filp, poll_table *wait)
{
  unsigned int mask = 0; struct globalfifo_dev *dev = filp->private_data; /*獲得設備結(jié)構(gòu)體指針*/ 
  down(&dev->sem);
  
  poll_wait(filp, &dev->r_wait, wait);
  poll_wait(filp, &dev->w_wait, wait); /*fifo非空*/
 if (dev->current_len != 0)
  {
    mask |= POLLIN | POLLRDNORM; /*標示數(shù)據(jù)可獲得*/ } /*fifo非滿*/ 
   if (dev->current_len != GLOBALFIFO_SIZE)
  {
    mask |= POLLOUT | POLLWRNORM; /*標示數(shù)據(jù)可寫入*/ }
     
  up(&dev->sem); return mask;
} /*globalfifo讀函數(shù)*/ 
static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count,loff_t *ppos)
{ int ret;
 struct globalfifo_dev *dev = filp->private_data; //獲得設備結(jié)構(gòu)體指針 
DECLARE_WAITQUEUE(wait, current); //定義等待隊列  
down(&dev->sem); //獲得信號量 
add_wait_queue(&dev->r_wait, &wait); //進入讀等待隊列頭 /* 等待FIFO非空 */ 
if (dev->current_len == 0)
  { if (filp->f_flags &O_NONBLOCK)
    {
      ret =  - EAGAIN; goto out;
    } 
    __set_current_state(TASK_INTERRUPTIBLE); //改變進程狀態(tài)為睡眠
  up(&dev->sem);

    schedule(); //調(diào)度其他進程執(zhí)行 
   if (signal_pending(current)) //如果是因為信號喚醒  
  {
      ret =  - ERESTARTSYS; goto out2;
    }

    down(&dev->sem);
  } /* 拷貝到用戶空間 */ 
 if (count > dev->current_len)
    count = dev->current_len;
 if (copy_to_user(buf, dev->mem, count))
  {
    ret =  - EFAULT; goto out;
  } 
else
 {
    memcpy(dev->mem, dev->mem + count, dev->current_len - count); //fifo數(shù)據(jù)前移 
dev->current_len -= count; //有效數(shù)據(jù)長度減少 
printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count, dev->current_len);
     
    wake_up_interruptible(&dev->w_wait); //喚醒寫等待隊列 
  ret = count;
  }
 out: up(&dev->sem); //釋放信號量 
 out2:remove_wait_queue(&dev->w_wait, &wait); //從附屬的等待隊列頭移除 
 set_current_state(TASK_RUNNING);
 return ret;
} /*globalfifo寫操作*/ 
static ssize_t globalfifo_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{ struct globalfifo_dev *dev = filp->private_data; //獲得設備結(jié)構(gòu)體指針
 int ret;
  DECLARE_WAITQUEUE(wait, current); //定義等待隊列 
 down(&dev->sem); //獲取信號量 
 add_wait_queue(&dev->w_wait, &wait); //進入寫等待隊列頭 /* 等待FIFO非滿 */ 
if (dev->current_len == GLOBALFIFO_SIZE)
  { if (filp->f_flags &O_NONBLOCK) //如果是非阻塞訪問 
 {
      ret =  - EAGAIN; goto out;
    } 
    __set_current_state(TASK_INTERRUPTIBLE); //改變進程狀態(tài)為睡眠 
up(&dev->sem);

    schedule(); //調(diào)度其他進程執(zhí)行 
if (signal_pending(current)) //如果是因為信號喚醒 
 {
      ret =  - ERESTARTSYS; goto out2;
    }

    down(&dev->sem); //獲得信號量  } /*從用戶空間拷貝到內(nèi)核空間*/ 
if (count > GLOBALFIFO_SIZE - dev->current_len)
    count = GLOBALFIFO_SIZE - dev->current_len;
 if (copy_from_user(dev->mem + dev->current_len, buf, count))
  {
    ret =  - EFAULT; goto out;
  } 
else
 {
    dev->current_len += count;
    printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count, dev ->current_len);
    wake_up_interruptible(&dev->r_wait); //喚醒讀等待隊列  
   ret = count;
  }


tasklet_schedule(&dev->tlet);
printk("in write jiffies=%ld\n",jiffies);
 out: up(&dev->sem); //釋放信號量 
out2:remove_wait_queue(&dev->w_wait, &wait); //從附屬的等待隊列頭移除  
set_current_state(TASK_RUNNING); 
return ret;
} /*文件操作結(jié)構(gòu)體*/ 
static const struct file_operations globalfifo_fops = {
  .owner = THIS_MODULE,
  .read = globalfifo_read,
  .write = globalfifo_write,
  .ioctl = globalfifo_ioctl,
  .poll = globalfifo_poll,
  .open = globalfifo_open,
  .release = globalfifo_release,
}; /*初始化并注冊cdev*/ 
static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
{ int err, devno = MKDEV(globalfifo_major, index);

  cdev_init(&dev->cdev, &globalfifo_fops);
  dev->cdev.owner = THIS_MODULE;
  dev->cdev.ops = &globalfifo_fops;
  err = cdev_add(&dev->cdev, devno, 1); if (err)
    printk(KERN_NOTICE "Error %d adding LED%d", err, index);
} 
void jit_tasklet_fn(unsigned long arg)
{
 printk("in jit_tasklet_fn  jiffies=%ld\n",jiffies);
} 
/*設備驅(qū)動模塊加載函數(shù)*/ 
int globalfifo_init(void)
{ int ret;
  dev_t devno = MKDEV(globalfifo_major, 0); /* 申請設備號*/ 
if (globalfifo_major)
    ret = register_chrdev_region(devno, 1, "globalfifo"); 
else /* 動態(tài)申請設備號 */ 
{
    ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo");
    globalfifo_major = MAJOR(devno);
  }
 if (ret < 0)
 return ret;
 /* 動態(tài)申請設備結(jié)構(gòu)體的內(nèi)存*/
 globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL); 
if (!globalfifo_devp) /*申請失敗*/
 {
    ret =  - ENOMEM; goto fail_malloc;
  }
  memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));

  globalfifo_setup_cdev(globalfifo_devp, 0);

  init_MUTEX(&globalfifo_devp->sem); /*初始化信號量*/ 
init_waitqueue_head(&globalfifo_devp->r_wait); /*初始化讀等待隊列頭*/ 
init_waitqueue_head(&globalfifo_devp->w_wait); /*初始化寫等待隊列頭*/ 
/* register the tasklet */ 
tasklet_init(&globalfifo_devp->tlet, jit_tasklet_fn, (unsigned long)globalfifo_devp); 
return 0;

  fail_malloc: unregister_chrdev_region(devno, 1); return ret;
} 
/*模塊卸載函數(shù)*/
 void globalfifo_exit(void)
{
  cdev_del(&globalfifo_devp->cdev); /*注銷cdev*/
 kfree(globalfifo_devp); /*釋放設備結(jié)構(gòu)體內(nèi)存*/ 
unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*釋放設備號*/ }

MODULE_AUTHOR("Song Baohua");
MODULE_LICENSE("Dual BSD/GPL");

module_param(globalfifo_major, int, S_IRUGO);

module_init(globalfifo_init);
module_exit(globalfifo_exit); 

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導購買等信息,謹防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多