1.什么是异步IO
几乎可以认为:异步IO就是操作系统用软件实现的一套中断响应系统。
我们当前进程注册一个异步IO事件(使用signal注册一个信号SIGIO的处理函数),然后当前进程可以正常处理自己的事情,当异步事件发生后当前进程会收到一个SIGIO信号从而执行绑定的处理函数去处理这个异步事件。
2.涉及的函数
(1)fcntl(F_GETFL、F_SETFL、O_ASYNC、F_SETOWN)
(2)signal或者sigaction(SIGIO)
fcntl函数的功能()
#include<unistd.h>
#include<fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd ,struct flock* lock);
int fcntl(int fd, int cmd, ... /* arg */ );
1.获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
(1)把鼠标的文件描述符设置为可以接受异步IO,还可以设置成很多其他属性,详细看man手册,所以操作都与下面代码类似
flag = fcntl(mousefd,F_GETFL); //F_GETFL取得fd的文件状态标志
flag |= O_ASYNC;
fcntl(mousefd , F_SETFL,flag) ; //F_SETFL设置给arg描述符状态标志
(2)fcntl的文件状态标志总共有7个:前三个属性在open一个文件时可以选择,fcntl不能更改
O_RDONLY , O_WRONLY , O_RDWR , O_APPEND , O_NONBLOCK , O_SYNC和O_ASYNC
(3)可更改的几个标志如下面的描述:
O_NONBLOCK 非阻塞I/O,如果read(2)调用没有可读取的数据,或者如果write(2)操作将阻塞,则read或write调用将返回-1和EAGAIN错误
O_APPEND 强制每次写(write)操作都添加在文件大的末尾,相当于open(2)的O_APPEND标志
O_DIRECT 最小化或去掉reading和writing的缓存影响。系统将企图避免缓存你的读或写的数据。如果不能够避免缓存,那么它将最小化已经被缓存了的数据造成的影响。如果这个标志用的不够好,将大大的降低性能
O_ASYNC 当I/O可用的时候,允许SIGIO信号发送到进程组,例如:当有数据可以读的时候
2. 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
什么是异步通知呢?首先这个机制是通过linux内核实现的,所以我们需要将一个打开的文件进行异步IO处理,需要这个文件设备驱动本身是支持异步IO机制的,只有驱动实现了这套处理机制,内核才能操作。比如鼠标,键盘,等等输入设备都是支持该机制.
异步通知的意思就是,一旦设备就绪,则主动通知应用程序,应用程序 根本就不需要查询设备状态,类似于中断的概念,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来 等待信号的到达。下面我们就看一下在linux中机制的实现方式。
在linux中,异步通知是使用信号来实现的,而在linux,大概有30种信号,比如大家熟悉的ctrl+c的SIGINT信号,进程能够忽略或者捕获除过SIGSTOP和SIGKILL的全部信号,当信号背捕获以后,有相应的函数来处理它。
(1)F_GETOWN 取得当前正在接收SIGIO或者SIGURG信号的进程id或进程组id,进程组id返回的是负值(arg被忽略)
(2)F_SETOWN 设置将接收SIGIO和SIGURG信号的进程id或进程组id,进程组id通过提供负值的arg来说明(arg绝对值的一个进程组ID),否则arg将被认为是进程id
(3)把异步IO事件的接收进程设置为当前进程
fcntl(mousefd,F_SETOWN,getpid());
(4)一旦鼠标发送IO事件,使用signal信号捕获,并执行服务函数
signal(SIGIO,func),func函数是我们自己定义编写的功能函数,只是通过signal函数进行绑定,一旦信号发送,捕获并执行func函数。
编写以下代码,就能同时读取键盘和鼠标
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
int mousefd = -1;
// 绑定到SIGIO信号,在函数内处理异步通知事件
void func(int sig)
{
char buf[200] = {0};
if (sig != SIGIO)
return;
read(mousefd, buf, 50);
printf("鼠标读出的内容是:[%s].\n", buf);
}
int main(void)
{
// 读取鼠标
char buf[200];
int flag = -1;
mousefd = open("/dev/input/mouse1", O_RDONLY);
if (mousefd < 0)
{
perror("open:");
return -1;
}
// 把鼠标的文件描述符设置为可以接受异步IO
flag = fcntl(mousefd, F_GETFL); //F_GETFL取得fd的文件状态标志
flag |= O_ASYNC; //意思是启用异步通知
fcntl(mousefd, F_SETFL, flag);
//上两句可以合并成:fcntl(mousefd, F_SETFL, flag|O_ASYNC); 意思也是启用异步通知
// 把异步IO事件的接收进程设置为当前进程
fcntl(mousefd, F_SETOWN, getpid());//设置一个用来接受SIGIO信号的进程,后面写这个进程的ID
// 注册当前进程的SIGIO信号捕获函数
signal(SIGIO, func);
// 读键盘
while (1)
{
memset(buf, 0, sizeof(buf));
//printf("before 键盘 read.\n");
read(0, buf, 5);
printf("键盘读出的内容是:[%s].\n", buf);
}
return 0;
}