在介紹異步通知前先說下阻塞和非阻塞,阻塞式I/O是一直等待直到設備可以訪問消耗cpu資源,非阻塞式I/O是定期輪詢設備是否可以訪問即poll機制所實現的。 其實異步通知很好理解,一旦設備准備好,就主動通知應用程序,這種情況下應用程序就不需要查詢設備狀態 ,
特像硬件上常提的“中斷的概念”。 比較准確的說法其實應該叫做“信號驅動的異步I/O”,信號是在軟件層次上對中斷機制的一種模擬。 阻塞I/O意味著一直等待設備可訪問再訪問,非阻塞I/O意味著使用poll()來查詢是否可訪問,而異步通知則意味著設備通知應用程序自身 可訪問。
在linux中,異步通知是使用信號來實現的,而在linux,大概有30種信號,比如大家熟悉的ctrl+c的SIGINT信號,進程能夠忽略或者捕獲除過SIGSTOP和SIGKILL的全部信號,當信號背捕獲以後,有相應的函數來處理它。
在linux中,使用signal()函數來捕獲信號,它的函數運行如下:
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
第 一個參數就是指定的信號的值,而第二個參數便是此信號的信號處理函數,當為SIG_IGN,表示信號被忽略,當為SIG_DFL時,表示采用系統的默認方 式來處理該信號。當然,信號處理函數也可以自己定義。當signal()調用成功後,返回處理函數handler值,調用失敗後返回SIG_ERR。
為了使設備能夠實現異步通知功能需要在驅動程序和應用程序分別作如下操作
驅動程序中涉及以下3項工作:
1. 支持F_SETOWN命令,能在這個控制命令處理中設置filp->f_owner為對應進程ID。
不過此項工作已由內核完成,設備驅動無須處理。
2. 支持F_SETFL命令的處理,每當FASYNC標志改變時,驅動程序中的fasync()函數將得以執行。
驅動中應該實現fasync()函數。
3. 在設備資源可獲得時,調用kill_fasync()函數激發相應的信號
應用程序要有如下代碼:
fcntl(fd, F_SETOWN, getpid()); // 告訴內核,發給誰
Oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, Oflags | FASYNC); // 改變fasync標記,最終會調用到驅動的faync > fasync_helper:初始化/釋放fasync_struct
下面以tiny210按鍵驅動來實現異步通知的實例
按鍵驅動程序如下:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/poll.h> #include <linux/irq.h> #include <asm/irq.h> #include <asm/io.h> #include <linux/interrupt.h> #include <asm/uaccess.h> #include <mach/hardware.h> #include <linux/platform_device.h> #include <linux/cdev.h> #include <linux/miscdevice.h> #include <mach/map.h> #include <mach/gpio.h> #include <mach/regs-clock.h> #include <mach/regs-gpio.h> #define DEVICE_NAME "buttons_poll" struct button_desc{ int gpio; int number; char *name; struct timer_list timer; }; static struct button_desc buttons[]={ {S5PV210_GPH2(0),0,"KEY0"}, {S5PV210_GPH2(1),1,"KEY1"}, {S5PV210_GPH2(2),2,"KEY2"}, {S5PV210_GPH2(3),3,"KEY3"}, {S5PV210_GPH2(4),4,"KEY4"}, {S5PV210_GPH2(5),5,"KEY5"}, {S5PV210_GPH2(6),6,"KEY6"}, {S5PV210_GPH2(7),7,"KEY7"}, }; static volatile char key_values[]={ '0','0','0','0','0','0','0','0' }; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); static volatile int ev_press=0; static struct fasync_struct *button_fasync; static void mini210_buttons_timer(unsigned long _data) { struct button_desc *bdata=(struct button_desc*)_data; int down; int number; unsigned tmp; tmp = gpio_get_value(bdata->gpio); down =!tmp; printk("KEY%d:%08x\n",bdata->number,down); number=bdata->number; if (down!=(key_values[number]&1)){ key_values[number]='0'+down; ev_press=1; wake_up_interruptible(&button_waitq); kill_fasync(&button_fasync, SIGIO, POLL_IN); } } static irqreturn_t button_interrupt(int irq,void *dev_id) { struct button_desc *bdata=(struct button_desc*)dev_id; mod_timer(&bdata->timer,jiffies+msecs_to_jiffies(40)); return IRQ_HANDLED; } static int mini210_buttons_open(struct inode *inode,struct file *file) { int irq; int i; int err=0; printk("open buttons fan"); for (i=0;i<ARRAY_SIZE(buttons);i++) { if(!buttons[i].gpio) continue; setup_timer(&buttons[i].timer, mini210_buttons_timer, (unsigned long)&buttons[i]); irq = gpio_to_irq(buttons[i].gpio); err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_BOTH, buttons[i].name, (void *)&buttons[i]); if (err) break; } if (err) { i--; for (; i >= 0; i--) { if (!buttons[i].gpio) continue; irq = gpio_to_irq(buttons[i].gpio); disable_irq(irq); free_irq(irq, (void *)&buttons[i]); del_timer_sync(&buttons[i].timer); } return -EBUSY; } } static int mini210_buttons_close(struct inode *inode, struct file *file) { int irq, i; for (i = 0; i < ARRAY_SIZE(buttons); i++) { if (!buttons[i].gpio) continue; irq = gpio_to_irq(buttons[i].gpio); free_irq(irq, (void *)&buttons[i]); del_timer_sync(&buttons[i].timer); } return 0; }
static int mini210_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) { unsigned long err; if (!ev_press) { if (filp->f_flags & O_NONBLOCK) return -EAGAIN; else wait_event_interruptible(button_waitq, ev_press); } printk("read buttons fan"); ev_press = 0; err = copy_to_user((void *)buff, (const void *)(&key_values), min(sizeof(key_values), count)); return err ? -EFAULT : min(sizeof(key_values), count); } static unsigned int mini210_buttons_poll( struct file *file, struct poll_table_struct *wait) { unsigned int mask = 0; poll_wait(file, &button_waitq, wait); if (ev_press) mask |= POLLIN | POLLRDNORM; return mask; } static int mini210_buttons_fasync(int fd, struct file *filp, int on) { return fasync_helper(fd, filp, on, &button_fasync); } static struct file_operations dev_fops = { .owner = THIS_MODULE, .open = mini210_buttons_open, .release = mini210_buttons_close, .read = mini210_buttons_read, .poll = mini210_buttons_poll, .fasync = mini210_buttons_fasync, }; static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &dev_fops, }; static int __init button_dev_init(void) { int ret; ret = misc_register(&misc); printk(DEVICE_NAME"\tinitialized\n"); return ret; } static void __exit button_dev_exit(void) { misc_deregister(&misc); } module_init(button_dev_init); module_exit(button_dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("FriendlyARM Inc.");應用程序
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/select.h> #include <sys/time.h> #include <errno.h> #include <poll.h> #include <signal.h> int buttons_fd; char buttons[8] = {'0', '0', '0', '0', '0', '0', '0', '0'}; void mysignal_fun(int signum) { char current_buttons[8]; int count_of_changed_key; int i; int ret; if (read(buttons_fd, current_buttons, sizeof current_buttons) != sizeof current_buttons) { perror("read buttons:"); exit(1); } for (i = 0, count_of_changed_key = 0; i < sizeof buttons / sizeof buttons[0]; i++) { if (buttons[i] != current_buttons[i]) { buttons[i] = current_buttons[i]; printf("%skey %d is %s", count_of_changed_key? ", ": "", i+1, buttons[i] == '0' ? "up" : "down"); count_of_changed_key++; } } if (count_of_changed_key) { printf("\n"); } } int main(void) { int flag; signal(SIGIO,mysignal_fun); buttons_fd = open("/dev/buttons_poll", 0); if (buttons_fd < 0) { perror("open device buttons"); exit(1); } /* F_SETOWN: Set the process ID * 告訴內核,發給誰 */ fcntl(buttons_fd, F_SETOWN, getpid()); /* F_GETFL :Read the file status flags * 讀出當前文件的狀態 */ flag = fcntl(buttons_fd,F_GETFL); /* F_SETFL: Set the file status flags to the value specified by arg * int fcntl(int fd, int cmd, long arg); * 修改當前文件的狀態,添加異步通知功能 */ fcntl(buttons_fd,F_SETFL,flag | FASYNC); while(1) { /* 為了測試,主函數裡,什麼也不做 */ sleep(1000); } close(buttons_fd); return 0; }