IO模型总结
我们常说的IO模型主要是从Unix上拓展而来,所以提到IO模型一般都只是Unix下的IO模型,Unix下有5种可用的IO模型:
- 阻塞式IO
- 非阻塞式IO
- IO复用
- 信号驱动IO
- 异步IO
除了【异步IO】以外,其他全部是同步IO,下面我们就每个概念详细说一下。
1、阻塞式IO
recvfrom 是系统调用,除非有数据返回或者发生错误才会返回,最常见错误就是信号中断,进程在调用recvfrom一直到它返回数据这段时间都是被阻塞的。
2、非阻塞IO模型
进程把一个套接字设置成非阻塞是在通知内核:当所有请求IO操作必须要把本进程投入睡眠才能完成的时候,不要把本进程投入睡眠,而是返回一个错误。
当一个应用进程对一个非阻塞进程进行循环调用的recvfrom的时候,我们称之为轮询(polling),这样的模型往往会消耗大量的cpu。
3、I/O复用模型
IO多路复用指的是单个线程通过追踪管理每个IO流的状态来同时管理多个IO流。
select poll epoll是IO多路复用的多个具体实现,之所以有三个实现是有先后顺序的。
select
- select是最初实现的IO多路复用,select主要有以下问题
- select会修改传入的参数数组,这个对一个会调用很多次的函数很不友好。
- select中如果一个socketIO出现了数据,那么select会返回但是不会告诉你是那个socket上有数据,需要你自己去轮询,如果数量大,开销也会变大。
- select只能监视1024个链接,这个跟linux系统参数有关系。
- select不是线程安全的,例如一个socket被加入select,另外一个线程又要关闭socket,那么select是不支持的,会发生无法预知的错误。
poll
poll修复了select大部分问题,例如不再修改入参数组,去除了1024个链接的限制,但是poll仍然不是线程安全的,这会导致你还是只能在一个线程里处理一组io,而不是多个线程来处理一组IO。
epoll
epoll是IO复用的最新实现,epoll修复了poll和select的绝大部分问题,并且提升了非常高的效率,例如:
- epoll是线程安全的
- epoll不仅会返回数据,而且还会告诉你是哪个socket有数据。
epoll缺点是只支持linux,在BSD中对应实现的是kqueue
性能对比:
可以看出epoll相对于select 和poll不会随着链接的增多而出现性能上的大幅度下降。
4、信号驱动IO模型
这种模型是在套接字中设置一个信号驱动函数,当第一次请求数据的时候会立刻返回。当数据准备完毕的时候,内核会为这个进程产生一个信号,就会调用一开始进程内的驱动函数,在函数中我们可以完成数据的复制处理,这种模式的优势在于在等待的期间进程不会被阻塞。
5、异步IO模型
这种模型的工作机制是告知内核我要启动某个操作,并且让内核在完成以后通知我,其中整个操作包含了数据从内核复制到用户进程中。
异步IO模型和信号驱动IO模型的区别在于,信号IO模型只是收到内核通知说数据准备完毕,但是复制数据这个操作还是由用户进程来完成,但是异步IO模型从内核态复制数据到用户态包含通知用户进程都是在内核中进行完毕的。
我们把几种常见的IO模型进行对比:
根据上述定义,前四种模型,阻塞IO,非阻塞IO,信号驱动IO,多路复用IO都是同步IO,只有最后一种才是真正的异步IO。
顺便贴一下关于IO多路复用的形象化理解:http://www.zhihu.com/question/32163005
参考资料:unix网络编程第六章部分章节