先簡要說一下這兩種機制:
中斷、poll機制一般都是應用程序主動去查詢有無中斷髮生,即在應用程序主函數中採用while(1)不斷查詢有無中斷髮生,這樣要額外消耗一個線程去查詢(CPU佔用率極高),及時採用poll機制降低CPU佔用率,但是還是必須消耗一個線程不斷循環查詢while(1);
而異步通知是當中斷髮生時,驅動程序發送一個信號去提醒應用程序去讀並執行用戶編寫的信號處理函數(和中斷服務函數類似,當有信號過來時自動執行此函數),它是通過信號signal實現的,不需要另外消耗一個線程去一直查詢,當沒有中斷髮生時是什麼也不做;當有中斷觸發時,驅動程序告訴應用程序中斷信號發生,調用相應信號處理函數,以此達到異步通知目的。
接下去的內容是在假設讀者能理解併成功編寫線程輪詢的驅動函數及應用程序的情況下。
OK,進入正題
------------------------------------------------------
驅動程序方面
------------------------------------------------------
爲了使設備支持異步通知機制,驅動程序涉及以下3項工作:
1.支持F_SETOWN命令,能在這個控制命令處理設置filp->f_owner爲對應進程ID。此工作已由內核完成;
2.支持F_SETFL命令的處理,每當FASYNV標誌改變時,驅動程序中的fasync()函數將得以執行,驅動中應該實現fasync()函數,通常通過函數結構體指定name_fasync函數來調用相應函數。
3.在設備資源可獲得時,調用kill_fasync()函數激發相應的信號
1.驅動通過kill_fasync()發送信號給應用程序,觸發應用程序調用信號處理函數(一般在中斷觸發函數裏面喚醒進程之後):
void kill_fasync(struct fasync_struct **fp, int sig, int band)
2.它在fasync_helper()函數裏完成*fp結構體的初始化:
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
3.並且要把它添加進函數結構體file operations裏面,讓應用程序能夠調用:
.fasync = name_fasync,
fasync的函數原型:int (*fasync) (int, struct file *, int);
如:
static struct fasync_struct *fasync_queue;
...
kill_fasync(&fasync_queue, SIGIO, POLL_IN);
...
static int name_fasync(int fd, struct file *filp, int on)
{
return fasync_helper(fd, filp, on, &fasync_queue);
}
------------------------------------------------------
測試程序設計
------------------------------------------------------
1.編寫信號處理函數signal_function();
2.在主函數裏註冊信號處理函數:
/*內核裏面發出信號信號一般是發出SIGIO,表示IO口有數據可讀或寫*/
signal(SIGIO, signal_function);
3.應用程序調用fcntl()這一函數來告訴驅動程序它的進程pid號:
fcntl(fd, F_SETOWN, getpid());
4.應用程序讀出flag,並在flag上修改,加上一個fasync:
flag = fcntl(fd, F_GETFL);
flag |= FASYNC;
5.應用程序調用fcntl()接口來調用驅動中的name_fasync函數(即調用fasync_helper函數):
fcntl(fd, F_SETFL, flag);
應用程序例子:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
int fd;
void buttons_signal_function(int sig)
{
char buf[6];
read(fd, buf, 1);
printf("key = %x\n", buf[0]);
}
int main(int argc, char **argv)
{
int flag;
signal(SIGIO, buttons_signal_function);
fd = open("/dev/button_led", O_RDWR);
if (fd < 0)
{
printf("can't open /dev/buttons\n");
return -1;
}
fcntl(fd, F_SETOWN, getpid());
flag = fcntl(fd, F_GETFL);
flag |= FASYNC;
fcntl(fd, F_SETFL, flag);
while (1)
{
sleep(2);
}
return 0;
}