回顾同时读键盘、鼠标的方法
①多进程 参考:高级IO——非阻塞IO
②多线程 参考:高级IO——非阻塞IO
③将“读鼠标”和“读键盘”设置为非阻塞 参考:高级IO——非阻塞IO
④多路IO(select、poll机制) 参考:高级IO——多路IO
⑤异步IO
③④都是单线的
异步IO的原理
①~④都是主动的去读,对于read函数来说它并不知道是不是一定有数据,如果有数据就读到数据,没有数据要么阻塞直到读到数据为止,要么就不阻塞。实际上除了以上描述的方式外,还有另外一种聪明的方式,那就是使用异步IO的方式来实现。异步IO的原理就是,底层把数据准备好后,内核就会给进程发送一个“异步通知的信号”通知进程,表示数据准备好了,然后调用信号处理函数去读数据,在没有准备好时,进程忙自己的事情。比如使用异步IO读鼠标,底层鼠标驱动把数据准备好后,会发一个“SIGIO”(异步通知信号)给进程,进程调用捕获函数读鼠标,读鼠标的SIGIO捕获函数需要我们自己定义。
举个例子:到吃饭时间了,你可以去饭店排队买饭,也可以提前给老板打电话,饭做好了叫我过去吃
使用异步IO方式读鼠标和键盘
进程正常阻塞读键盘,然后将读鼠标设置为异步IO方式。进程正常阻塞读键盘时,如果鼠标没有数据的话,进程不关心读鼠标的事情,如果鼠标数据来了,底层鼠标驱动就会向进程发送一个SIGIO信号,然后调用注册的SIGIO信号捕获函数读鼠标数据。当然也可以反过来,进程正常阻塞读鼠标,然后将读键盘设置为异步IO方式。
异步IO这个名字怎么理解?
答:比如以异步IO方式读鼠标数据为例,如果知道什么时间数据会来,等这个时间到时再去读数据,这就是步调统一的同步读。如果不知道什么时候会有数据来,这种就只能是什么时候数据来了就什么时候读,这种就是异步的读。之所叫异步,是因为我不知道你什么时候来,没办法统一步调(异步的),只能是随时来是随时读。
不过使用异步IO有两个前提:
(1)底层驱动必须要有相应的发送SIGIO信号的代码,只有这样当底层数据准备好后,底层才会发送SIGIO信号给进程。我们之所以可以对鼠标设置异步IO,是因为人家在实现鼠标驱动时,有写发送SIGIO信号的代码,如果驱动程序是我们自己写的,发送SIGIO的代码就需要我们自己来写。
(2)应用层必须进行相应的异步IO的设置,否者无法使用异步IO应用层进行异步IO设置时,使用的也是fcntl函数。
使用异步IO时,应用层的设置步骤
①调用signal函数对SIGIO信号设置捕获函数。在捕获函数里面实现读操作,比如读鼠标。
②使用fcntl函数,将接收SIGIO信号的进程设置为当前进程。如果不设置的,底层驱动并不知道将SIGIO信号发送给哪一个进程。
fcntl(mousefd, F_SETOWN, getpid());
③使用fcntl函数,对文件描述符增设O_ASYNC的状态标志,让fd支持异步IO
mouse_fd = open("/dev/input/mouse1", O_RDONLY); flag = fcntl(mouse_fd, F_GETFL); flag |= O_ASYNC; //补设O_ASYNC fcntl(mouse_fd, F_SETFL, flag);
代码演示
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <string.h> 5 #include <strings.h> 6 #include <errno.h> 7 #include <sys/time.h> 8 #include <sys/types.h> 9 #include <unistd.h> 10 #include <sys/types.h> 11 #include <sys/stat.h> 12 #include <fcntl.h> 13 #include <poll.h> 14 #include <signal.h> 15 16 void print_err(char *str, int line, int err_no) 17 { 18 printf("%d, %s: %s\n", line, str, strerror(err_no)); 19 exit(-1); 20 } 21 22 int mousefd = 0; 23 void signal_fun(int signo) 24 { 25 int buf; 26 int ret = 0; 27 28 if(SIGIO == signo) 29 { 30 bzero(&buf, sizeof(buf)); 31 ret = read(mousefd, &buf, sizeof(buf)); 32 if(ret > 0) printf("%d\n", buf); 33 } 34 } 35 36 int main(void) 37 { 38 int ret = 0; 39 char buf[100] = {0}; 40 struct pollfd fds[2]; 41 42 mousefd = open("/dev/input/mouse0", O_RDONLY); 43 if(mousefd == -1) print_err("open mouse0 fail", __LINE__, errno); 44 45 //为SIGIO设置捕获函数,在捕获函数里面读鼠标 46 signal(SIGIO, signal_fun); 47 48 //告诉鼠标驱动,他发送的SIGIO信号由当前进程接收 49 fcntl(mousefd, F_SETOWN, getpid()); 50 51 //对mousefd进行设置,让其支持异步IO 52 int flg = fcntl(mousefd, F_GETFL); 53 flg |= O_ASYNC; 54 fcntl(mousefd, F_SETFL, flg); 55 56 while(1) 57 { 58 bzero(buf, sizeof(buf)); 59 ret = read(0, buf, sizeof(buf)); 60 if(ret > 0) printf("%s\n", buf); 61 } 62 63 return 0; 64 }