W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
盡管阻塞和非阻塞操作和 select 方法的結合對于查詢設備在大部分時間是足夠的, 一些情況還不能被我們迄今所見到的技術來有效地解決.
讓我們想象一個進程, 在低優(yōu)先級上執(zhí)行一個長計算循環(huán), 但是需要盡可能快的處理輸入數(shù)據(jù). 如果這個進程在響應新的來自某些數(shù)據(jù)獲取外設的報告, 它應當立刻知道當新數(shù)據(jù)可用時. 這個應用程序可能被編寫來調用 poll 有規(guī)律地檢查數(shù)據(jù), 但是, 對許多情況, 有更好的方法. 通過使能異步通知, 這個應用程序可能接受一個信號無論何時數(shù)據(jù)可用并且不需要讓自己去查詢.
用戶程序必須執(zhí)行 2 個步驟來使能來自輸入文件的異步通知. 首先, 它們指定一個進程作為文件的擁有者. 當一個進程使用 fcntl 系統(tǒng)調用發(fā)出 F_SETOWN 命令, 這個擁有者進程的 ID 被保存在 filp->f_owner 給以后使用. 這一步對內核知道通知誰是必要的. 為了真正使能異步通知, 用戶程序必須設置 FASYNC 標志在設備中, 通過 F_SETFL fcntl 命令.
在這 2 個調用已被執(zhí)行后, 輸入文件可請求遞交一個 SIGIO 信號, 無論何時新數(shù)據(jù)到達. 信號被發(fā)送給存儲于 filp->f_owner 中的進程(或者進程組, 如果值為負值).
例如, 下面的用戶程序中的代碼行使能了異步的通知到當前進程, 給 stdin 輸入文件:
signal(SIGIO, &input_handler); /* dummy sample; sigaction() is better */
fcntl(STDIN_FILENO, F_SETOWN, getpid());
oflags = fcntl(STDIN_FILENO, F_GETFL);
fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);
這個在源碼中名為 asynctest 的程序是一個簡單的程序, 讀取 stdin. 它可用來測試 scullpipe 的異步能力. 這個程序和 cat 類似但是不結束于文件尾; 它只響應輸入, 而不是沒有輸入.
注意, 但是, 不是所有的設備都支持異步通知, 并且你可選擇不提供它. 應用程序常常假定異步能力只對 socket 和 tty 可用.
輸入通知有一個剩下的問題. 當一個進程收到一個 SIGIO, 它不知道哪個輸入文件有新數(shù)據(jù)提供. 如果多于一個文件被使能異步地通知掛起輸入的進程, 應用程序必須仍然靠 poll 或者 select 來找出發(fā)生了什么.
對我們來說一個更相關的主題是設備驅動如何實現(xiàn)異步信號. 下面列出了詳細的操作順序, 從內核的觀點:
雖然實現(xiàn)第一步是容易的--在驅動部分沒有什么要做的--其他的步驟包括維護一個動態(tài)數(shù)據(jù)結構來跟蹤不同的異步讀者; 可能有幾個. 這個動態(tài)數(shù)據(jù)結構, 但是, 不依賴特殊的設備, 并且內核提供了一個合適的通用實現(xiàn)這樣你不必重新編寫同樣的代碼給每個驅動.
Linux 提供的通用實現(xiàn)是基于一個數(shù)據(jù)結構和 2 個函數(shù)(它們在前面所說的第 2 步和第 3 步被調用). 聲明相關材料的頭文件是<linux/fs.h>(這里沒新東西), 并且數(shù)據(jù)結構被稱為 struct fasync_struct. 至于等待隊列, 我們需要插入一個指針在設備特定的數(shù)據(jù)結構中.
驅動調用的 2 個函數(shù)對應下面的原型:
int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **fa);
void kill_fasync(struct fasync_struct **fa, int sig, int band);
fasync_helper 被調用來從相關的進程列表中添加或去除入口項, 當 FASYNC 標志因一個打開文件而改變. 它的所有參數(shù)除了最后一個, 都被提供給 fasync 方法并且被直接傳遞. 當數(shù)據(jù)到達時 kill_fasync 被用來通知相關的進程. 它的參數(shù)是被傳遞的信號(常常是 SIGIO)和 band, 這幾乎都是 POLL_IN25.
這是 scullpipe 如何實現(xiàn) fasync 方法的:
static int scull_p_fasync(int fd, struct file *filp, int mode)
{
struct scull_pipe *dev = filp->private_data;
return fasync_helper(fd, filp, mode, &dev->async_queue);
}
顯然所有的工作都由 fasync_helper 進行. 但是, 不可能實現(xiàn)這個功能在沒有一個方法在驅動里的情況下, 因為這個幫忙函數(shù)需要存取正確的指向 struct fasync_struct (這里是 與dev->async_queue)的指針, 并且只有驅動可提供這個信息.
當數(shù)據(jù)到達, 下面的語句必須被執(zhí)行來通知異步讀者. 因為對 sucllpipe 讀者的新數(shù)據(jù)通過一個發(fā)出 write 的進程被產(chǎn)生, 這個語句出現(xiàn)在 scullpipe 的 write 方法中.
if (dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
注意, 一些設備還實現(xiàn)異步通知來指示當設備可被寫入時; 在這個情況, 當然, kill_fasnyc 必須被使用一個 POLL_OUT 模式來調用.
可能會出現(xiàn)我們已經(jīng)完成但是仍然有一件事遺漏. 我們必須調用我們的 fasync 方法, 當文件被關閉來從激活異步讀者列表中去除文件. 盡管這個調用僅當 filp->f_flags 被設置為 FASYNC 時需要, 調用這個函數(shù)無論如何不會有問題并且是常見的實現(xiàn). 下面的代碼行, 例如, 是 scullpipe 的 release 方法的一部分:
/* remove this filp from the asynchronously notified filp's */
scull_p_fasync(-1, filp, 0);
這個在異步通知之下的數(shù)據(jù)結構一直和結構 struct wait_queue 是一致的, 因為 2 種情況都涉及等待一個事件. 區(qū)別是這個 struct file 被用來替代 struct task_struct. 隊列中的結構 file 接著用來存取 f_owner, 為了通知進程.
[25] POLL_IN 是一個符號, 用在異步通知代碼中; 它等同于 POLLIN|POLLRDNORM.
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: