`

[转发]I/O模型-读书笔记

阅读更多

I/O模型:

I/O操作需要内核系统调用来完成,系统调用需要Cpu来调度,而Cpu的访问速度相对于I/O来说比较快,所以Cpu不得不浪费Cpu时间来等待慢速I/O操作.

通过多进程方式来充分利用CPU资源,当还是希望让Cpu花费少的时间在I/O操作的调度上,这样就可以有更多的Cpu来完成I/O操作.

很多技术和策略都围绕如何让高速的Cpu和慢速的I/O设备更好的协调工作.

I/O操作主要是网络数据的接收和发送,以及磁盘文件的访问.归纳为多种模型称为I/O模型,本质区别在于Cpu的参与方式.

PIO和DMA:

慢速I/O设备和内存之间传输方式

PIO:磁盘和内存之间的数据传输需要Cpu控制,读取磁盘文件到内存的时候,数据需要经过Cpu存储转发.

DMA:可以不经过Cpu而直接进行磁盘和内存的数据交换,DMA模式下,Cpu只要向DMA控制器下达指令,让DMA控制器来处理数据的传输即可.DMA控制器通过系统总线来传输数据,传输

完毕后再通知Cpu.这样减低了Cpu占有率.

同步阻塞I/O:

I/O等待例子很多,比如web服务器等待用户请求.比如服务器与浏览器建立TCP链接后,需要等待用户发送HTTP请求.

I/O等待不可避免,有等待就会有阻塞.阻塞指的是当前发起I/O操作的进程被阻塞,并不是Cpu被阻塞,实际上没有什么能让Cpu阻塞,它只知道干活.

同步阻塞I/O指当前进程调用I/O操作的系统调用或者库函数时候,比如accept,send,recv等,进程便暂停,等待I/O操作完成后再继续运行.这样的I/O模型可以和多进程结合有效利用Cpu资源.

同步非阻塞I/O:

在同步阻塞I/O中,进程实际上等待的时间包括二部分,一个是等待数据的就绪,另外一个是等待数据的复制.对于网络I/O来说,前者的时间可能更长.

同步非阻塞I/O的调用不会等待数据的就绪,如果数据不可读写,则立即告诉进程.这种非阻塞I/O结合反复轮询来尝试数据是否就绪,防止进程被阻塞.最大的好处便在于可以在同一个进程里

同时处理多个I/O操作.但是由于一直在多次轮询查看数据是否就绪,花费大量Cpu时间.非阻塞I/O一般只针对网络I/O有效,对于磁盘I/O非阻塞I/O不产生效果.

多路I/O就绪通知:

web服务器同时处理大量文件描述符必不可少.由于要对socket检查是否可以接收的数据,则会浪费太多的Cpu时间.

多路I/O就绪通知出现,提供了对文件描述符就绪检查的高性能方案.允许进程通过一种方法来同时监视所有文件描述符.并可以快速获得所有就绪的文件描述符.然后只针对这些描述符进行数据访问.

I/O就绪通知只是帮助快速获得就绪的文件描述符.当得知数据就绪后,就访问数据本身而言.仍然需要选择阻塞和非阻塞的访问方式.多路I/O就绪通知有多种实现.

select:

通过一个select系统调用来监视包含多个文件描述符的数组.当select返回后,该数组中就绪的文件描述符会被内核修改标志位.使得进程可以获得这些文件描述符从而进行读写操作.

select的缺点

1)单个进程能够监视的文件描述符的数量存在最大限制.

2)select维护的存储大量文件描述符的数据结构,随着文件描述符量的增大,复制的开销也线性增长.

3)网络响应时间的延迟使得大量Tcp链接处于非活跃状态,但是select会对所有socket进行一次性扫描,也浪费了一定开销.

poll:

没有最大文件描述符的限制.select和poll将就绪的文件描述符告诉进程后,如果进程没有对其进行I/O操作,下次调用select和poll的时候将再次报告这件文件描述符.所以不会丢失就绪的

消息,该方式称为水平触发.

sigio:

sigio不是每次都告诉我们文件描述符是否就绪,而是高速文件描述符刚刚变为就绪,只说一次,称呼为边缘触发.

/dev/poll:

使用虚拟的/dev/poll设备,可以将有监视的文件描述符数组写入这个设备,然后通过ioctl来等待时间通知.没有提供直接的内核支持,所以性能不是很稳定.

/dev/epoll:

在/dev/epool的基础上增加了内存映射.

epoll:

本质的改进是epoll基于事件的就绪通知,通过callback的机制来激活文件描述符.

内存映射:

linux内核提供一种访问文件的特殊方式.可以将内存中某块地址空间和制定的磁盘文件相关联.从而对这块内存的访问转换为对磁盘文件的访问.

大部分情况下,使用内存映射可以提高磁盘I/O访问的性能,因为无须read或者write系统调用,而是通过mmap系统调用建立内存和磁盘文件的关联,然后像访问内存一样只有访问文件.

直接I/O:

linux2.6中,内存映射和直接访问文件没有本质区别.因为数据从进程用户态内存空间到磁盘需要经过二次复制,及在磁盘与内核缓存区之间以及在内核缓存区与用户态内存空间.

引入内核缓存区的目的在于提高磁盘文件的访问性能.比如进程读取磁盘文件的时,如果文件已经在内核缓冲区中,那么就无须再访问磁盘.而进程需要向文件中写入数据的时候.则

直接写到内核缓存区便告诉进程已经成功写入,写入磁盘是通过一定策略进行延迟.

为了充分提高性能,系统绕过内核缓存区,由自己在用户空间实现管理I/O缓存区,比如数据库的缓存机制和写延迟机制.

Linux提供了对这种需求的支持,在open系统调用的时候增加参数选项O_DIRECT.用它打开的文件便可以通过内核缓存区的直接访问,有效避免了Cpu和内存的多余时间开销.

Sendfile:

引入Sendfile在于内核希望请求的处理尽量在内核完成,减少内核态的切换以及用户数据复制的开销,Linux通过系统调用提高这种机制,它可以将磁盘文件的特定部分直接传送到客户端的Socket

描述符,所以一般使用在网卡传输,减少cpu和内存的开销.

异步I/O:

同步和异步,阻塞和非阻塞容易被混用,其实完全不一样,修饰的对象也不同.

 

原文地址:http://hi.baidu.com/ywdblog/blog/item/eef2d5fc42cba396b801a0a1.html

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics