歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux技術

LINUX中驅動異步通知應用程序的方法

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

Copyright © Linux教程網 All Rights Reserved