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