socket中的epoll及I/O复用总结

epoll是Linux系统中的一种高效I/O复用技术,对比select和poll,epoll在处理大量文件描述符时效率更高,避免了随着监听fd数目增长而性能下降的问题。epoll分为LT(水平触发)和ET(边缘触发)两种模式,LT模式适合简单场景,而ET模式在大并发下更有优势,但编程时需要特别注意防止事件丢失。epollCtl用于事件注册、修改和删除,epoll_wait则返回就绪事件。epoll_oneshot配合ET模式可避免重复处理同一事件。epoll的性能取决于活跃连接比例,而非总连接数。
摘要由CSDN通过智能技术生成

select的限制

1.一个进程能打开的最大文件描述符是有限的

2.FD_SETSIZE(fd_set)限制,select内部使用一个数据结构fd_set,它的容量最大不能超过FD_SETSIZE。

poll的限制

一个进程能打开的最大文件描述符是有限的

上面的进程能打开的最大文件描述符的个数可通过命令ulimit -n number更改,但也不是无限大,还受到系统所能打开的最大文件描述符个数的限制,这个大小和内存有关。

但是很肯定的说,这个只是个说明,你的内存不一定支持这么多。

select和poll的另一个主要的缺点是:

内核要遍历所有我们所感兴趣的文件描述符,直到找到发生事件的文件描述符,这样的话,当并发数增长的时候,内核要遍历的文件描述符的个数也随之增大,导致效率下降。这也就是epoll引入的原因。

在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。

epoll

epoll是Linux特有的I/O复用函数。它在实现和使用上与select、poll有很大差异。首先,epoll使用一组函数来完成任务,而不是单个函数。其次,epoll把用户关心的文件描述符上的事件放在内核里的一个事件表中,从而无需像select和poll那样每次调用都要重复传入文件描述符集或事件集。但epoll需要使用一个额外的文件描述符,来唯一标识内核中的这个事件表。这个文件描述符使用epoll_create函数来创建。

//通过拷贝的方式,每次循环都需要重复传入文件描述符集
nready=poll(client,maxi+1,-1);
//用户关心的文件描述符上的事件放在内核里的一个事件表中,通过epollfd标识
//这个事件表
nready=epoll_wait(epollfd,&*events.begin(),static_cast<int>(events.size()),-1);

epoll_create函数

int epoll_create(int size);

作用:

该函数生成一个epoll专用的文件描述符,返回的文件描述符将用作其它所有epoll系统调用的第一个参数,以指定要访问的内核事件表。

参数:

size参数现在并不起作用,只是给内核一个提示,告诉它事件表需要多大。(曾经表示在这个epoll fd上能关注的最大fd数)

epoll_ctl函数

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

作用:

该函数用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删除事件。

参数:

epfd:由 epoll_create 生成的epoll专用的文件描述符;
op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD修改、EPOLL_CTL_DEL 删除
fd:关联的文件描述符,加入epoll_ctl进行管理

event:事件,对文件描述符所感兴趣的事件,指向epoll_event的指针;

返回值

如果调用成功返回0,不成功返回-1

说明

typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;

struct epoll_event {
__uint32_t events; //感兴趣的事件是可读还是可写
epoll_data_t data; /* User data variable */这是epoll高效的地方,数据类型是上面的那个共用体   
};

events可以是以下几个宏的集合:

  • EPOLLIN:触发该事件,表示对应的文件描述符上有可读数据。(包括对端SOCKET正常关闭);
  • EPOLLOUT:触发该事件,表示对应的文件描述符上可以写数据;
  • EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
  • EPOLLERR:表示对应的文件描述符发生错误;
  • EPOLLHUP:表示对应的文件描述符被挂断;
  • EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
  • EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。

epoll_wait函数

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

说明:

epoll_wait函数如果检测到事件,就将所有就绪的事件从内核事件表(由epfd参数指定)中复制它的第二个参数events指向的数组中。这个数组只用于输出epoll_wait检测到的就绪事件,而不像select和poll的数组参数那样既用于传入用户注册的事件,又用于输出内核检测到的就绪事件。这就极大地提高了应用程序索引就绪文件描述符的效率。

参数:
epfd:由epoll_create 生成的epoll专用的文件描述符;
epoll_event:用于回传待处理事件的数组;
maxevents:每次能处理的事件数;

timeout:等待I/O事件发生的超时值;-1相当于阻塞,0相当于非阻塞。一般用-1即可返回发生事件数。

返回值:

如果函数调用成功,返回对应I/O上已准备好的文件描述符数目

epoll实现原理

https://blog.csdn.net/russell_tao/article/details/7160071

关键:

epoll是一种IO多路复用技术,可以非常高效的处理数以百万计的socket句柄,比起以前的select和poll效率高大发了。
首先要调用epoll_create建立一个epoll对象。参数size是内核保证能够正确处理的最大句柄数,多于这个最大数时内核可不保证效果。epoll_ctl可以操作上面建立的epoll,可以添加、移除socket句柄。epoll_wait在调用时,在给定的timeout时间内,当在监控的所有句柄中有事件发生时,就返回。

Epoll高效主要体现在以下三个方面:

①从上面的调用方式就可以看出epoll比select/poll的一个优势:select/poll每次调用都要传递所要监控的所有fd给select/poll系统调用(这意味着每次调用都要将fd列表从用户态拷贝到内核态,当fd数目很多时,这会造成低效)。而每次调用epoll_wait时(作用相当于调用select/poll),不需要再传递fd列表给内核,因为已经在epoll_ctl中将需要监控的fd告诉了内核(epoll_ctl不需要每次都拷贝所有的fd,只需要进行增量式操作)。所以,在调用epoll_create之后,内核已经在内核态开始准备数据结构存放要监控的fd了。每次epoll_ctl只是对这个数据结构进行简单的维护。

 

② 此外,内核使用了slab机制,为epoll提供了快速的数据结构:

在内核里,一切皆文件。所以,epoll向内核注册了一个文件系统,用于存储上述的被监控的fd。当你调用epoll_create时,就会在这个虚拟的epoll文件系统里创建一个file结点。当然这个file不是普通文件,它只服务于epoll。epoll在被内核初始化时(操作系统启动),同时会开辟出epoll自己的内核高速cache区,用于安置每一个我们想

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值