epoll,解决C10K问题的关键

epoll是event poll的意思。因为涉及到用户态wait获取到内核返回的读写就绪事件之后、去主动到内核缓冲区获取数据、所以本质上是属于同步非阻塞io模型。linux上边真正的异步非阻塞io模型还没提供、windows倒是有了,但是服务器端仍然是linux的天下,所以现在真正事实上的标准高性能网络io模型仍然是epoll。它也是解决c10k问题的关键突破。

一、epoll在linux中的定义和系统调用
//用户数据载体、用来用户态和内核态交换数据

typedef union epoll_data {
    void *ptr;
    int fd;
    uint32_t u32;
    uint64_t u64;
} epoll_data_t;

//fd装载入内核的载体

struct epoll_event {
    uint32_t events; /* Epoll events */
    epoll_data_t data; /* User data variable */
};

//三个主要api

int epoll_create(int size);  

//在内核空间创建epoll句柄epfd,就是新建一个多路复用器(struct eventpoll对象)然后返回它的文件描述符。可以类比java nio里的selector.open()方法。

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

//向epfd指向的内核空间里边的多路复用器epoll添加/删除(int op)一个fd以及感兴趣的事件event。这个event是epoll_event类型,里边封装了epoll_data用户态的数据、以及事件类型。可以类比一下java nio里边的register()方法。

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

//这个就是需要用户态程序去轮询的方法了,可以查到就绪的事件、内核会通过这个方法告知用户态程序就绪的events。一般来说用户态程序拿到一批就绪的事件以后会遍历、然后逐个判断属于哪种事件、比如是connect还是read/write,然后执行不同的逻辑。类比java nio里的selector.selectedKeys()。

//epoll官方例子

#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;
/* Set up listening socket, 'listen_sock' (socket(),bind(), listen()) */
epollfd = epoll_create(10); //创建多路复用器epollfd
if(epollfd == -1) {
    perror("epoll_create");
    exit(EXIT_FAILURE);
} 
ev.events = EPOLLIN;
ev.data.fd = listen_sock;

if(epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) { //向创建好的多路复用器epollfd添加server socket, 然后指定其event为EPOLLIN即监听客户端连接请求
    perror("epoll_ctl: listen_sock");
    exit(EXIT_FAILURE);
} 
for(;;) { //自旋
    nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); //从多路复用器epollfd中查询已就绪的events
    if (nfds == -1) {
        perror("epoll_pwait");
        exit(EXIT_FAILURE);
    } 
    for (n = 0; n < nfds; ++n) {
        if (events[n].data.fd == listen_sock) {
            //主监听socket有新连接,connect事件
            conn_sock = accept(listen_sock,(struct sockaddr *) &local, &addrlen);
            if (conn_sock == -1) {
                perror("accept");
                exit(EXIT_FAILURE);
            } 
            setnonblocking(conn_sock);
            ev.events = EPOLLIN | EPOLLET;
            ev.data.fd = conn_sock;
            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,&ev) == -1) {
                perror("epoll_ctl: conn_sock");
                exit(EXIT_FAILURE);
            }
        } else {
            //已建立连接的可读写句柄,read/write事件
            do_use_fd(events[n].data.fd);
        }
    }
}

二、LT模式和ET模式的简单理解
level triggered水平触发和edge triggered边缘触发,指的是epoll在socket读写上的两种通知方式。

1、LT 是默认的模式,支持阻塞和非阻塞socket、epoll_wait获取到内核拷贝来的就绪事件之后、用户态程序如果没有处理完、下次仍然会在调用epoll_wait的时候通知给用户态程序、数据不会丢失因为反复提醒、更加安全。
这里边read的话只要有数据就通知就绪可读了,但是write的话,一般来说socket空闲了、写缓冲区不满就会提醒写就绪、也就是反复的提醒某个socket可写。但此时用户态程序可能并没有对这个socket的写的需求,大量连接的时候这也是个不小的开销,所以一般是在没有数据要发送的时候,由用户态程序把对应的fd写事件从epoll列表里去掉,需要的时候再加进去。

2、ET模式是高速模式,只支持非阻塞socket,如果用户态程序对事件没有处理完,那下一次epoll_wait调用就不会继续通知了。对用户态读写处理的逻辑容错提出了更高的要求、但因为没有反复通知、所以性能更高。简单来说ET模式只在socket的读写状态发生变化的时候通知、状态不变则不通知,比如读缓冲区由无数据到有数据通知read事件、写缓冲区由满到未满通知可写write事件。
通过前面的对比可以看到LT模式比较安全并且代码编写也更清晰,但是ET模式属于高速模式,在处理大高并发场景使用得当效果更好,具体选择什么根据自己实际需要和团队代码能力来选择。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容