IO多路复用模型

文章目录
  1. 1. 前置知识
  2. 2. 为什么需要IO复用
  3. 3. 三种IO复用方法
    1. 3.1. select
    2. 3.2. poll
    3. 3.3. epoll
  4. 4. 三者区别
    1. 4.1. 一个进程支持的最大连接数
    2. 4.2. 连接的客户端数量剧增后的效率
    3. 4.3. 消息传递方式
  5. 5. 总结
  6. 6. 实现原理分析
  7. 7. IO复用模型的缺点
  8. 8. 参考

不同于传统的“一个进程处理一个客户端请求”的方式,IO复用可以让一个进程处理多个客户端的请求,更加节省资源。

前置知识

为什么需要IO复用

一个简单地服务端可能是这样的:

1
2
3
4
5
6
7
8
调用socket()创建套接字
bind()绑定地址和端口
listen()监听套接字
while(1){
调用accept()连接客户端
fork()创建进程B来处理客户端的需求/使用新的线程来执行任务
}
释放资源

当使用上面这种方式来处理客户端的请求时,如果客户端数量特别多,服务端就会创建很多进程或线程来执行任务。这种一个进程/线程对应一个客户端的方式其实是挺浪费资源的,如果让一个进程就能够处理多个客户端的连接,那么就能够减少很多不必要的资源浪费。IO就可以解决这个问题。

三种IO复用方法

select

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
参数:
nfds为需要检查的文件描述符个数
readfds检测可读的文件描述符集合
writefds检测可写的问价描述符集合
exceptfds检测异常条件出现的文件描述符
timeout超时时间
阻塞:设置NULL,会一直阻塞,直到有描述符准备好IO
立即返回:必须设置timeval结构体,但其中的值为0
等待一段时间:在规定时间内如果发生IO活动就马上返回,如果一直没有就等超时后再返回。
返回值:
返回发生所检测操作的fd总数,错误时返回SOCKET_ERROR。
发生io活动的fd存储在相应的参数中(会删除所有传入的fd,只留下发生io活动的)
fd_set
long类型数组,存储文件描述符。可以用下面几个宏来设置。
FD_ZERO(fd_set *fdset) 将指定的文件描述符集清空
FD_SET(fd_set *fdset) 用于在文件描述符集合中增加一个新的文件描述符。
FD_CLR(fd_set *fdset) 用于在文件描述符集合中删除一个文件描述符。
FD_ISSET(int fd,fd_set *fdset) 用于测试指定的文件描述符是否在该集合中。
struct timeval{
long tv_sec; //seconds
long tv_usec; //microseconds
};

poll

1
2
3
4
5
6
7
int poll (struct pollfd *fds, unsigned int nfds, int timeout);
poll使用一个 pollfd的指针来传入要检测的IO活动,和返回发生的IO活动
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events to watch */
short revents; /* returned events witnessed */
};
  • select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。
    (?这里是在函数执行的时候轮询,还是返回后轮询)

epoll

1
2
3
int epoll_create(int size);//创建一个epoll的句柄,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);
  • epoll的两种工作模式
    水平触发
    边缘触发

三者区别

一个进程支持的最大连接数

select:单进程支持最大连接数FD_SETSIZE个,一般32位机器位1024,64位为2048。可以重新编译内核修改数量,但性能无法保证。
poll:无数量限制,与select本质上没有区别,用链表存储fd。但连接数较多时无法保证性能。
epoll:连接数很大,1G内存机器可以10万左右连接,2G内存可以20万连接。

连接的客户端数量剧增后的效率

(?这里是在select函数内部对fd轮询还是函数返回后轮询浪费时间。)
select:每次调用都会对所有的fd(一个描述符对应一个客户端连接)进行遍历,随着fd数量增加,性能下降。
poll:同select。
epoll:不对所有描述符进行遍历,所有不会由于连接数量增加导致性能过分下降。(由于采用回调函数实现。只有活跃的客户端才会调用回调函数,所有epoll会因为活跃的连接数过多而性能下降)

消息传递方式

select:将消息从内核空间拷贝到用户空间。
poll:同上。
epoll:使用mmap来与内核空间共享内存。

总结

简单地说来,select和poll适合连接数量小,活跃数量多的情况。而epoll适合客户端的连接数量很大,活跃数量很小的情况。

实现原理分析

待定

IO复用模型的缺点

待定

参考

欢迎与我分享你的看法。
转载请注明出处:http://taowusheng.cn/