unix下的五中I/0模型:
阻塞式I/O
非阻塞式I/O
I/O多路复用
信号驱动式I/O
异步I/O(POSIX的aio_系列函数)
阻塞式I/O图
理解:进程阻塞于recvfrom的调用-->系统调用-->在内核中午数据报准备好(等待数据)-->数据报准备好以后复制数据报(等待:从内核空间复制到用户空间)--->复制完成-->返回成功指示。
举个例子:
非阻塞式I/O图
系统调用后,无数据准备好也会立马返回(这里并不代表I/O操作完成,如果我们下一行的代码依赖于之前的非阻塞I/O操作完成的结果,这时候可能就需要while循环,while循环是非常消耗cpu的,[捂脸],这里还不如不使用非阻塞I/O,因为既需要等待上面的结果,还消耗CPU,但是如果我们下一行代码不依赖于之前的结果,我们这里可以做一些计算任务,或者继续发起I/O操作,我们是可以使用非阻塞IO的。
举个例子:
这里可以通过while循环来一直问connect是否连接成功,直到连接成功后执行client.send方法
关于copy data from kernel to user:
比如说我们现在有8个G的内存,操作系统为了安全起见,操作系统在使用内存,我们自己写的应用程序也在使用内存,操作系统将1个G低地址的内存保护起来,供自己使用,当我们发送recvfrom的时候这是需要操作系统底层支持才行的,当我们调用recv的时候其实操作系统在做事,操作系统请求网络-->拿到数据之后也是在低地址的内存中,我们用户使用的时候需要将低地址的内存里的数据copy到用户内存地址来。
I/O复用
这里我们通过调用select来判断哪些socket或者文件句柄已经操作好了,比如上面的connect我们可以用select判断是否已经连接好,这种select和不同的循环请求它的状态(while)区别在于select可以监听多个socket状态。它给我们带来的好处在于,我们有一百个socket链接,只要有一个socket发生变化我们就可以处理它,但是I/O多路复用依然没有省去将数据从内核拷贝到用户空间的时间.
信号驱动式I/O
信号渠道I/O应用非常少,大家有兴趣可以自己研究一下
异步I/O(POSIX的aio_系列函数)
异步I/O对于多路复用性能没有很大提升,它们主要的区别在于:真正意义上的异步IO在数据从内核拷贝到用户空间才会发起信号,所以现在使用I/O多路复用比较多,比如torando框架 nginx
select 目前几乎可以应用在所有的平台上,但是它的缺点在于单个检查能够监视的文件描述符的数量存在最大限制,在linux上一般为1024。select在监听文件描述符的时候是要遍历所有的fds的,所以select的效率比较低。
poll 使用pollfd的指针实现,pollfd没有最大数量限制,但是还是需要轮询pollfd来获取就绪的描述符。所以效率提升不高。
epoll是select和poll的增强版本,epoll更加灵活,没有描述符的限制,epoll使用一个描述符管理多个描述符,将这些描述符事件存放到了一个事件表中。
我们通常使用selectors,from selectorsimport DefaultSelector, 其实还是使用select,但是selectors封装的更加好用,selectors到底是选用select 还是epoll,它会根据平台字段选用,在windows上使用select在linux上使用epoll。
下次整理下torando框架原理。