IO(Input/Output,输入输出)有两种操作,同步IO和异步IO;网络中的IO由一下四种情况:
- 输入操作:等待数据到达套接字接收缓冲区;
- 输出操作:等待套接字发送缓冲区有足够的空间容纳将要发送的数据;
- 服务器接收连接请求:等待新的客户端连接请求的到来;
- 客户端发送连接请求:等待服务器回送客户端发起的SYN所对应的ACK;
当一个网络IO发生时,会涉及到两个系统对象:(1)调用这个IO的进程;(2)系统内核;比如:当一个read操作发生时,会经历两个阶段:(1)等待数据准备;(2)将数据从内核拷贝到进程中
下面来介绍四种网络IO模型
1 四种网络IO模型
1.1 阻塞IO模型
- 特点:在IO执行的两个阶段(等待数据和拷贝数据)都被阻塞
- 存在的问题:会把整个线程阻塞,效率低下
- 解决服务器处理效率时提到的几个概念:
(1)线程池:旨在降低创建和销毁线程的频率,使其维持一定合理数量的线程,并让空闲的线程重新承担新的执行任务;
(2)连接池:维持链接的缓存池,尽量重用已有的连接,降低创建和关闭连接的频率;
1.2 非阻塞IO模型
- 特点:顾名思义就是在执行IO时,在等待数据阶段会直接返回,而不会阻塞,只有当数据准备好后,执行IO操作,在拷贝数据阶段会阻塞;
- 设置非阻塞状态的接口:
fcntl(fd, F_SETFL, O_NONBLOCK) - 存在的问题:需要循环执行IO操作,才能获取IO操作的结果
1.3 多路IO复用模型
- 基本原理:有个函数(如select)会不断地轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程;调用select时,整个现场会被阻塞;
- 注意:在多路复用IO模型中,对每个socket,都设置成非阻塞的;
- 存在的问题:
(1)select()接口并不是实现事件驱动的最好选择。因为当需要探测的句柄值较大时,select()接口本身需要消耗大量的时间去轮询各个句柄;
(2)该模型将时间探测和事件响应夹杂在一起,一旦事件响应的执行体过于攀达,则对整个模型都是灾难性的。
1.4 异步IO模型
- 特点:当进程发起IO操作之后,就直接返回,直到内核发送一个信号,告诉进程IO已完成,则在这整个过程中,进程完全没有被阻塞;
- 接口:
aio_read, aio_write
2 select
- 接口:
FD_ZERO(fd_set* fds); //将fds清零
FD_SET(int fd, fd_set fds); //将fd加入fds
FD_ISSET(int fd, fd_set* fds); //如果fd在fds中则为真
FD_CLR(int fd, fd_set* fds); //将fd从fds中清除
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval *timeout);
3 poll
- 头文件和原型:
#include <poll.h>
int poll(struct pollfd *fds, unsigned int nfds, int timeout) - pollfd结构体定义:
struct pollfd{
int fd; //文件描述符
short events; //等待的事件
short revents; //实际发生的事件
};
4 epoll
- 头文件 #include <sys/epoll.h>
- 操作过程中需要用到的三个接口:
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); - epool_event结构体定义:
struct epoll_event{
__unit32_t events;
epoll_data_t data;
};
5 select、poll、epoll
select, poll, epoll本质都是同步IO
select与poll:
(1)poll不要求开发者计算最大文件描述符+1操作;
(2)poll在应付大数目的文件描述符时速度更快;
(3)select可以监控的文件描述符数目是固定的,相对来说较少;poll可以监控的文件数目远大于select
(4)所监控的fd_set在select返回之后会发生变化,所以在下一次进入select之前都需要重新初始化所需监控的fd_set,poll函数将监控的输入和输出时间分开,允许被监控的文件数组被复用而无需重新初始化;
(5)select函数的超时参数在返回时也是未定义的,考虑到可移植性,每次在超时之后再下一次进入到select之前都需要重新设置超时参数。
- epoll的优点:
1.支持一个进程打开大数目的sock描述符(FD)
2.IO效率不随FD数目增加而线性下降
3.使用mmap加速内核与用户空间的消息传递