网络IO模型

IO有两种操做,同步IO和异步IO。同步IO指的是,必须等待IO操做完成后,控制权才返回给用户进程。异步IO指的是,无须等待IO操做完成,就将控制权返回给用户进程。编程

网络中的IO,因为不一样的IO设备有着不一样的特色,网络通讯中每每须要等待。常见的有如下4种状况。数组

(1)输入操做:等待数据到达套接字接收缓冲区。服务器

(2)输出操做:等待套接字发送缓冲区有足够的空间容纳将要发送的数据。网络

(3)服务器接收链接请求:等待新的客户端链接请求的到来。异步

(4)客户端发送链接请求:等待服务器会送你个客户的发起的SYN所对应的ACK。socket

当一个网络IO(假设是read)发生时,它会涉及两个系统对象,一个是调用这个IO的进程,另外一个是系统内核。当一个read操做发生时,它会经历两个阶段:(1)等待数据准备;(2)将数据从内核拷贝到进程中。函数

 

4种网络IO模型线程

(1)阻塞IO模型(2)非阻塞IO模型(3)多路IO复用模型(4)异步IO模型3d

 

1.阻塞IO模型对象

在Linux中,默认状况下全部的socket都是阻塞的,一个典型的读写流程以下图:

阻塞和非阻塞的概念描述的是用户线程调用内核IO操做的方式:阻塞是指IO操做须要完全完成后才返回用户空间;而非阻塞是指IO操做被调用后当即返回给用户一个状态值,不须要等到IO操做完全完成。

 

 2.非阻塞IO模型

在Linux下,能够经过设置socket使IO变为非阻塞状态。当对一个非阻塞的socket执行read操做时,流程以下图

 

 

3.多路IO复用模型

多路IO复用,有时也称为事件驱动IO。它的基本原理就是有个函数(如select)会不断地轮询所负责的socket,当某个socket有数据到达了,就通知用户进程,多路IO复用模型的流程图以下:

 

 

select、poll和epoll的区别

select、poll和epoll都是多路IO复用的机制。多路IO复用就经过一种机制,能够监视多个描述符,一旦某个描述符就绪(通常是读就绪或者写就绪),可以通知程序进行相应的读写操做。但select、poll和epoll本质上都是同步IO,由于它们都须要在读写事件就绪后本身负责进行读写,便是阻塞的,而异步IO则无须本身负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

下面对这3种多路IO复用进行对比。

(1) 首先仍是来看常见的select()和poll()。对于网络编程来讲,通常认为poll比select要高级一些,这主要源于如下几个缘由。

1)poll()不要求开发者在计算最大文件描述符时进行+1的操做。

2)poll()在应付大数目的文件描述符的时候速度更快,由于对select()来讲内核须要检查大量描述符对应的fd_set中的每个比特位,比较费时。

3)select()能够监控的文件描述符数目是固定的,相对来讲也较少(1024或2048)。若是须要监控数值比较大的文件描述符,或是分布得很稀疏的较少的描述符,效率也会很低。而对于poll()函数来讲,就能够建立特定大小的数组来保存监控的描述符,而不受文件描述符值大小的影响,并且poll()能够监控的文件数目远大于select()。

4)对于select()来讲,所监控的fd_set在select返回以后会发生变化,因此在下一次进入select()以前都须要从新初始化须要监控的fd_set,poll()函数将监控的输入和输出事件分开,容许被监控的文件数组被复用而不须要从新初始化。

5)select()函数的超时参数在返回时也是未定义的,考虑到可移植性,每次在超时以后在下一次进入到select()以前都须要从新设置超时参数。

(2)select()的优势以下所述。

1)select的可移植性更好,在某些UNIX系统上不支持poll().

2)select()对于超时值提供了更好的精度,而poll()是精度较差。

(3)epoll的优势以下所述。

1)支持一个进程打开大数目的socket描述符(FD)

select()最不能忍受的是一个进程所打开的FD是有必定限制的,由FD_SETSIZE的默认值是1024/2048。对于那些须要支持上万链接数目的IM服务器来讲显然太少了。这时候能够选择修改这个宏而后从新编译内核。不过epoll则没有这个限制,它所支持的FD上限是最大能够打开文件的数目,这个数字通常大于2048.举个例子,在1GB内存的空间中这个数字通常是10万左右,具体数目可使用cat/proc/sys/fs/file-max查看,通常来讲这个数目和系统内存关系很大。

2)IO效率不随FD数目增长而线性降低。

传统的select/poll另外一个致命弱点就是当你拥有一个很大的socket集合,不过因为网络延迟,任一时间只有部分的socket是“活跃”的,可是selecct/poll每次调用都会线性扫描所有的集合,致使效率呈现线性降低。可是epoll不存在这个问题,它只会对“活跃”的socket进行操做——这是由于在内核中实现epoll是根据每一个fd上面的callback函数实现的。那么,只有“活跃”的socket才会主动去调用callback函数,其余idle状态socket则不会,在这个点上,epoll实现了一个“伪”AIO,由于这时候推进力由Linux内核提供。

3)使用mmap加速内核与用户空间的消息传递

这点实际上涉及epoll的具体实现。不管是selecct、poll仍是epoll都须要内核把fd消息通知给用户空间,如何避免没必要要的内存拷贝就显得尤其重要。在这点上,epoll是经过内核与用户空间mmap处于同一块内存实现的。

对于poll来讲须要将用户传入的pollfd数组拷贝到内核空间,由于拷贝操做和数组长度相关,时间上来看,这是一个O(n)操做,当事情发生后,poll将得到的数据传送到用户空间,并执行释放内存和剥离等待队列等工做,向用户空间拷贝数据与剥离等待队列等操做的时间复杂度一样是O(n)。

相关文章
相关标签/搜索