Linux裝置驅動之阻塞I O與非同步通知

2021-07-25 06:16:38 字數 3277 閱讀 5469



阻塞與非阻塞訪問是 i/o 操作的兩種不同模式,前者在 i/o 操作暫時不可進行時會讓程序睡眠,後者則不然。在裝置驅動中阻塞 i/o一般基於等待佇列來實現,等待佇列可用於同步驅動中事件發生的先後順序。使用非阻塞 i/o 的應用程式也可借助輪詢函式來查詢裝置是否能立即被訪問,使用者空間呼叫 select()和 poll()介面,裝置驅動提供 poll()函式。裝置驅動的 poll()本身不會阻塞,但是 poll()和 select()系統呼叫則會阻塞地等待檔案描述符集合中的至少乙個可訪問或超時。

在裝置驅動中使用非同步通知可以使得對裝置的訪問可進行時,由驅動主動通知應用程式進行訪問。這樣,使用無阻塞 i/o 的應用程式無需輪詢裝置是否可訪問,而阻塞訪問也可以被類似「中斷」的非同步通知所取代。

程序阻塞會進入睡眠,進入睡眠時應確保有其他程序或者中斷將其喚醒,而用於喚醒的程序必須知道正在睡眠的程序在哪兒,於是就有了等待佇列——睡眠程序的佇列,都等待乙個特定的喚醒訊號。

include 

/* defined and initialized statically */

declare_wait_queue_head(name);

/* dynamicly */

wait_queue_head_t my_queue;

init_waitqueue_head(&my_queue);

/* 

condition為等待條件,睡眠前後都要判斷,如果condition == false則繼續睡眠;

interruptible版被中斷時會返回非0值,驅動應據此返回-erestartsys;

timeout版等待超時後返回0,單位jiffies。

*/wait_event(queue, condition)

wait_event_interruptible(queue, condition)

wait_event_timeout(queue, condition, timeout)

wait_event_interruptible_timeout(queue, condition, timeout)

/*

對應的常見的喚醒方法會喚醒等待佇列頭所管理的等待佇列中所有程序;

interruptible版只能喚醒interruptible版的wait_event。

*/void

wake_up

(wait_queue_head_t *queue)

; void

wake_up_interruptible

(wait_queue_head_t *queue)

;

使用非阻塞 i/o的應用程式通常會使用 select()和 poll()系統呼叫查詢是否可對裝置進行無阻塞的訪問。 select()和poll()系統呼叫最終會引發裝置驅動中的 poll()函式被執行。

裝置驅動中 poll()函式的原型是:

include /*

wait: 輸入的輪詢表指標

返回是否能對裝置進行無阻塞讀、寫訪問的掩碼:

pollin | pollrdnorm -> 可讀

pollout | pollwrnorm -> 可寫

pollhup -> 讀到檔案尾

pollerr -> 裝置錯誤

read more: poll.h

*/unsigned

int(*poll)

(struct file * filp, struct poll_table* wait)

;

關鍵的用於向 poll_table 註冊等待佇列的 poll_wait()函式的原型如下:

/* 把管理當前程序的佇列頭queue新增到wait引數指定的等待列表(poll_table)中 */

void

poll_wait

(struct file *filp, wait_queue_heat_t *queue, poll_table * wait)

;

非同步通知的意思是:一旦裝置就緒,則主動通知應用程式,這樣應用程式根本就不需要查詢裝置狀態,這一點非常類似於硬體上「中斷」的概念,比較準確的稱謂是「訊號驅動的非同步 i/o」。

阻塞 i/o 意味著一直等待裝置可訪問後再訪問,非阻塞 i/o 中使用 poll()意味著查詢裝置是否可訪問,而非同步通知則意味著裝置通知自身可訪問,實現了非同步 i/o。

為了使裝置支援非同步通知機制,驅動程式中涉及 3 項工作:

支援 f_setown 命令,能在這個控制命令處理中設定 filp->f_owner 為對應程序 id。不過此項工作已由核心完成,裝置驅動無需處理。

支援 f_setfl 命令的處理,每當 fasync 標誌改變時,驅動程式中的 fasync()函式將得以執行。因此,驅動中應該實現 fasync()函式。

在裝置資源可獲得時,呼叫 kill_fasync()函式激發相應的訊號。

驅動中的上述 3 項工作和應用程式中的 3 項工作是一一對應的,如圖所示為非同步通知處理過程中使用者空間和裝置驅動的互動:

裝置驅動中非同步通知程式設計比較簡單,主要用到一項資料結構和兩個函式。資料結構是fasync_struct 結構體, 兩個函式分別是:

/* 處理 fasync 標誌變更 */

intfasync_helper

(int fd, struct file *filp, int mode, struct fasync_struct **fa)

; /* 釋放訊號給使用者 */

void

kill_fasync

(struct fasync_struct **fa, int sig, int band)

;

使用方法:

static

int***_fasync

(int fd, struct file *filp, int mode)

static ssize_t ***_write

(struct file *filp, const

char __user *buf, size_t count, loff_t *f_pos)

static

int***_release

(struct inode *inode, struct file *filp)

linux裝置驅動(8)阻塞型IO

當驅動程式無法立即滿足請求,該如何響應?如當我們想要寫入的時候,裝置對應的緩衝區已滿,或者是當我們想要讀的時候當前緩衝區是空的。為了提高cpu的效率,我們的驅動程式應該阻塞等待該程序,將其置於休眠狀態直到請求可繼續。休眠 sleep 對於程序來講意味著什麼?當乙個程序被置入休眠時,他會被標記為一種特...

Linux 裝置驅動阻塞 非阻塞IO 等待佇列

阻塞 顧名思義,就是指在執行裝置操作時若不能獲得資源則掛起操作,直到滿足可操作的條件後再進行操作,被掛起的程序進入休眠狀態,被從排程器的執行佇列移 走,直到等待的條件滿足。非阻塞 就是反過來,程序在不能進行裝置操作時並不掛起,它或者放棄,或者不停的查詢,直到可以進行位置。小王,明白了沒這兩個基本的概...

Linux裝置驅動中的阻塞和非阻塞I O

阻塞操作 是指在執行裝置操作時,若不能獲得資源,則掛起程序直到滿足操作條件後再進行操作。被掛起的程序進入休眠,被從排程器移走,直到條件滿足。非阻塞操作 在不能進行裝置操作時,並不掛起,它或者放棄,或者不停地查詢,直到可以進行操作。非阻塞應用程式通常使用select系統呼叫查詢是否可以對裝置進行無阻塞...