高级IO——异步IO

时间:2023-01-21 17:57:56

回顾同时读键盘、鼠标的方法

①多进程                                                参考:高级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

高级IO——异步IO高级IO——异步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);
View Code

代码演示

高级IO——异步IO高级IO——异步IO
 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 }
View Code