Linux非同步通知 fasync

2021-06-22 18:11:11 字數 3308 閱讀 9397

要弄明白這個問題,我們得從最基本的原理開始。我們知道,驅動程式執行在核心空間中,應用程式執行在使用者空間中,兩者是不能直接通訊的。但在實際應用中,在裝置已經準備好的時候,我們希望通知使用者程式裝置已經ok,使用者程式可以讀取了,這樣應用程式就不需要一直查詢該裝置的狀態,從而節約資源,這就是非同步通知。

好,那下乙個問題就來了,這個過程如何實現呢?簡單,兩方面的工作。

1.驅動方面:

呵呵,簡單吧,就三點。其中fasync_helper和kill_fasync都是核心函式,我們只需要呼叫就可以了。在裝置抽象的資料結構中定義的指標是乙個重要引數,fasync_helper和kill_fasync會使用這個引數。

2.應用層方面:

完成了以上的工作的話,當核心執行到kill_fasync函式,使用者空間sigio函式的處理函式就會被呼叫了。

呵呵,看起來不是很複雜把,讓我們結合具體**看看就更明白了。

先從應用層**開始吧:

#include

#include

#include

#include

#include

#include

#define max_len 100

void input_handler(int num)

//處理函式,沒什麼好講的,使用者自己定義

main()

再看驅動層**,驅動層其他部分**不變,就是增加了乙個fasync方法的實現以及一些改動

static struct fasync_struct *fasync_queue;

static int my_fasync(int fd, struct file * filp, int on)

在驅動的release方法中我們再呼叫my_fasync方法

int my_release(struct inode *inode, struct file *filp)

這樣後我們在需要的地方(比如中斷)呼叫下面的**,就會向fasync_queue佇列裡的裝置傳送sigio訊號

,應用程式收到訊號,執行處理程式

if(fasync_queue)

kill_fasync(&fasync_queue, sigio, poll_in);

好了,這下大家知道該怎麼用非同步通知機制了吧?

以下是幾點說明[1]:

1 兩個函式的原型

int fasync_helper(struct inode *inode, struct file *filp, int mode,struct fasync_struct **fa);

乙個"幫忙者", 來實現 fasync 裝置方法. mode 引數是傳遞給方法的相同的值, 而 fa指標指向乙個裝置特定的 fasync_struct*

void kill_fasync(structfasync_struct *fa, int sig, int band);

如果這個驅動支援非同步通知, 這個函式可用來傳送乙個訊號到登記在 fa 中的程序. 2.

fasync_helper 被呼叫來從相關的程序列表中新增或去除入口項, 當 fasync標誌因乙個開啟檔案而改變

. 它的所有引數除了最後乙個, 都被提供給 fasync方法並且被直接傳遞. 當資料到達時 kill_fasync

被用來通知相關的程序. 它的引數是被傳遞的訊號(常常是sigio)和 band, 這幾乎都是 poll_in[25](但

是這可用來傳送"緊急"或者帶外資料,在網路**裡).

這是 scullpipe 如何實現 fasync 方法的:

static int scull_p_fasync(intfd, struct file *filp, int mode)

顯然所有的工作都由 fasync_helper 進行. 但是,不可能實現這個功能在沒有乙個方法在驅動裡的情況

下, 因為這個幫忙函式需要訪問正確的指向 structfasync_struct (這裡是 與dev->async_queue)的指標,並且只有驅動可提供這個資訊.

當資料到達, 下面的語句必須被執行來通知非同步讀者. 因為對sucllpipe 讀者的新資料通過乙個發出

write 的程序被產生, 這個語句出現在 scullpipe的 write 方法中.

if (dev->async_queue)

kill_fasync(&dev->async_queue, sigio, poll_in);

注意, 一些裝置還實現非同步通知來指示當裝置可被寫入時;在這個情況, 當然, kill_fasnyc 必須被使用

乙個 poll_out 模式來呼叫.

1.上一節我們已經學習了用poll輪詢資料,來避免不必要的休眠,但是事實上,輪詢的直接負面作用就是效率低下,這樣一節我們學習如何使用非同步通知io來提高效率

2. fcntl系統呼叫

int fcntl(int fd, int cmd, long arg);

fcntl的作用是改變乙個已開啟檔案的屬性,fd是要改變的檔案的描述符,cmd是命令羅列如下:

f_dupfd, f_getfd, f_setfd, f_getfl, f_setfl, f_setlk, f_setlkw,f_getlk, f_getown, f_setown

本節只關心f_setown(設定非同步io所有權),f_getfl(獲取檔案flags),f_setfl(設定檔案flags)

arg是要改變的屬性內容

3. 使用者程序啟用非同步通知機制

首先,設定乙個程序作為乙個檔案的屬主(owner),這樣核心就知道該把檔案的訊號傳送給哪個程序

fcntl(fd, f_setown, getpid()); // getpid()就是當前程序咯

然後,給檔案設定fasync標誌,以啟用非同步通知機制

fcntl(fd, f_setfl, fcntl(fd, f_getfl) | fasync);

4. 缺陷

當有多個檔案傳送非同步通知訊號給乙個程序時,程序無法知道是哪個檔案傳送的訊號,這時候還是要借助poll的幫助完成io

5. 從驅動程式的角度考慮

當檔案的狀態標誌設定了fasync操作時,驅動程式會呼叫fasync的函式。

fasync的實現相當簡單

static int scull_p_fasync(int fd, struct file *filp, intmode)

當有新的資料到達時,驅動程式應該傳送乙個sigio給使用者,這個操作用kill_fasync方法完成

if(dev->async_queue)

kill_fasync(&dev->async_queue, sigio, poll_in);

Linux非同步通知fasync

實現非同步通知,核心需要知道幾個東西 哪個檔案 filp 什麼訊號 sigiio 發給哪個程序 pid 收到訊號後做什麼 sig handler 這些都由前兩個步驟完成了。在這裡,同樣需要把乙個結構體 struct fasync struct 新增到核心的非同步佇列頭 名字是我自己取的 中。這個結構...

字元裝置驅動之筆記 非同步通知(fasync)

發訊號 1.誰發 2.發給誰 3.發什麼 4.怎麼發 5.收到訊號後做什麼 驅動和應用 發訊號 1.誰發 按鍵中斷服務程式 2.發給誰 使用按鍵的應用程式 3.發什麼 sigio 4.怎麼發 kill fasync 誰,sigio 5.收到訊號後做什麼 sighandler t signal int...

Linux核心的非同步通知

非同步通知類似於中斷,主要用於實現驅動通過傳送訊號通知應用程式。應用層 void my signal fun int signum int main int argc,char argv fcntl fd,f setown,getpid 告訴驅動要發訊號給本應用程式。oflags fcntl fd,...