异步io

异步io
1.1 同时读键盘、鼠标的方法有
1)多进程
2)多线程
3)将“读鼠标”和“读键盘”设置为非阻塞
4)多路IO(select、poll机制)
5)异步IO

1.2 异步IO的原理
  前面四种方式都是主动的去读,对于read函数来说它并不知道是不是一定有数据,如果有数据就读到数据,没有数据要么阻塞直到读到数据为止,要么就不阻塞。
  实际上除了以上描述的方式外,还有另外一种聪明的方式,那就是使用异步IO的方式来实现。
  异步IO的原理就是,底层把数据准备好后,内核就会给进程发送一个“异步通知的信号”通知进程,表示数据准备好了,然后调用信号处理函数去读数据,在没有准备好时,进程忙自己的事情

  比如使用异步IO读鼠标,底层鼠标驱动把数据准备好后,会发一个“SIGIO”(异步通知信号)给进程,进程调用捕获函数读鼠标,读鼠标的SIGIO捕获函数需要我们自己定义。

1.3 使用异步IO方式读鼠标和键盘
  进程正常阻塞读键盘,然后将读鼠标设置为异步IO方式。
  进程正常阻塞读键盘时,如果鼠标没有数据的话,进程不关心读鼠标的事情,如果鼠标数据来了,底层鼠标驱
  动就会向进程发送一个SIGIO信号,然后调用注册的SIGIO信号捕获函数读鼠标数据。
  当然也可以反过来,进程正常阻塞读鼠标,然后将读键盘设置为异步IO方式。

异步IO这个名字怎么理解?
比如以异步IO方式读鼠标数据为例,如果知道什么时间数据会来,等这个时间到时再去读数据,这就是步调统一的同步读。

如果不知道什么时候会有数据来,这种就只能是什么时候数据来了就什么时候读,这种就是异步的读。
之所叫异步,是因为我不知道你什么时候来,没办法统一步调(异步的),只能是随时来是随时读

不过使用异步IO有两个前提,
(1)底层驱动必须要有相应的发送SIGIO信号的代码,只有这样当底层数据准备好后,底层才会发送SIGIO信号给进程。
我们之所以可以对鼠标设置异步IO,是因为人家在实现鼠标驱动时,有写发送SIGIO信号的代码,
如果驱动程序是我们自己写的,发送SIGIO的代码就需要我们自己来写。
(2)应用层必须进行相应的异步IO的设置,否者无法使用异步IO 应用层进行异步IO设置时,使用的也是fcntl函数

使用异步IO时,应用层的设置步骤
(1)调用signal函数对SIGIO信号设置捕获函数在捕获函数里面实现读操作,比如读鼠标
(2)使用fcntl函数,将接收SIGIO信号的进程设置为当前进程
  如果不设置的,底层驱动并不知道将SIGIO信号发送给哪一个进程
  fcntl(mousefd, F_SETOWN, getpid());
(3)使用fcntl函数,对文件描述符增设O_ASYNC的状态标志,让fd支持异步IO
  mousefd = open("/dev/input/mouse1", O_RDONLY);
  flag = fcntl(mouse_fd, F_GETFL);
  flag |= O_ASYNC; //补设O_ASYNC
  fcntl(mouse_fd, F_SETFL, flag);
代码async.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> 
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>

int mousefd=0;
void print_err(char *str,int line,int err_no){
    printf("%d,%s:%s\n",line,str,strerror(err_no) );
    exit(-1);
}

void signal_fun(int signo){
    int buf;
    int ret=0;
    if(SIGIO == signo){
        bzero(&buf,sizeof(buf));
        ret=read(mousefd,&buf,sizeof(buf));
        if(ret>0) printf("%d\n",buf );
    }
}

int main(int argc, char const *argv[])
{

    char buf[100]={0};
    int ret= 0;

    mousefd =open("/dev/input/mouse1",O_RDONLY); //可能Mouse0或2
    if(mousefd == -1) print_err("open mouse0 fail",__LINE__,errno);
    //设置捕获函数
    signal(SIGIO,signal_fun);
    //告诉鼠标驱动,他发送的sigio信号由当前进程接收
    fcntl(mousefd,F_SETOWN,getpid());
    //
    int flg=fcntl(mousefd,F_GETFL);
    flg|=O_ASYNC;
    fcntl(mousefd,F_SETFL,flg);


    while(1){
        bzero(buf,sizeof(buf));
        ret= read(0,buf,sizeof(buf));
        if(ret>0) printf("%s\n",buf );

    }
    return 0;
}