异步I/O(Asynchronous I/O):比如你在美团外卖订了个盒饭,付款之后你自己该干嘛干嘛,饭送到时骑手自然会打电话通知你。异步I/O中数据到达缓冲区后,不需要由调用进程主动进行从缓冲区复制数据的操作,而是复制完成后由操作系统向线程发送信号,所以它一定是非阻塞的。
同步I/O(Synchronous I/O):比如你自己去饭堂打饭,这时可能有如下情形发生。
阻塞I/O(Blocking I/O):你去饭堂打饭,发现饭还没做好,只能等待(线程休眠),直到饭做好,这就是被阻塞了。阻塞I/O是最直观的I/O模型,逻辑清晰,也比较节省CPU资源,但缺点是线程休眠所带来的上下文切换,这是一种需要切换到内核态的重负载操作,不应当频繁进行。
非阻塞I/O(Non-Blocking I/O):你去饭堂,发现饭还没做好,你就回去了,然后每隔3分钟来一次饭堂看饭是否做好,一直重复,直到饭做好。非阻塞I/O能够避免线程休眠,对于一些很快就能返回结果的请求,非阻塞I/O可以节省切换上下文切换的消耗,但是对于较长时间才能返回的请求,非阻塞I/O反而白白浪费了CPU资源,所以目前并不太常用。
多路复用I/O(Multiplexing I/O):多路复用I/O本质上是阻塞I/O的一种,但是它的好处是可以在同一条阻塞线程上处理多个不同端口的监听。仍以去食堂打饭为例,比如你代表整个宿舍去饭堂打饭,去到饭堂,发现饭还没做好,还是继续等待,其中某个舍友的饭好了,你就马上把那份饭送回去,然后继续等待其他舍友的饭做好。多路复用I/O是目前高并发网络应用的主流,它还可以细分为select、epoll、kqueue等不同实现,这里就不再展开了。
信号驱动I/O(Signal-Driven I/O):你去到饭堂,发现饭还没做好,但你跟厨师很熟,跟他说饭做好了叫你,然后你就回去了,等收到厨师通知后,你再去饭堂把饭拿回宿舍。这里厨师的通知就是那个“信号”,信号驱动I/O与异步I/O的区别是“从缓冲区获取数据”这个步骤的处理,前者收到的通知是可以开始进行复制操作了,即要你自己从饭堂拿回宿舍,在复制完成之前线程处于阻塞状态,所以它仍属于同步I/O操作,而后者收到的通知是复制操作已经完成,即外卖小哥已经把饭送到了。