linux应用编程中的异步IO

前面有提到IO的多路复用,解决了阻塞式访问的困境。这里再介绍一种解决方法——异步IO异步

一、异步IO的实现原理很想硬件上的中断。异步IO就是操做系统用软件实现的一套中断响应系统,工做方法是当前进程注册一个异步IO事件(使用signal注册一个信号SIGIO的处理函数),而后当前进程能够正常处理本身的事情,当异步事件发生后当前进程会收到一个SIGIO信号从而执行绑定的处理函数去处理这个异步事件。函数

涉及的函数:spa

  • fcntl函数
所需头文件 #include <unistd.h>
#include <fcntl.h>
函数原型 int fcntl(int fd, int cmd, ... /* arg */ );
函数传入值 fd:文件描述符
cmd:命令 复制一个现有的文件描述符:F_DUPFD
获取或设置文件描述符标记:F_GETFD、F_SETFD
获取或设置文件状态标记:F_GETFL、F_SETFL
获取或设置锁:F_SETLK、F_SETLKW、F_GETLK
获取或设置异步IO全部权:F_GETOWN、F_SETOWN、F_GETSIG、F_SETSIG
lease、File and directory change notification等等,具体查看man手册
...:可变参数
函数返回值 成功:>=0,根据cmd的取值返回不一样的含义值
失败:返回-1 

实现异步IO使用到的cmd是F_GETFL、F_SETFL、F_SETOWN、O_ASYNC操作系统

  • signal函数(或者使用sigaction)
所需头文件 #include <signal.h>
函数原型

typedef void (*sighandler_t)(int);指针

sighandler_t signal(int signum, sighandler_t handler);code

函数传入值 signum:指明了所要处理的信号类型,它能够取除了SIGKILL和SIGSTOP外的任何一种信号
handler:描述了与信号关联的动做,它能够取三种值:  SIG_IGN:这个符号表示忽略该信号
SIG_DFL:这个符号表示恢复对信号的系统默认处理。不写此处理函数默认也是执行系统默认操做
func(返回值类型为sighandler_t的函数名):接收到信号后,执行指定的处理方式
函数返回值

成功:返回先前的信号处理函数指针进程

失败:返回SIG_ERR(-1)

func函数必须在signal()被调用前申明,handler中为这个函数的名字。当接收到一个类型为sig的信号时,就执行handler 所指定的函数。(int)signum是传递给它的惟一参数。执行了signal()调用后,进程只要接收到类型为sig的信号,无论其正在执行程序的哪一部分,就当即执行func()函数。当func()函数执行结束后,控制权返回进程被中断的那一点继续执行。事件

signal()会依参数signum 指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行。当一个信号的信号处理函数执行时,若是进程又接收到了该信号,该信号会自动被储存而不会中断信号处理函数的执行,直到信号处理函数执行完毕再从新调用相应的处理函数。可是若是在信号处理函数执行时进程收到了其它类型的信号,该函数的执行就会被中断。ci

二、这里依然用鼠标键盘的例子进行说明get

#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);
	flag |= O_ASYNC;
	fcntl(mousefd, F_SETFL, flag);
	// 把异步IO事件的接收进程设置为当前进程
	fcntl(mousefd, F_SETOWN, getpid());
	
	// 注册当前进程的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;
}