|
在I/O多路复用中,进程通过系统调用select或poll来主动查询文件描述符上是否可以执行I/O操作。而在异步I/O中,当文件描述符上可以执行I/O操作时,进程可以请求内核为自己发送一个信号。之后进程就可以执行任何其它的任务直到文件描述符可以执行I/O操作为止,此时内核会发送信号给进程。 异步I/O通常也称为信号驱动I/O。 要使用异步I/O,程序需要按照如下步骤来执行: 1.通过指定O_NONBLOCK标志使能非阻塞I/O。 2.通过指定O_ASYNC标志使能异步I/O。 3.设置异步I/O事件的接收进程。也就是当文件描述符上可执行I/O操作时会发送信号通知该进程,通常将调用进程设置为异步I/O事件的接收进程。 4.为内核发送的通知信号注册一个信号处理函数。默认情况下,异步I/O的通知信号是 SIGIO,所以内核会给进程发送信号SIGIO。 5.之后,进程就可以执行其它任务了,当I/O操作就绪时,内核会向进程发送一个SIGIO信号,当进程接收到信号时,会执行预先注册好的信号处理函数,就可以在信号处理函数中进行I/O操作。 需要注意的是,在调用open时无法通过指定O_ASYNC标志来使能异步I/O,但可以使用fcntl函数配置对应的文件描述符。 添加O_ASYNC标志使能异步I/O: int flag; flag = fcntl(0, F_GETFL); //先获取原来的 flag flag |= O_ASYNC; //将 O_ASYNC 标志添加到 flag fcntl(fd, F_SETFL, flag); //重新设置 flag 为文件描述符设置异步I/O事件的接收进程,也就是设置异步I/O的所有者。同样也是通过fcntl函数进行设置,操作命令cmd设置为F_SETOWN,第三个参数传入接收进程的进程ID(PID),通常将调用进程的PID传入: fcntl(fd, F_SETOWN, getpid()); 对于信号处理,可以使用signal,用于设置信号处理方式最简单的接口,可将信号的处理方式设置为捕获信号、忽略信号以及系统默认操作。 1.头文件 #include 2.函数原型 typedef void (*sig_t)(int); sig_t signal(int signum, sig_t handler); 3.参数 signum:表示指定需要进行设置的信号,可使用信号名(宏)或信号的数字编号。 handler:sig_t类型的函数指针,指向信号对应的信号处理函数,当进程接收到信号后会自动执行该处理函数;参数handler既可以设置为用户自定义的函数,也就是捕获信号时需要执行的处理函数,也可以设置为SIG_IGN或SIG_DFL,SIG_IGN表示此进程需要忽略该信号,SIG_DFL 则表示设置为系统默认操作。sig_t函数指针的int类型参数指的是,当前触发该函数的信号,可将多个信号绑定到同一个信号处理函数上,此时就可通过此参数来判断当前触发的是哪个信号。 SIG_IGN、SIG_DFL分别取值如下: #define SIG_ERR ((sig_t) -1) /* Error return. */ #define SIG_DFL ((sig_t) 0) /* Default action. */ #define SIG_IGN ((sig_t) 1) /* Ignore signal. */。 通过signal函数为SIGIO信号注册一个信号处理函数,当进程接收到内核发送过来的SIGIO信号时,会执行该处理函数,所以应该在处理函数当中执行相应的I/O操作。 4.返回值 此函数的返回值也是一个sig_t类型的函数指针,成功情况下的返回值则是指向在此之前的信号处理函数;如果出错则返回SIG_ERR,并会设置errno。 5.示例:(异步I/O方式读取鼠标数据,进程接收到SIGIO信号后读取鼠标数据) #include #include #include #include #include #include #include static int fd; static int num = 0; static void sigio_handler(int sig) { char buf[100]; int ret; num++; if (sig != SIGIO) //判断获取的信号是否是SIGIO return; ret = read(fd, buf, sizeof(buf)); if (ret > 0) printf("event2 num:%d\n", ret); if (num == 10) { //读取次数到10时结束 close(fd); exit(0); } } int main() { int flag; fd = open("/dev/input/event2", O_RDONLY | O_NONBLOCK); if (fd < 0) { perror("event2 open error"); return -1; } flag = fcntl(fd,F_GETFL); //使能异步I/O flag = O_ASYNC; fcntl(fd,F_SETFL,flag); fcntl(fd,F_SETOWN,getpid()); //设置异步I/O的所有者 signal(SIGIO,sigio_handler); //注册SIGIO信号的处理函数 while(1); sleep(1); return 0; } 6)编译运行并查看测试结果 event2 num:48 event2 num:72 event2 num:72 event2 num:48 event2 num:72 event2 num:48 event2 num:48 event2 num:48 event2 num:48 event2 num:48
|