天天看點

9---異步通知與異步IO

 異步通知的概念

一旦裝置就緒,則主動通知應用程式,這樣應用程式根不需要查詢裝置狀态。在原理上,一個程序收到一個信号與處理器收到一個中斷請求可以說是一樣的

阻塞、非阻塞、異步通知幾種方式I/O互補 ,根據場合合理選擇

  • 阻塞 I/O :一直等待裝置可通路後再通路,
  • 非阻塞I/O:使用poll()查詢裝置是否可通路,
  • 異步通知:裝置通知自身可通路,實作了異步I/O

 異步通知程式設計

信号的接收

1 void sigterm_handler(int signo) {
3 printf("Have caught sig N.O. %d\n", signo);//程序捕獲信号并輸出信号值
4 exit(0);
5 }
6
7 int main(void){
9 signal(SIGINT, sigterm_handler);//按下[Ctrl+c]組合鍵将向其發出SIGINT信号
10 signal(SIGTERM, sigterm_handler);//kill正在運作的程序将向其發出SIGTERM信号
11 while(1);
13 return 0;
14 }

//函數signal
sighandler_t signal(int signum,//信号的值
                   sighandler_t handler);//針對前面信号值的處理函數
           

在使用者空間接受一個裝置釋放的信号,需完成以下三步

  • 通過F_SETOWN IO 控制指令設定裝置檔案的擁有者為本程序,這樣從裝置驅動發出的信号才能被本程序接收到。
  • 通過F_SETFL IO 控制指令設定裝置檔案支援FASYNC,即異步通知模式。
  • 通過signal()函數連接配接信号和信号處理函數。

應用執行個體

1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <stdio.h>
4 #include <fcntl.h>
5 #include <signal.h>
6 #include <unistd.h>
7 #define MAX_LEN 100
8 void input_handler(int num) {
10 char data[MAX_LEN];
11 int len;
13 //讀取并輸出STDIN_FILENO上的輸入
14 len = read(STDIN_FILENO, &data, MAX_LEN);
15 data[len] = 0;
16 printf("input available:%s\n", data);
17 }
18
19 main(){
21 int oflags;
23 //啟動信号驅動機制
24 signal(SIGIO, input_handler);
25 fcntl(STDIN_FILENO, F_SETOWN, getpid());//設定本程序為STDIN_FILENO 檔案的擁有者(owner),讓核心知道應将信号發給哪個程序
26 oflags = fcntl(STDIN_FILENO, F_GETFL);
27 fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);//為啟用異步通知,對裝置設定FASYNC标志
29 //進入一個死循環來保持程序不終止,如果程式中沒有這個死循環,會立即執行完畢
31 while (1);
32 }

整個程式的執行效果如下:
[[email protected] driver_study]# ./signal_test
I am Chinese.
input available: I am Chinese.
I love Linux driver.
input available: I love Linux driver
           

信号的釋放

為了使裝置支援異步通知機制,驅動程式中涉及以下3 項工作,和應用程式中的3 項工作是一一對應。

  • 支援 F_SETOWN 指令,在這個控制指令進行中核心完成設定filp->f_owner 為對應程序ID。
  • 支援F_SETFL指令的處理,每當FASYNC标志改變時,驅動程式中定義并執行fasync()函數。
  •  在裝置資源可獲得時,調用kill_fasync()函數激發相應的信号。
//将fasync_struct 結構體指針放在裝置結構體中
1 struct xxx_dev {
3 struct cdev cdev; /*cdev結構體*/
4 ...
5 struct fasync_struct *async_queue; /* 異步結構體指針*/
6 };

//支援異步通知的裝置驅動fasync()函數的模闆
1 static int xxx_fasync(int fd, struct file *filp, int mode){
3 struct xxx_dev *dev = filp->private_data;
//處理 FASYNC标志變更的函數
4 return fasync_helper(fd, filp, mode, &dev->async_queue);
5 }

//支援異步通知的裝置驅動信号釋放的模闆
1 static ssize_t xxx_write(struct file *filp, const char _ _user *buf, size_t count, loff_t *f_pos){
4 struct xxx_dev *dev = filp->private_data;
5 ...
6 /* 産生異步讀信号*/
7 if (dev->async_queue)
//釋放信号用的函數
8 kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
9 ...
10 }

//支援異步通知的裝置驅動release()函數的模闆
1 static int xxx_release(struct inode *inode, struct file *filp) {
3 struct xxx_dev *dev = filp->private_data;
4 /* 将檔案從異步通知清單中删除*/
5 xxx_fasync(-1, filp, 0);
6 ...
7 return 0;
8 }
           

繼續閱讀