linux 字符驅動阻塞型 等待隊列

內核等待隊列linux

等待隊列程序員

linux驅動程序設計中,能夠使用等待隊列來實現進程的阻塞,等待隊列可看做保存進程的容器,在阻塞進程時,將進程放入等待隊列,當喚醒進程時,從等待隊列中取出進程。函數

linux2.6內核提供了以下關於等待隊列的操做:
1.定義等待隊列
測試

wait_queue_head_t  my_queuespa

2.初始化等待隊列設計

init_waitqueue_head(&my_queue)指針

3.定義並初始化等待隊列(直接代替第12兩個操做)
DECLARE_WAIT_QUEUE_HEAD(my_queue)
orm

4.有條件睡眠隊列

——wait_event(queue, condition)進程

condition(一個布爾表達式)爲真時,當即返回;不然讓進程進入TASK_UNINTERRUPTIBLE模式的睡眠,並掛載queue參數所指定的等待隊列上

——wait_event_interruptible(queue, condition)

condition(一個布爾表達式)爲真時,當即返回;不然讓進程進入TASK_INTERRUPTIBLE的睡眠,並掛載queue參數所指定的等待隊列上

——int wait_event_killable(wait_queue_t queue, condition)

condition(一個布爾表達式)爲真時,當即返回;不然讓進程進入TASK_KILLABLE的睡眠,並掛載queue參數所指定的等待隊列上(TASK_UNINTERRUPTIBLE在中斷和給它發送信號的時候都不會被喚醒;TASK_KILLABLE也如此,不過在發送kill信號的時候能被喚醒)

5.無條件睡眠(老版本,不建議使用)

——sleep_on(wait_queue_head_t *q)

讓進程進入不可中斷的睡眠,並把它放入等待隊列q

——interruptible_sleep_on(wait_queue_head_t *q)

讓進程進入可中斷的睡眠,並把它放入等待隊列q

6.從等待隊列中喚醒進程

——wake_up(wait_queue _t *q)

從等待隊列q中喚醒狀態爲TASK_UNINTERRUPTIBLETASK_INTERRUPTIBLETASK_KILLABLE全部進程(喚醒:將進程從睡眠狀態改成TASK_RUNNING狀態,不過只是就緒狀態而非執行態)

——wake_up_interruptible(wait_queue_t *q)

從等待隊列q中喚醒狀態爲TASK_INTERRUPTIBLE的進程


阻塞型字符設備驅動


一、阻塞型字符設備驅動的功能 
      當一個設備沒法馬上知足用戶的讀寫請求時。例如:調用read時沒有數據可讀, 但之後可能會有;或者一個進程試圖向設備寫入數據,可是設備暫時沒有準備好接收數據。應用程序一般不關心這種問題,應用程序只是調用 read 或 write 並獲得返回值。驅動程序應當(缺省地)阻塞進程,使它進入睡眠,直到請求能夠獲得知足。 
二、阻塞方式 
      1)在阻塞型驅動程序中,Read實現方式以下:若是進程調用read,但設備沒有數據或數據不足,進程阻塞。當新數據到達後,喚醒被阻塞進程。 
      2)在阻塞型驅動程序中,Write實現方式以下:若是進程調用了write,但設備沒有足夠的空間供其寫入數據,進程阻塞。當設備中的數據被讀走後,緩衝區中空出部分空間,則喚醒進程。 
三、非阻塞方式 
      阻塞方式是文件讀寫操做的默認方式,但應用程序員可經過使用O_NONBLOCK標誌來人爲的設置讀寫操做爲非阻塞方式(該標誌定義在<linux/fcntl.h>中,在打開文件時指定)。 
      若是設置了O_NONBLOCK標誌,read和write的行爲是不一樣的。若是進程在沒有數據就緒時調用了read,或者在緩衝區沒有空間時調用了write,系統只是簡單地返回-EAGAIN,而不會阻塞進程。 

例子

在.c中添加

bool have_data = false;

在頭文件.h中添加 等待隊列

讀函數添加 檢查是否有數據 並添加到等待隊列

