驅動程序運行在內核空間中,應用程序運行在用戶空間中,兩者是不能直接通信的。但在實際應用中,在設備已經准備好的時候,我們希望通知用戶程序設備已經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](但是這可用來發送”緊急”或者帶外數據, 在網絡代碼裡).