epoll 的内部实现原理【上】

1
最近看了几天 IO多路复用,epoll相关, 理论明白了大概,看的脑瓜疼,先写个文章记录一下。

一、关键词

  • IO多路复用
  • epoll select poll
  • mmap 红黑树 连表
  • 中断程序

    二、epoll

    1、epoll概念

    epoll 接口是解决Linux内核处理大量文件描述符提出的方案。

场景:100万用户同事与一个TCP保持连接,而每一时刻只有几个或者几十个连接是活跃的(接受TCP包);进程收集有事件的连接时,把这100万套接字传递给炒作系统,由操作系统内核计算连接上有没有未处理的事件(select 和 poll的做法,极大的浪费了资源),而epoll在linux内核中申请了一个简单的文件系统,在进程启动时创建一个epoll对象,并在需要时候向它添加删除。

2、epoll基本思想

  • epoll在Linux内核中采用红黑树构建了文件系统,。
  • epoll红黑树上采用事件异步唤醒,内核监听IO,事件发生后内核搜索红黑树并将对应节点数据放入异步唤醒的事件队列中
  • epoll的数据从用户空间到内核空间采用mmap存储I/O映射来加速(传递最快,消耗最小,传递数据过程不涉及系统调用)

二、epoll如何工作

epoll三个方法

  • 调用epoll_create创建一个epoll 对象(epoll文件系统中给这个句柄分配资源)
  • 调用epoll_ctl向epoll对象中添加连接的套接字
  • 调用epoll_wait收集发生事件的连接

    调用epoll_create时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储以后epoll_ctl传来的socket外,还会再建立一个rdllist双向链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个rdllist双向链表里有没有数据即可。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。所以,epoll_wait非常高效

    三、epoll两种触发方式

  • ET模式(边缘触发)只有数据到来才触发,不管缓存区中是否还有数据,缓冲区剩余未读尽的数据不会导致epoll_wait返回
  • LT 模式(水平触发,默认)只要有数据都会触发,缓冲区剩余未读尽的数据会导致epoll_wait返回。
    。。。。。。对外来说有点难啃了一天半时间

参考网上案例写的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <unistd.h>
#include <sys/epoll.h>

int main(void)
{
int epfd,nfds;
int epfd,nfds;
struct epoll_event ev,events[5]; //ev用于注册事件,数组用
于返回要处理的事件
epfd = epoll_create(1); //只需要监听一个描述>符——标准输入
ev.data.fd = STDIN_FILENO;
ev.events = EPOLLIN; //监听读状态同时设置LT模

epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev); //注册epoll事件
for(;;)
{
nfds = epoll_wait(epfd, events, 5, -1);
int i;
for( i=0; i < nfds; i++)
{
if(events[i].data.fd==STDIN_FILENO)
{
char buf[1024] = {0};
read(STDIN_FILENO, buf, sizeof(buf));
printf("welcome to epoll's word!\n");
}
}
}
}

运行结果

参考文献:

趣谈linux操作系统

tcp/ip 协议栈——epoll 的内部实现原理

彻底学会使用epoll(一)——ET模式实现分析

Linux下的I/O复用与epoll详解