实现用户态epoll的原理

简介: 实现用户态epoll的原理

为什么要在用户态协议栈去实现一个epoll?

内核里面实现的epoll是对sockfd,文件系统 vfs进行的一个管理。

而对于用户空间的fd, (int类型的fd),内核里的epoll是用不了的,不好管理,于是可以自己去实现一个epoll。包括腾讯的f-stack,也实现了一个epoll。

实现一个用户态的epoll需要解决的问题

1.如何定义数据结构

2.协议栈如何与epoll模块通信

3.epoll如何加锁

4.ET与LT如何实现

epoll对外提供epoll_create(),epoll_ctl(),epoll_wait()这三个接口

一、如何定义数据结构

服务器管理了大量的fd,至少包含2个集合,需要包含哪些集合呢

1.所有fd总集(红黑树)

2.就绪的fd集合(队列)(不用于查找,所有的都需要处理,也没有key-value关系,可以使用线性数据结构。用队列比较合适,通过epoll_wait把对头部的取出来。如果用stack可能导致,部分数据一直都取不出来)

用什么数据结构去存呢?

是一个key-value的格式,可以通过fd对找到对应的value,有以下几种可能

1.hash(缺点:空间浪费,优点:当fd数量比较多的时候,查找性能比较优)

2.数组(空间不好扩展,查找速度慢)

3. 红黑树

4. b树/b+树是个N叉树,层高比较低,但是对比次数不比红黑树少)(对于一个节点查找下一个节点时间比较长的情况比较使用,降低层高,常用在磁盘上)

所以一般可以选择:红黑树 (在时间和空间综合考虑下,时间是最优的)

select/poll与epoll区别

1.使用;select/poll需要把总集拷贝到内核,而epoll不用

2.实现原理:select/poll,循环遍历总集合,检查是否有就绪的。而epoll是哪个节点,就把它加入到就绪队列

二、协议栈如何与epoll模块通信

epoll如何检测到是哪个节点呢?

1.在三次握手之后,tcb会加入全连接队列,此时服务器协议栈会通知epoll,后续通过accept处理通知

2.当客户端发送信息时,服务端协议栈接收到信息后,会回复一个ack,然后通知epoll,也就是EPOLLIN事件,后续通过recv来接受数据

3.当服务端的sendbuffer不为空的时候,服务器会向客户端发送数据,然后当客户端回送一个ack时,然后回通知epoll(EPOLLOUT),后续通过send来发送数据

4.接收到fin标志位的时候,也会通知epoll(EPOLLIN),后续通过close来关闭fd

5.接受到rst标志位的时候,会通知epoll(EPOLLERR)

那是epoll如何检测到是哪个fd的事件触发了呢?

根据 协议栈接收到的五元组—>查找到fd---->根据fd去红黑树找到相应的节点

如何将总集中的节点和红黑树中的节点关联起来呢?

就绪队列中的节点和 总集(红黑树,用来管理全部fd)中的节点是同一个,只不过,将它们连接了起来。如果要在就绪队列中删除一个节点,只要将它们的节点断开就行了。

struct epitem就是节点

rbn表示红黑树的节点,rdlink表示就绪队列的节点

struct epitem {
  RB_ENTRY(epitem) rbn;
  LIST_ENTRY(epitem) rdlink;
  int rdy; //exist in list 
  int sockfd;
  struct epoll_event event; 
};

协议栈如何通知epoll呢?

从协议栈回调(通过回调函数 )到epoll模块

1.通过fd查找对应的节点

2.把节点加入到就绪队列

epoll要实现的功能

1.epoll_create 创建一个红黑树的根节点

2.epoll_ctl ,ADD,DEL, MOD

3.epoll_wait 把就绪就绪队列里面的节点copy到用户空间

协议栈如何与epoll模块通信?

是异步的,没有耦合

三、epoll如何加锁

(为什么要加锁:为了使得线程安全,需要加锁)

对一颗红黑树操作的情况下

epoll_ctl时 对 红黑树加锁 (使用互斥锁)

epoll_wait时 对 就绪队列加锁(使用自选锁)

互斥锁和自旋锁使用的区别:

互斥锁在没拿到锁的时候会让出cpu,而自旋锁不会,它会一直等着,考虑到等待一段时间的成本更低的情况可以选择使用自旋锁。因此队列用自旋锁更加合适

四、ET与LT如何实现

1.et接受到数据,就调用一次回调

2.lt 检测recvbuffer里面有数据,就调用回调

回调要做的时候,就是查找fd,加入到就绪队列中。如果要加入的时候已经在就绪队列中了,就不需要去管了(不用再加入了)。

struct eventpoll {
  ep_rb_tree rbr;//红黑树的头节点
  int rbcnt;//红黑树节点的个数
  LIST_HEAD( ,epitem) rdlist;//就绪队列的起点 
  int rdnum;//就绪队列的节点数量
  int waiting;
  pthread_mutex_t mtx; //rbtree update
  pthread_spinlock_t lock; //rdlist update
  pthread_cond_t cond; //block for event   //条件等待,在epoll_wait的时候第四个参数为-1,会一直等待。 每触发一次回调就调用一次signal 
  pthread_mutex_t cdmtx; //mutex for cond
};

epoll_wait进行条件等待,当协议栈通知epoll事件的时候,就调用一次signal,使得epoll_wait往下走


相关文章
|
2月前
|
存储 网络协议 安全
Epoll的实现原理
Epoll的实现原理
|
调度 数据安全/隐私保护
用户态和内核态 中断处理机制
用户态和内核态 中断处理机制
331 0
|
5月前
|
存储 安全 网络协议
epoll的实现原理
epoll的实现原理
66 0
|
5月前
|
算法 Unix Linux
进程原理及系统调用
进程原理及系统调用
|
11月前
|
存储 监控 安全
2.9 epoll的实现原理
2.9 epoll的实现原理
75 0
|
存储 Linux
select/poll/epoll
select/poll/epoll
46 0
|
算法 Linux 调度
进程原理及其系统调用(上)
进程原理及其系统调用
123 0
|
Unix Linux
进程原理及其系统调用(下)
进程原理及其系统调用
75 0
|
存储 监控 NoSQL
epoll底层原理
epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本。
418 0
epoll底层原理
|
Linux Windows