linux异步IO

异步IO

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

  1. 多进程
  2. 多线程
  3. 将“读鼠标”和“读键盘”设置为非阻塞
  4. 多路IO(select、poll机制)
  5. 异步IO

异步IO的原理

前面四种方式都是主动去读,对于read函数来说它并不知道是不是一定有数据,如果有数据就读到数据,没有数据要么阻塞直到读到数据为止,要么不阻塞。

这就好比我想去澡堂洗澡,我不知道有没有位置,我去了后如果有位置我就立即洗澡(立即读数据),如果没有位置要么等(阻塞读),要么离开过段时间再来看(非阻塞读)。

实际上除了以上描述符的方式外,还有另外一种聪明的方式,那就是使用异步IO的方式来实现。

异步IO的原理就是,底层把数据准备好后,内核就会给进程发送一个“异步通知的信号”通知进程,表示数据准备好了,然后调用信号处理函数去读数据,在没有准备好时,进程忙自己的事情。

这就好比我跟澡堂老板说一声“有位置了打电话给我哈”,我就会去该干嘛就干嘛,等老板通知我了我就知道有位置了,这样的方式不就更好吗?

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

使用异步IO方式读鼠标读键盘

进程正常阻塞读键盘,然后将读鼠标设置为异步IO方式。

进程正常阻塞读键盘时,如果鼠标没有数据的话,进程不关心读鼠标的事情,如果鼠标数据来了,底层鼠标驱动会向

进程发送一个SIGIO信号,然后调用注册的SIGIO信号捕获函数读鼠标数据。

当然也可以反过来,进程正常阻塞读鼠标,然后将读键盘设置为异步IO方式。

异步IO这个名字怎么理解?

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

如果不知道什么时候会有数据来,这种就只能是什么时候数据来了就什么时候读,这种就是异步读。

之所以叫异步,是因为我不知道你什么时候来,没办法统一步调(异步的),只能是随时来随时读

使用异步IO要有两个前提

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

使用异步IO,应用层的设置步骤

  1. 调用signal函数对SIGIO信号设置捕获函数

    • 在捕获函数里面实现读操作,比如读鼠标。
  2. 使用fcntl函数,将接收SIGIO信号的进程设置为当前进程

    • 如果不设置,底层驱动并不知道将SIGIO信号发送给哪一个进程
    • fcntl(mousefd, F_SETOWN, getpid()); /* 将当前进程的进程号告诉给内核 */

    F_GETOWN

    获取当前在文件描述符 fd上接收到SIGIO 或 SIGURG事件信号的进程ID或进程组,arg忽略。

    F_SETOWN

    设置在文件描述符fd上接收SIGIO 或 SIGURG事件信号的进程ID或进程组ID,值为arg。

  3. 使用fcntl函数,对文件描述符增设O_ASYNC的状态标志,让fd支持异步IO

    • mousefd = open("/dev/input/mouse1", O_RDONLY);
    • flag = fcntl(mousefd, F_GETFL);
    • flag |= O_ASYNC; //补设O_ASYNC
    • fcntl(mousefd, F_SETFL, flag);/* 设置进程启用异步通知功能 */

代码演示:

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 *argv[])
{
	int ret = 0;
	char buf[100] = {0};
	struct pollfd fds[2];

	mousefd = open("/dev/input/mouse0", O_RDONLY);
	if (mousefd == -1) print_err("open /dev/input/mouse0 fail", __LINE__, errno);
	/* 为SIGIO设置捕获函数,在捕获函数里面读鼠标 */
	signal(SIGIO, signal_fun);

	/* 告诉鼠标驱动,它发送的SIGIO信号由当前进程接收 */
	fcntl(mousefd, F_SETOWN, getpid());

	/* 对mousefd进行设置让其支持异步IO */
	int flag = fcntl(mousefd, F_GETFL);
	flag |= O_ASYNC;
	fcntl(mousefd, F_SETFL, flag);

	while(1)
	{
		bzero(buf, sizeof(buf));
		ret = read(0, buf, sizeof(buf));
		if (ret > 0) printf("%s\n", buf);
	}
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值