驅動程序運行在內核空間中,應用程序運行在用戶空間中,二者是不能直接通訊的。但在實際應用中,在設備已經準備好的時候,咱們但願通知用戶程序設備已經ok,用戶程序能夠讀取了,這樣應用程序就不須要一直查詢該設備的狀態,從而節約了資源,這就是異步通知。好,那下一個問題就來了,這個過程如何實現呢?簡單,兩方面的工做。
一 驅動方面:
1. 在設備抽象的數據結構中增長一個struct fasync_struct的指針
2. 實現設備操做中的fasync函數,這個函數很簡單,其主體就是調用內核的fasync_helper函數。
3. 在須要向用戶空間通知的地方(例如中斷中)調用內核的kill_fasync函數。
4. 在驅動的release方法中調用前面定義的fasync函數
呵呵,簡單吧,就三點。其中fasync_helper和kill_fasync都是內核函數,咱們只須要調用就能夠了。在
1中定義的指針是一個重要參數,fasync_helper和kill_fasync會使用這個參數。
二 應用層方面
1. 利用signal或者sigaction設置SIGIO信號的處理函數
2. fcntl的F_SETOWN指令設置當前進程爲設備文件owner
3. fcntl的F_SETFL指令設置FASYNC標誌
完成了以上的工做的話,當內核執行到kill_fasync函數,用戶空間SIGIO函數的處理函數就會被調用了。
呵呵,看起來不是很複雜把,讓咱們結合具體代碼看看就更明白了。
先從應用層代碼開始吧:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#define MAX_LEN 100
//處理函數,沒什麼好講的,用戶本身定義
void input_handler(int num)
{
char data[MAX_LEN];
int len;
//讀取並輸出STDIN_FILENO上的輸入
len = read(STDIN_FILENO, &data, MAX_LEN);
data[len] = 0;
printf("input available:%s\n", data);
}
void main()
{
int oflags;
//啓動信號驅動機制,將SIGIO信號同input_handler函數關聯起來,一旦產生SIGIO信號,就會執行input_handler
signal(SIGIO, input_handler);
//STDIN_FILENO是打開的設備文件描述符,F_SETOWN用來決定操做是幹什麼的,getpid()是個系統調用,
//功能是返回當前進程的進程號,整個函數的功能是STDIN_FILENO設置這個設備文件的擁有者爲當前進程。
fcntl(STDIN_FILENO, F_SETOWN, getpid());
//獲得打開文件描述符的狀態
oflags = fcntl(STDIN_FILENO, F_GETFL);
//設置文件描述符的狀態爲oflags | FASYNC屬性,一旦文件描述符被設置成具備FASYNC屬性的狀態,
//也就是將設備文件切換到異步操做模式。這時系統就會自動調用驅動程序的fasync方法。
fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);
//最後進入一個死循環,程序什麼都不幹了,只有信號能激發input_handler的運行
//若是程序中沒有這個死循環,會當即執行完畢
while (1);
}
再看驅動層代碼,驅動層其餘部分代碼不變,就是增長了一個fasync方法的實現以及一些改動
//首先是定義一個結構體,其實這個結構體存放的是一個列表,這個
//列表保存的是一系列設備文件,SIGIO信號就發送到這些設備上
static struct fasync_struct *fasync_queue;
//fasync方法的實現
static int my_fasync(int fd, struct file * filp, int on)
{
int retval;
//將該設備登記到fasync_queue隊列中去
retval=fasync_helper(fd,filp,on,&fasync_queue);
if(retval<0)
{
return retval;
}
return 0;
}
在驅動的release方法中咱們再調用my_fasync方法
int my_release(struct inode *inode, struct file *filp)
{
//..processing..
drm_fasync(-1, filp, 0);
//..processing..
}
這樣後咱們在須要的地方(好比中斷)調用下面的代碼,就會向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(struct fasync_struct *fa, int sig, int band);
若是這個驅動支持異步通知, 這個函數可用來發送一個信號到登記在 fa 中的進程.
2.
fasync_helper 用來向等待異步信號的設備鏈表中添加或者刪除設備文件, kill_fasync被用來通知擁有相關設備的進程. 它的參數是被傳遞的信號(經常是 SIGIO)和 band, 這幾乎都是 POLL_IN[25](可是這可用來發送"緊急"或者帶外數據, 在網絡代碼裏).node