通俗易懂说多路复用(2-1)epoll对select的改进及epoll原理

1. epoll 对 select 的改进

1.1 措施一:功能分离

select低效的原因之一是将“维护等待队列”和“阻塞进程”两个步骤合二为一。
如下图所示,每次调用select都需要这两步操作,然而大多数应用场景中,需要监视的socket相对固定,并不需要每次都修改。
epoll将这两个操作分开,先用epoll_ctl维护等待队列,再调用epoll_wait阻塞进程。
显而易见的,效率就能得到提升。
在这里插入图片描述

1.2 措施二:就绪列表:rdlist

select低效的另一个原因在于程序不知道哪些socket收到数据,只能一个个遍历。
如果内核维护一个“就绪列表”,引用收到数据的socket,就能避免遍历。
如下图所示,计算机共有三个socket,收到数据的sock2和sock3被rdlist(就绪列表)所引用。
当进程被唤醒后,只要获取rdlist的内容,就能够知道哪些socket收到数据。
在这里插入图片描述

2. epoll 原理

2.1 创建epoll对象

当某个进程调用epoll_create方法时,内核会创建一个eventpoll对象(也就是程序中epfd所代表的对象)。
eventpoll对象也是文件系统中的一员,和socket一样,它也会有等待队列。

epoll_create 源码实现
epoll_create函数的功能是创建eventpoll。

long sys_epoll_create(int size) 
{
	struct eventpoll *ep;
	ep_alloc(&ep); // 为ep分配内存并进行初始化
	fd = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep, O_RDWR | (flags & O_CLOEXEC));
	return fd;
}

2.2. 维护监视列表

创建epoll对象后,可以用epoll_ctl添加或删除所要监听的socket。以添加socket为例,
如下图,如果通过epoll_ctl添加sock1、sock2和sock3的监视,内核会将eventpoll添加到这三个socket的等待队列中。
当socket收到数据后,中断程序会操作eventpoll对象,而不是直接操作进程。

2.3. 接收数据

当socket收到数据后,中断程序会给eventpoll的“就绪列表”添加socket引用。
如下图展示的是sock2和sock3收到数据后,中断程序让rdlist引用这两个socket。
eventpoll对象相当于是socket和进程之间的中介,socket的数据接收并不直接影响进程,而是通过改变eventpoll的就绪列表来改变进程状态。

当程序执行到epoll_wait时,如果rdlist已经引用了socket,那么epoll_wait直接返回,如果rdlist为空,阻塞进程。

2.4. 阻塞和唤醒进程

假设计算机中正在运行进程A和进程B,在某时刻进程A运行到了epoll_wait语句。如下图所示,内核会将进程A放入eventpoll的等待队列中,阻塞进程。

参考:
https://zhuanlan.zhihu.com/p/64138532
https://zhuanlan.zhihu.com/p/64746509

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值