前言

看过很多资料,很多对I/O模型概念模糊甚至错误,希望这篇文章有助理解I/O,欢迎讨论和纠正

参考资料:《UNIX网络编程卷1》  P122

I/O中涉及概念

介绍阻塞非阻塞,同步异步之前,先分析一下I/O请求过程

  1. 等待数据准备好
  2. 从内核向进程复制数据

过程1决定是否阻塞,过程2决定是否同步

误区1:不要以为是否阻塞和是否同步会排列组合成4中状态,实际上只有三种:同步阻塞,同步非阻塞,异步非阻塞(一般来说异步非阻塞就成为异步,都异步了还阻塞啥?很多资料竟然还扯异步阻塞)

那么究竟什么是同步异步,阻塞非阻塞?

同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)

所谓同步,就是在发出一个*调用*时,在没有得到结果之前,该*调用*就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由*调用者*主动等待这个*调用*的结果。

而异步则是相反,*调用*在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在*调用*发出后,*被调用者*通过状态、通知来通知调用者,或通过回调函数处理这个调用。

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态

阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。

非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

总结

同步和异步说的是消息通知机制:同步的情况下,是由处理消息者自己去等待消息是否被触发,而异步的情况下是由触发机制来通知处理消息者

阻塞非阻塞说的是消息处理机制:

Unix可用的5中I/O模型:

  • 阻塞式I/O
  • 非阻塞式I/O
  • I/O复用(select和poll)
  • 信号驱动式I/O(SIGIO)
  • 异步I/O(POSIX的aio_系列函数)

生活中的例子

我经常和同学去吃庆丰包子,从点餐开始就出现了I/O模型

我:应用程序

前台:内核

点餐:I/O请求

阻塞式I/O:我去前台点餐(I/O请求),点玩后站那毛都不干(等待数据,进程sleep),于此同时前台让后厨准备包子,后厨做好后给前台(数据准备好),我端着盘子去找位置坐下(复制数据)

非阻塞式I/O:我去前台点餐(I/O请求),点玩后站在那里无聊,就和朋友打电话(等待数据但进程没有sleep),但眼睛还是时不时扫一眼看看包子好没好(轮询内核),于此同时前台让后厨准备包子,后厨做好后给前台(数据准备好),我端着盘子去找位置坐下(复制数据)

I/O复用(select和poll):班里组织吃包子,4人一桌,班长说各位落座,我自己在前台等着通知大家,班长记录了一个纸条,上面是对应的桌子和点的餐(描述符集合),他在前台等着谁的包子做好了就叫谁,被叫到的同学端着盘子回到座位(复制数据)

信号驱动式I/O:事实上这种才是庆丰包子铺的做法,我我去前台点餐(I/O请求),服务员给我一个号码,然后我落座跟同学吹牛逼,一会服务员叫到38号你的包子好了(信号),我就屁颠屁颠跑到前台把包子端回来(复制数据)

异步I/O:(POSIX的aio_系列函数):由于常去庆丰吃包子,我成为了VIP用户。这天我去前台点餐(I/O请求),然后我落座跟同学吹牛逼,吹到一半包子好了,但是服务员也没叫我(事实上我都不知道包子好了这件事),我还在继续吹牛逼,服务员这时把包子端上来了(复制数据由内核完成了)。这实际上是饭点常用的做法

总结:等待包子做好的过程是等待数据,等待过程中你是挂起还是搞基(是否sleep)决定了是否阻塞;包子准备好了,是你自己去取还是让服务员给你拿来决定了是否是异步,事实上如果你选择让服务员给你拿来(异步),也就没必要看着盯着前台看看包子是否做好了,因为你不用关心是否做好了(因为好了后服务员给你拿过来),从这个角度去思考就知道为什么没有异步阻塞了

陈硕在知乎上的回答也印证了这一点:

刨根问底拦不住——I/O模型-LMLPHP

阻塞式I/O

刨根问底拦不住——I/O模型-LMLPHP

默认情况下,所有套接字都是阻塞的。从调用recvfrom开始到它返回的整段时间内是被阻塞的。recvfrom成功返回后,应用进程开始处理数据报

非阻塞式I/O

刨根问底拦不住——I/O模型-LMLPHP

I/O复用(select和poll)

刨根问底拦不住——I/O模型-LMLPHP

信号驱动式I/O(SIGIO)

刨根问底拦不住——I/O模型-LMLPHP

这种模型的优势在于等待数据报到达期间进程不被阻塞,主循环可以继续执行

异步I/O

刨根问底拦不住——I/O模型-LMLPHP

信号驱动式I/O是由内核通知我们何时可以启动一个I/O操作,而异步I/O模型是由内核通知我们I/O操作何时完成

I/O模型比较

同步I/O操作:导致请求进程阻塞,直到I/O操作完成

异步I/O操作:不导致请求进程阻塞

刨根问底拦不住——I/O模型-LMLPHP

前四种模型的主要区别在第一阶段,因为它们第二阶段是一样的:在数据从内核复制到调用者的缓冲区期间,进程阻塞于recvfrom调用

异步I/O模型在这两个阶段都要处理,前四种都是同步,因为其中真正的I/O操作(recvfrom)将阻塞进程。只有异步I/O模型与POSIX定义的异步I/O相匹配

其它好的博客;

也谈同步异步I/O

select、poll、epoll之间的区别总结

Linux IO模式及 select、poll、epoll详解

可能是最接地气的 I/O 多路复用小结

Linux IO模式及 select、poll、epoll详解

04-15 04:40