1、异步通知概念: 一旦设备就绪则主动通知应用程序,比较准确的称谓是“信号驱动的异步I/O”.信号是软件层次上对中断机制的一种模拟。信号时一步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号什么时候到达。 2、信号操作的关键: (1)、注册一个信号处理函数; (2)、谁发送此信号; (3)、把信号发给谁; (4)、怎么发该信号。 3、注册信号处理函数 void (*signal(int signum, void (*handler) (int) ) ) (int) 分析此函数: (1)void (*handler) (int) 这是一个函数指针,指向的函数返回值为void,只有一个参数int (2)signal(int signum, void (*handler) (int) )) 这是一个函数,有两个参数, 一个是int型,另一个是函数指针, 该函数指针指向的函数返回值为void,只有一个参数int; (3)void (*signal(int signum, void (*handler) (int) ) ) (int),这样就可以看成是signal(int signum, void (*handler) (int) )) 函数执行之后,它的返回值是一个函数指针,这个函数指针指向参数为int型,返回值为void的函数。 (4)void (*signal(int signum, void (*handler) (int) ) ) (int) 使用typedef进行简化: typedef void (*HANDLER)(int); HANDLER signal(int,HANDLER); 使用例子:signal(SIGIO, my_signal_fun);这是一个注册或说设置对应信号的处理函数,SIGIO这是一个IO信号,my_signal_fun是要注册的处理信号。信号处理函数在应用程序中完成,此操作表示当应用程序接收到SIGIO信号时调用my_signal_fun函数进行处理,即在该进程内把信号和函数绑定。 4、驱动程序发送信号 以按键中断为例,当中断发生时,中断处理程序调用向应用程序发送信号。调用下面函数: kill_fasync (&button_async, SIGIO, POLL_IN); button_async的定义: static struct fasync_struct *button_async; struct fasync_struct { int magic; int fa_fd; struct fasync_struct *fa_next; struct file *fa_file; }; 对于struct fasync_struct这个数据结构在编写驱动时并不需要特别关注,它会由内核来维护,驱动程序中调用它即可。 button_async该结构体变量用来区分方向,谁发送信号,由结构体中的int fa_fd变量确定;发送SIGIO信号,POLL_IN表示该信号可读。 5、发信号发给应用程序 int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp) {struct fasync_struct *fa, **fp; struct fasync_struct *new = NULL; int result = 0; if (on) { new = kmem_cache_alloc(fasync_cache, GFP_KERNEL); if (!new) return -ENOMEM; } write_lock_irq(&fasync_lock); for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) { if (fa->fa_file == filp) { if(on) { fa->fa_fd = fd; //区分向谁发 kmem_cache_free(fasync_cache, new); } else { *fp = fa->fa_next; kmem_cache_free(fasync_cache, fa); result = 1; } goto out; } } if (on) { new->magic = FASYNC_MAGIC; new->fa_file = filp; new->fa_fd = fd; new->fa_next = *fapp; *fapp = new; result = 1; } out: write_unlock_irq(&fasync_lock); return result; } fcntl(fd, F_SETOWN, getpid()); fd为打开的该设备文件 F_SETOWN设置设备文件的拥有者为本进程,这样从驱动发出的信号才能被本进程接收。 getpid()获得本进程的PID Oflags = fcntl(fd, F_GETFL); 获得FASYNC标志位 fcntl(fd, F_SETFL, Oflags | FASYNC); 更改FASYNC标志位调用fasync_helper函数启动异步通知机制。 6、总结 驱动程序 static int fifth_drv_fasync (int fd, struct file *filp, int on) { printk("driver: fifth_drv_fasync\n"); return fasync_helper (fd, filp, on, &button_async); } static struct file_operations sencod_drv_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = fifth_drv_open, .read = fifth_drv_read, .release = fifth_drv_close, .poll = fifth_drv_poll, .fasync = fifth_drv_fasync, }; 1、编写.fasync函数,给启动异步通知机制时调用 2、中断发生时调用发送信号函数kill_fasync (&button_async, SIGIO, POLL_IN); 应用程序 1、signal(SIGIO, my_signal_fun); //绑定信号与处理函数 2、fcntl(fd, F_SETOWN, getpid()); //告诉驱动该进程号,并设置设备文件只属于该进程 Oflags = fcntl(fd, F_GETFL); //读取FASYNC标志位 fcntl(fd, F_SETFL, Oflags | FASYNC); //更改标志位启动异步通知机制 框图: |
|
来自: nt_bookworm > 《驱动》