高級字符驅動學習--阻塞型I/0

提出問題:若驅動程序無法立即滿足請求,該如何響應? 比如:當數據不可用時調用read,或是在緩衝區已滿時,調用write

解決問題:驅動程序應該(默認)該阻塞進程,將其置入休眠狀態直到請求可繼續。

 

休眠:

當一個進程被置入休眠時,它會被標記爲一種特殊狀態並從調度器運行隊列中移走,直到某些情況下修改了這個狀態,才能運行該進程。

安全進入休眠兩原則:

1.永遠不要在原子上下文中進入休眠。(原子上下文:在執行多個步驟時,不能有任何的併發訪問。這意味着,驅動程序不能再擁有自旋鎖,seqlock,或是RCU鎖時,休眠)

2.對喚醒之後的狀態不能做任何假定,因此必須檢查以確保我們等待的條件真正爲真

 

臨界區  vs  原子上下文

原子上下本:一般說來,具體指在中斷,軟中斷,或是擁有自旋鎖的時候。

臨界區:每次只允許一個進程進入臨界區,進入後不允許其它進程訪問。

 

other question:

要休眠進程,必須有一個前提:有人能喚醒進程,而起這個人必須知道在哪兒能喚醒進程,這裏,就引入了“等待隊列”這個概念。

等待隊列:就是一個進程鏈表(我的理解:是一個休眠進程鏈表),其中包含了等待某個特定事件的所有進程。

等待隊列頭:wait_queue_head_t,定義在<linux/wait.h>

定義方法:靜態  DECLARE_QUEUE_HEAD(name)

                      動態  wait_queue_head_t  my_queue;

                                 init_waitqueue_head(&my_queue);

 

struct __wait_queue_head {
    spinlock_t lock;
    struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;

 

簡單休眠

linux最簡單的休眠方式是wait_event(queue,condition)及其變種,在實現休眠的同時,它也檢查進程等待的條件。四種wait_event形式如下:

wait_event(queue,condition);/*不可中斷休眠,不推薦*/

wait_event_interruptible(queue,condition);/*推薦,返回非零值意味着休眠被中斷,且驅動應返回-ERESTARTSYS*/

wait_event_timeout(queue,condition,timeout);

wait_event_interruptible_timeout(queue,conditon,timeout);/*有限的時間的休眠,若超時,則不管條件爲何值返回0*/

 

喚醒休眠進程的函數:wake_up

void wake_up(wait_queue_head_t  *queue);

void wake_up_interruptible(wait_queue_head  *queue);

慣例:用wake_up喚醒wait_event,用wake_up_interruptible喚醒wait_event_interruptible

 

休眠與喚醒 實例分析:

本例實現效果爲:任何從該設備上讀取的進程均被置於休眠。只要某個進程向給設備寫入,所有休眠的進程就會被喚醒。

static DECLARE_WAIT_QUEUE_HEAD(wq);

static int flag =0;

ssize_t sleepy_read(struct file *filp,char __user *buf,size_t count,loff_t *pos)

{

pirntk(KERN_DEBUG "process %i (%s) going to sleep\n",current->pid,current->comm);

wait_event_interruptible(wq,flag!=0);

flag=0;

printk(KERN_DEBUG "awoken %i (%s) \n",current->pid,current->comm);

return 0;

}

 

ssize_t sleepy_write(struct file *filp,const char __user *buf,size_t count,loff_t *pos)

{

printk(KERN_DEBUG "process %i (%s) awakening the readers ...\n",current->pid,current->comm);

flag=1;

wake_up_interruptible(&wq);

return count;  /*成功並避免重試*/

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章