网络IO模型

同步IO:必须等待IO操作完成后,控制权才返回给用户进程。
异步IO:无序等待IO操作完成,就将控制权返回给用户进程。

一、4种网络IO模型:
1、阻塞IO模型
在Linux中,默认情况下所有的socket都是阻塞的。
阻塞是指IO操作需要彻底完成后才返回到用户空间;
非阻塞是指IO操作被调用后立即返回给用户一个状态值,不需要等到IO操作彻底完成。
阻塞IO模型的特点就是在IO执行的两个阶段(等待数据和拷贝数据)都被阻塞了。
大部分的socket接口都是阻塞的。所谓阻塞型接口是指系统调用时(一般是IO接口)却不返回调用结果,并让当前线程一直处于阻塞状态,只有当该系统调用获得结果或者超时出错时才返回结果。
线程池旨在降低创建和销毁线程的频率,使其维持一定合理数量的线程,并让空闲的线程重新承担新的执行任务。
连接池是指维持连接的缓存池,尽量重用已有的连接,降低创建和关闭连接的频率。

2、非阻塞IO模型
多线程模型可以方便高效的解决小规模的服务请求,但面对大规模的服务请求,多线程模型也会遇到瓶颈,可以用非阻塞模型来尝试解决这个问题。
在Linux下,可以通过设置socket使IO变为非阻塞状态。
非阻塞的接口相比于阻塞型接口的显著差异在于被调用之后立即返回。

3、多路IO复用模型
多路IO复用,有时也称为事件驱动IO,基本原理是有个函数(如select)会不断地轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。
用select的优势在于它可以同时处理多个连接。
这种模型的特征在于每一个执行周期都会探测一次或一组事件,一个特定的事件会触发某个特定的响应,因此可以将这种模型归类为“事件驱动模型”。
相比其他模型,使用select()的事件驱动模型只用单线程(进程)执行,占用资源少,不消耗太多CPU资源,同时能够为多客户端提供服务。但是select()接口并不是实现“事件驱动”的最好选择,因为当需要探测的句柄值较大时,select()接口本身需要消耗大量时间去轮询各个句柄。

4、异步IO模型
流程:用户进程发起read操作之后,立刻就可以开始去做其他事;而另一方面,从内核的角度,当它收到一个异步的read请求操作之后,首先会立刻返回,所以不会对用户进程产生任何阻塞。然后,内核会等待数据准备完成,然后将数据拷贝到用户内存中,当这一切都完成之后,内核会给用户进程发送一个信号,返回read操作已完成的信息。
调用阻塞IO会一直阻塞住对应的进程直到操作完成,而非阻塞IO在内核还在准备数据的情况下会立刻返回。

二、select
1、函数原型

int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval*timeout);

使用select就可以完成非阻塞方式工作的程序,它能够监视需要被监视的文件描述符的变化情况–读、写或异常。

三、poll
和select函数一样,poll函数也可以用于执行多路复用IO
头文件和函数原型如下:

#include<poll.h>
int poll(struct pollfd *fds, unsigned int nfds, int timeout);
struct pollfd
{
int fd; //文件描述符
short events; //等待的事件
short revents; //实际发生了的事件
};

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

1、对于网络编程来说,一般认为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,也可以选择修改这个宏然后重新编译内核。
2)IO效率不随FD数目增加而线性下降。
传统的select/poll另一个致命弱点就是当你拥有一个很大的socket集合,不过由于网络延迟,任一时间只有部分的socket是“活跃”的,但是select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。但是epoll不存在这个问题,它只会对“活跃”的socket进行操作–这是因为在内核中实现epoll是根据每个fd上面的callback函数实现的。
3)使用mmap加速内核与用户空间的消息传递。
无论是select、poll还是epoll都需要内核把fd消息通知给用户空间,如何避免不必要的内存拷贝就显得尤为重要。在这点上,epoll是通过内核与用户空间mmap处于同一块内存实现的。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值