static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) 

  unsigned long p =  *ppos; 
  unsigned int count = size; 
  int ret = 0; 
  struct mem_dev *dev = filp->private_data; /*得到設備結構體指針*/ 
  /*判斷讀位置是否有效*/ 
  if (p >= MEMDEV_SIZE) 
    return 0; 
  if (count > MEMDEV_SIZE - p) 
    count = MEMDEV_SIZE - p; 
     
while (!have_data) /* 沒有數據可讀 ,考慮爲何不用if,而用while。由於wait_event_interruptible函數,除了收到數據會跳出外,中斷也會跳出,中斷喚醒等待隊列,若是使用if就會往下運行但此時並無數據可讀會出錯,故次用while和interruptible配合的緣由 */ 

{

          /*判斷用戶是否設置了非阻塞方式*/ 

        if (filp->f_flags & O_NONBLOCK) 
            return -EAGAIN; /*設置了非阻塞方式*/ 
        /* 當設置了阻塞方式*/ 

       wait_event_interruptible(dev->inq,have_data);/**當have_data爲真時,當即返回,不然讓進程進入TASK_KILL的睡眠 並掛在dev->inq隊列上*/

} 
  /*讀數據到用戶空間*/ 
  if (copy_to_user(buf, (void*)(dev->data + p), count)) 
  { 
    ret =  - EFAULT; 
  } 
  else 
  { 
    *ppos += count; 
    ret = count; 
    
    printk(KERN_INFO "read %d bytes(s) from %d\n", count, p); 
  } 
  

  have_data = false; /* 代表再也不有數據可讀 */ 
  return ret; 

寫函數添加 有數據標誌,並移出等待隊列

static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) 

  unsigned long p =  *ppos; 
  unsigned int count = size; 
  int ret = 0; 
  struct mem_dev *dev = filp->private_data; /*得到設備結構體指針*/ 
  
  /*分析和獲取有效的寫長度*/ 
  if (p >= MEMDEV_SIZE) 
    return 0; 
  if (count > MEMDEV_SIZE - p) 
    count = MEMDEV_SIZE - p; 
     
  /*從用戶空間寫數據*/ 
  if (copy_from_user(dev->data + p, buf, count)) 
    ret =  - EFAULT; 
  else 
  { 
    *ppos += count; 
    ret = count; 
     
    printk(KERN_INFO "written %d bytes(s) from %d\n", count, p); 
  } 
  
  have_data = true; /* 有新的數據可讀 */ 
     
    /* 喚醒讀進程*/ 
    wake_up(&(dev->inq)); 


  return ret; 

添加等待隊列初始化函數

static int memdev_init(void) 

  int result; 
  int i; 

  dev_t devno = MKDEV(mem_major, 0); 

  /* 靜態申請設備號*/ 
  if (mem_major) 
    result = register_chrdev_region(devno, 2, "memdev"); 
  else  /* 動態分配設備號 */ 
  { 
    result = alloc_chrdev_region(&devno, 0, 2, "memdev"); 
    mem_major = MAJOR(devno); 
  }   
  
  if (result < 0) 
    return result; 

  /*初始化cdev結構*/ 
  cdev_init(&cdev, &mem_fops); 
  cdev.owner = THIS_MODULE; 
  cdev.ops = &mem_fops; 
  
  /* 註冊字符設備*/ 
  cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS); 
    
  /* 爲設備描述結構分配內存*/ 
  mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL); 
  if (!mem_devp)    /*申請失敗*/ 
  { 
    result =  - ENOMEM; 
    goto fail_malloc; 
  } 
  memset(mem_devp, 0, sizeof(struct mem_dev)); 
  
  /*爲設備分配內存*/ 
  for (i=0; i < MEMDEV_NR_DEVS; i++) 
  { 
        mem_devp[i].size = MEMDEV_SIZE; 
        mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL); 
        memset(mem_devp[i].data, 0, MEMDEV_SIZE); 
  
       /*初始化等待隊列*/ 
     init_waitqueue_head(&(mem_devp[i].inq)); 
  } 
    
  return 0; 

  fail_malloc: 
  unregister_chrdev_region(devno, 1); 
  
  return result; 

測試函數

寫函數

相關文章
相關標籤/搜索