Linux設備驅動中的阻塞與非阻塞I/O
本文從兩個方面進行闡述阻塞與非阻塞、等待隊列、輪詢機制;
1. 阻塞與非阻塞
1.1 阻塞與非阻塞概念
阻塞:在執行設備操作時,當資源被佔用,進程將被掛起處於休眠狀態,同時將從調度器的運行隊列中移走,直至等待的條件滿足時,才用中斷來進行喚醒,否則將無法繼續執行。
非阻塞:當執行設備操作時,當資源被佔用無法獲取時,或者立即返回,或者輪詢等待,對cpu是一種不斷的消耗。
1.2 等待隊列
等待隊列是用來對阻塞的喚醒機制,同時與進程調度機制相結合,實現異步事件的通知機制。
等待隊列的基本操作:
1)定義“等待隊列頭”
wait_queue_head_t my_queue;
<strong>2) 初始化等待隊列頭</strong>
init_waitqueue_head(&my_queue);
<strong>3)定義等待隊列</strong>
DECLEARE_WAITQUEUE(name, tsk);
<strong>4)添加/刪除等待隊列</strong>
void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
<strong>5)等待事件</strong>
wait_event(queue, condition);
wait_event_interruptible(queue, condition);
wait_event_timeout(queue, condition);
wait_event_interruptible_timeout(queue, condition);
<strong>6)喚醒隊列</strong>
與等待隊列成對使用
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
<strong>7)等待隊列上睡眠</strong>
sleep_on(wait_queue_heat_t *q);
interruptible_sleep_on(wait_queue_head_t *q);
在驅動程序中改變進程狀態並調用schedule代碼實例:
static ssize_t xxx_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
...
<strong>DECLEARE_WAITQUEUE(wati, current); /*定義等待隊列*/
add_wait_queue(&xxx_wait, &wait); /*添加等待隊列*/</strong>
ret = count;
/*等待設備緩衝區可寫*/
do
{
avail = device_writable(...);
if(avail < 0)
<strong>__set_current_state(TASK_INTERRUPTIBLE); /*改變進程狀態*/</strong>
if(avail < 0)
{<strong>
</strong> if(file->f_flags * O_NONBLOC) /*非阻塞*/
{
if(!ret)
ret = - EAGAIN;
goto out;
}
<strong>schedule(); /*調度其它進程*/
if(signal_pending(current) ) /*如果是因爲信號量喚醒*/</strong>
{
if(!ret)
{
ret = - ERESTARTSYS;
goto out;
}
}
}
}while (avail < 0)
/*寫設備緩衝區*/
device_write(...);
out:
<strong>remove_wait_queue(&xxx_wait, &wait); /*將等待隊列移出等待隊列頭*/
set_current_state(TASK_RUNNING); /*設置進程狀態*/</strong>
return ret;
}
1.3 阻塞與非阻塞實例
2. 輪詢機制
使用非阻塞IO的應用程序通過使用select()和poll()對設備進行非阻塞的訪問。
3. 總結
阻塞與非阻塞是IO操作的兩種方式,阻塞通過等待隊列來實現,而非阻塞在應用程序層通過select()和poll()來實現,在驅動層通過poll來實現。
4.參考文獻
1)Linux設備驅動開發詳解 宋寶華著