IO多路复用epoll
作者:互联网
0 why: 问题来源
0.1 网络编程流程
//创建socket
int s = socket(AF_INET, SOCK_STREAM, 0);
//绑定IP地址和端口号port
bind(s, ...)
//监听客户端连接
listen(s, ...)
//接受客户端连接
int c = accept(s, ...)
//接收客户端数据
recv(c, ...);
//处理数据
operation(...)
0.2 内核接收网络数据过程
创建socket时,操作系统会创建一个由文件系统管理的socket对象。这个socket对象包含了发送缓冲区、接收缓冲区、等待队列等成员。等待队列指向所有需要等待该socket事件的进程。
新的文件描述符fd都会插入等待队列中,等到有数据到来时,等待序列会唤醒一个进程来处理数据。
0.3 问题来源
如何同时监视多个socket的数据?
1 解决方案之select模式
预先传入一个socket列表,如果列表中的socket都没有数据,挂起进程,直到有一个socket收到数据,唤醒进程。
int s = socket(AF_INET, SOCK_STREAM, 0);
bind(s, ...)
listen(s, ...)
int fds[] = ;//存放需要监听的socket
while(1){
int n = select(..., fds, ...)
for(int i=0; i < fds.count; i++){
if(FD_ISSET(fds[i], ...)){
//fds[i]的数据处理
}
}
}
这里需要注意的问题是:select查看是否有数据输入,需要进行遍历所有的socket;而在进程唤醒后,进程一脸懵逼,只知道有数据来了,却不知道是谁的数据,因此需要再次进行一次遍历,找到数据来自于哪个socket。这种多次遍历,每次都要将整个fds列表传递给内核,开销很大,因此我们需要改进一下。
2 what: 解决方案之epoll模式
epoll模式相比于select模式,最大的改进在于增加了一个中间环节“就绪列表”,还有就是分离了“socket插入到等待列表”和“阻塞等待事件到来”这两个过程。
2.1 功能分离
每次调用select都需要这两步操作,然而大多数应用场景中,需要监视的socket相对固定,并不需要每次都修改。epoll将这两个操作分开,先用epoll_ctl维护等待队列,再调用epoll_wait阻塞进程。
注意:epoll_wait方法不是使用循环的方式看是否有就绪时间,而是epoll_wait()一直阻塞直到:fd产生事件 / 被信号处理函数打断 / 超时。
int s = socket(AF_INET, SOCK_STREAM, 0);
bind(s, ...)
listen(s, ...)
int epfd = epoll_create(...);
epoll_ctl(epfd, ...); //将所有需要监听的socket添加到epfd中
while(1){
int n = epoll_wait(...)
for(接收到数据的socket){
//处理
}
}
2.2 增加中间环节“就绪列表”
select低效的另一个原因在于程序不知道哪些socket收到数据,只能一个个遍历。如果内核维护一个“就绪列表”,引用已就绪数据的socket,就能避免遍历。
3 how: 如何用?
3.1 创建epoll
int epoll_create(int size);
在最初的epoll_create()实现中,size参数将调用者希望添加到的文件描述符的数量告知内核。epoll实例。内核使用该信息作为内部数据结构初始分配空间的提示,事件。 (如果有必要,如果调用方的使用超出了大小提示,内核将分配更多空间。)如今,此提示不再必需(内核无需提示即可动态调整所需数据结构的大小),但是大小必须仍大于零,以便当新的epoll应用程序在较旧的内核上运行时,请确保向后兼容。
3.2 操作事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
向 epfd 对应的内核epoll 实例添加、修改或删除对 fd 上事件 event 的监听。op 可以为 EPOLL_CTL_ADD, EPOLL_CTL_MOD, EPOLL_CTL_DEL 分别对应的是添加新的事件,修改文件描述符上监听的事件类型,从实例上删除一个事件。
3.3 监听事件
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
当 timeout 为 0 时,epoll_wait 永远会立即返回。而 timeout 为 -1 时,epoll_wait 会一直阻塞直到任一已注册的事件变为就绪。当 timeout 为一正整数时,epoll 会阻塞直到计时 timeout 毫秒终了或已注册的事件变为就绪。因为内核调度延迟,阻塞的时间可能会略微超过 timeout 毫秒。
4 参考
https://www.cnblogs.com/Hijack-you/p/13057792.html
https://www.agedcat.com/programming_language/cpp/525.html
https://blog.csdn.net/zhoumuyu_yu/article/details/112472419
标签:...,socket,多路复用,epoll,int,内核,IO,wait 来源: https://www.cnblogs.com/yunmeng-shi/p/16462911.html