linux异步通知
简述
linux下异步方式有两种:异步通知和异步IO(AIO),aio请参考:linux异步IO--aio
异步通知的含义是:一旦设备就绪,则主动通知应用程序,这样应用程序就不需要查询设备状态,准确称谓是“信号驱动的异步I/O”。
Linux的I/O机制经历了一下几个阶段的演进:
1. 同步阻塞I/O: 用户进程进行I/O操作,一直阻塞到I/O操作完成为止。
2. 同步非阻塞I/O: 用户程序可以通过设置文件描述符的属性O_NONBLOCK,I/O操作可以立即返回,但是并不保证I/O操作成功。
3. 异步事件阻塞I/O: 用户进程可以对I/O事件进行阻塞,但是I/O操作并不阻塞。通过select/poll/epoll等函数调用来达到此目的。
4. 异步事件非阻塞I/O: 也叫做异步I/O(AIO),用户程序可以通过向内核发出I/O请求命令,不用等待I/O事件真正发生,可以继续做另外的事情,等I/O操作完成,内核会通过函数回调或者信号机制通知用户进程。这样很大程度提高了系统吞吐量。
阻塞IO意味着一直等待设备可访问后再访问,非阻塞IO中使用poll()意味着查询设备是否可访问,而异步通知则意味着设备通知自身可访问,实现了异步IO。
实现
信号定义在:arch/arm/include/uapi/asm/signal.h
异步通知在linux下实现分为两部分:应用层和内核驱动层。
应用层主要处理signal部分(signal编程),处理步骤如下:
1. 通过F_SETOWN IO控制命令(fcntl(fd, F_SETOWN, getpid()))设置设备文件的拥有者为本进程,这样从设备驱动发出的信号才能被本进程接收到。将本应用程序的进程号告诉内核。
2. 通过F_SETFL IO控制命令(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC))设置文件支持FASYNC,即异步通知模式。开启异步通知。
3. 通过signal()函数连接信号和信号处理函数。
内核驱动层处理步骤:
1. 支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。内核已完成。
2. 支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将执行。
3. 在设备资源可获得时,调用kill_fasync()函数激发相应的信号。
设备驱动中异步通知编程涉及一项数据结构和两个函数,数据结构是fasync_struct,两个函数分别是:
处理FASYNC标志变更的:
int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **fa);
释放信号函数:
void kill_fasync(struct fasync_struct **fa, int sig, int band);
sig为SIGIO,band可读时POLL_IN,可写时POLL_OUT。
struct fasync_struct { spinlock_t fa_lock; int magic; int fa_fd; struct fasync_struct *fa_next; struct file *fa_file; struct rcu_head fa_rcu; };
和其他设备驱动一样,将fasync_struct结构体指针放在设备结构体中仍然是最佳选择。
要使用异步通知,需要实现fasync函数:
int (*fasync) (int fd, struct file *filp, int on)
fasync 函数里面一般通过调用 fasync_helper 函数来初始化fasync_struct 结构体指针。当应用程序通过“fcntl(fd, F_SETFL, flags | FASYNC)”改变fasync 标记的时候,驱动程序file_operations 操作集中的fasync函数就会执行。
在关闭驱动文件的时候需要在 file_operations 操作集中的 release 函数中释放 fasync_struct,fasync_struct 的释放函数同样为 fasync_helper。
struct fasync_struct *async_queue; // 中断中发送信号 if(atomic_read(&dev->releasekey)){ if(dev->async_queue){ kill_fasync(&dev->async_queue, SIGIO, POLLIN); } } static int imx6uirq_fasync(int fd, struct file *filp, int on) { struct imx6uirq_dev *dev = (struct imx6uirq_dev*)filp->private_data; return fasync_helper(fd, filp, on, &dev->async_queue); } static int imx6uirq_release(struct inode *inode, struct file *filp) { return imx6uirq_fasync(-1, filp, 0); } static struct file_operations chrtest_fops = { .owner = THIS_MODULE, .open = imx6uirq_open, .read = imx6uirq_read, .poll = imx6uirq_poll, .fasync = imx6uirq_fasync, .release = imx6uirq_release, };
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h> #include <linux/ioctl.h> #include <stdbool.h> #include <signal.h> static int fd = 0; static void sigio_handler(int signum) { int err = 0; unsigned int keyvalue = 0; err = read(fd, &keyvalue, sizeof(keyvalue)); if(err < 0){ } else { printf("sigio signal! key value=%d\n", keyvalue); } } int main(int argc, char **argv) { char *filename = NULL; if(argc != 2){ printf("Usage: %s filename ", argv[0]); exit(-1); } filename = argv[1]; fd = open(filename, O_RDWR); if(fd < 0){ printf("Can't open file: %s\n", filename); return -1; } signal(SIGIO, sigio_handler); fcntl(fd, F_SETOWN, getpid()); fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC); while(1){ sleep(2); } close(fd); return 0; }
参考:
1. Linux 异步IO机制