阻塞型字元裝置驅動
[概述]
首先明確一點,不管你是睡眠、休眠還是阻塞,還是掛起,本質上都是把進程放到等待隊列上。
[休眠的實現]
休眠通過等待隊列進行處理。等待隊列是由等待某些事件發生的進程組成的簡單鏈表。核心中用wake_queue_head_t來代表等待隊列。等待隊列可以通過DECLARE_WAITQUEUE()靜態建立,也可以由Init_waitqueue_head()動態建立。進程把自己放入等待隊列中並設定成不可執行狀態。當與等待隊列相關的事件發生的時候,隊列上的進程會被喚醒。為了避免產生競爭條件,休眠和喚醒的實現不能有紕漏。
針對休眠,核心提供了一些簡單的介面。但那些介面會帶來競爭條件:有可能導致在判斷條件為真後,進程卻開始了休眠,那樣就會使進程無限期地休眠下去,所以,在核心中進行休眠的推薦操作就想複雜了一些:
DEFINE_WAIT(wait); Add_wait_queue(q, &wait);While (!condition) { Prepare_to_wait(&q,&wait, TASK_INTERRUPTIBLE); If(signal_pending(current)) /*處理訊號 */ Schedule();}Finish_wait(&q, &wait);
1) 調用宏DEFINE_WAIT()建立一個等待隊列的項;
2) 調用add_wait_queue()把自己加入到隊列中,該隊列會在進程等待的條件滿足時喚醒它,當然我們必須在其他地方編寫相關代碼,在事件發生時,對等待隊列執行wake_up()操作;
3) 調用prepare_to_wait()方法將進程的狀態變更為TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE。而且該函數如果有必要的話會將進程家回到等待隊列,這是在接下來的迴圈遍曆中所需要的;
4) 如果狀態被設定為TASK_INTERRUPTIBLE,則訊號喚醒進程。這就是所謂的偽喚醒,因此檢查並處理訊號;
5) 當進程被喚醒的時候,它會再次檢查條件是否為真。如果是,它就退出迴圈;如果不是,它再次調用schedule()並一直重複這步操作;
6) 當條件滿足後,進程將自己設定為TASK_RUNNING並調用finish_wait()方法把自己移出等待隊列;
如果需要瞭解這些函數的實現,可以參考http://edsionte.com/techblog/archives/1854
[一個阻塞型FIFO執行個體]
對上面休眠實現理論有了一個瞭解,看下面的代碼應該不難了。
[fifo結構體]
struct simple_chrdev_fifo { structcdev cdev; charfifo[MAX_SIZE]; intfifo_size; intcurrent_pos; /* read and writeposition in fifo */ structsemaphore sem; /* semaphore */ wait_queue_head_treadq, writeq; /* read queue andwrite queue */};
[阻塞型read方法]
static ssize_t simple_read(struct file*filp, char __user *buf, size_t count, loff_t*f_pos){ structsimple_chrdev_fifo *dev = filp->private_data; intret = 0; DEFINE_WAIT(wait); if(down_interruptible(&dev->sem)) return-ERESTARTSYS; add_wait_queue(&dev->readq,&wait); while(dev->current_pos == 0) { up(&dev->sem); if(filp->f_flags & O_NONBLOCK) return- EAGAIN; prepare_to_wait(&dev->readq,&wait, TASK_INTERRUPTIBLE); if(signal_pending(current)) { ret = - ERESTARTSYS; gotoout2; } schedule(); if(down_interruptible(&dev->sem)) { ret= -ERESTARTSYS; gotoout2; } } if(count > dev->current_pos) count= dev->current_pos; if(copy_to_user(buf, (void *)(dev->fifo), count)) { ret= -EFAULT; gotoout; } dev->current_pos-= count; ret= count; wake_up_interruptible(&dev->writeq);out: up(&dev->sem);out2: finish_wait(&dev->readq,&wait); returnret;}
[模組初始化函數]
static int __initsimple_chrdev_fifo_init(void){ …… /* * initialze wait queue */ init_waitqueue_head(&dev->readq); init_waitqueue_head(&dev->writeq); init_MUTEX(&dev->sem); ……}
使用init_waitqueue_head宏對等待讀隊列頭和等待寫隊列頭進行初始化;
這裡只貼出了重要的代碼,如果需要完整的代碼,請發email給我。