在很多應用中都需要應用程式主動去查詢驅動中是否有資料可讀或者是否可以向驅動寫入資料,對於單線程的應用,這可能會導致進程阻塞。當然,可以使用select來不斷輪詢驅動是否可讀或可寫,但是這並不是很好的解決方案,更好的解決方式是由驅動主動通知應用程式其狀態,而不是應用程式主動去查詢驅動的狀態。
非同步通知就類似於回調,應用程式首先向驅動註冊一個回呼函數,然後應用程式就可以無阻塞地去做其他事情,當驅動認為其已經可以被寫入或者被讀取時就會調用應用程式之前註冊的回呼函數,從而實現非同步通知。下面以一個簡單的例子說明驅動中非同步通知的用法。
建立eclipse工程,具體過程見前面的文章,編寫非同步通知驅動程式,為了方便說明,這裡使用核心定時器的逾時操作作為觸發非同步通知的行為(實際使用中應根據具體的時機來觸發),驅動程式比較簡單,直接上代碼:
1 #include <linux/miscdevice.h> 2 #include <linux/delay.h> 3 #include <linux/kernel.h> 4 #include <linux/module.h> 5 #include <linux/init.h> 6 #include <linux/mm.h> 7 #include <linux/fs.h> 8 #include <linux/types.h> 9 #include <linux/delay.h> 10 #include <linux/moduleparam.h> 11 #include <linux/errno.h> 12 #include <linux/ioctl.h> 13 #include <linux/cdev.h> 14 #include <linux/string.h> 15 #include <linux/list.h> 16 #include <linux/poll.h> 17 #include <linux/fcntl.h> 18 #include <linux/signal.h> //for SIGIO,POLL_IN 19 #include <linux/timer.h> 20 #include <linux/jiffies.h> 21 22 //裝置名稱 23 #define DEVICE_NAME "sync" 24 //主裝置號 25 #define DEVICE_MAJOR 228 26 27 //非同步通知結構變數 28 struct fasync_struct *async_queue; 29 //核心定時器 30 struct timer_list my_timer; 31 32 33 //定時器逾時函數 34 static void timer_function(unsigned long arg) 35 { 36 //發送訊號到使用者空間,POLL_IN表示可寫 37 kill_fasync(&async_queue, SIGIO, POLL_IN); 38 } 39 40 //使用者空間調用fcntl函數時會調用這個函數 41 static int async_fasync(int fd, struct file *filp, int mode) 42 { 43 //根據使用者空間的需要,擷取或設定相應的屬性 44 return fasync_helper(fd, filp, mode, &async_queue); 45 } 46 47 static int async_open(struct inode *node,struct file *flip) 48 { 49 //初始化定時器 50 init_timer(&my_timer); 51 my_timer.data = 0; 52 //定時器逾時函數 53 my_timer.function = &timer_function; 54 //定時器逾時時間(目前時間的後1000個時鐘滴答) 55 my_timer.expires = jiffies + 5*200; 56 //啟動定時器 57 add_timer(&my_timer); 58 59 return 0; 60 } 61 62 63 static struct file_operations async_fops = 64 { 65 .owner = THIS_MODULE, 66 .fasync = async_fasync, 67 .open = async_open, 68 }; 69 70 71 static int __init dev_init(void) 72 { 73 int ret; 74 75 //註冊字元裝置 76 ret = register_chrdev(DEVICE_MAJOR , DEVICE_NAME, &async_fops); 77 if (ret < 0) 78 { 79 printk(DEVICE_NAME " can't register\n"); 80 return ret; 81 } 82 else 83 { 84 printk(DEVICE_NAME " register successful\n"); 85 return 0; 86 } 87 88 } 89 90 91 static void __exit dev_exit(void) 92 { 93 //撤銷字元裝置 94 unregister_chrdev(DEVICE_MAJOR , DEVICE_NAME); 95 printk("unregister success \n"); 96 } 97 98 99 module_init(dev_init);100 module_exit(dev_exit);101 MODULE_LICENSE("GPL");102 MODULE_AUTHOR("LKN@SCUT");
編譯成功後會產生fasync.ko檔案,先載入該驅動:
#insmod fasync.ko
然後,在/dev目錄下建立裝置檔案:
#mknod sync c 228 1
接著,編寫應用程式:
1 #include <unistd.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <stdlib.h> 5 #include <stdio.h> 6 #include <signal.h> 7 #include <string.h> 8 9 //是否已接收到訊號標誌10 int flag = 0;11 12 //訊號處理函數定義,由核心調用執行13 void sig_handler(int signo) 14 { 15 if (signo==SIGIO) 16 { 17 printf("receive successful!!!\n");18 flag = 1;19 } 20 } 21 22 int main() 23 { 24 struct sigaction action; 25 int async_fd; 26 27 memset(&action, 0, sizeof(action)); 28 //訊號處理函數29 action.sa_handler = sig_handler; 30 action.sa_flags = 0; 31 //註冊訊號類型32 sigaction(SIGIO, &action, NULL); 33 34 async_fd = open("/dev/sync", O_RDONLY); 35 if(async_fd < 0)36 {37 printf("can not open /dev/sync \n");38 return -1;39 }40 41 //告訴驅動當前進程的PID42 fcntl(async_fd, F_SETOWN, getpid()); 43 //設定驅動的FASYNC屬性,支援非同步通知44 fcntl(async_fd, F_SETFL, fcntl(async_fd, F_GETFL) | FASYNC); 45 46 printf("waiting for receive...\n");47 48 while(!flag)49 {50 } 51 52 close(async_fd); 53 54 return 0; 55 }
編譯之:
gcc -o fasync_test fasync_test.c
運行應用程式,效果