网络编程实战
盛延敏
前大众点评云平台首席架构师
立即订阅
6034 人已学习
课程目录
已完结 39 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 学好网络编程,需要掌握哪些核心问题?
免费
第一模块:基础篇 (9讲)
01 | 追古溯源:TCP/IP和Linux是如何改变世界的?
02 | 网络编程模型:认识客户端-服务器网络模型的基本概念
03丨套接字和地址:像电话和电话号码一样理解它们
04 | TCP三次握手:怎么使用套接字格式建立连接?
05 | 使用套接字进行读写:开始交流吧
06 | 嗨,别忘了UDP这个小兄弟
07 | What? 还有本地套接字?
08 | 工欲善其事必先利其器:学会使用各种工具
09丨答疑篇:学习网络编程前,需要准备哪些东西?
第二模块:提高篇 (10讲)
10 | TIME_WAIT:隐藏在细节下的魔鬼
11 | 优雅地关闭还是粗暴地关闭 ?
12 | 连接无效:使用Keep-Alive还是应用心跳来检测?
13 | 小数据包应对之策:理解TCP协议中的动态数据传输
14丨UDP也可以是“已连接”?
15 | 怎么老是出现“地址已经被使用”?
16 | 如何理解TCP的“流”?
17 | TCP并不总是“可靠”的?
18 | 防人之心不可无:检查数据的有效性
19丨提高篇答疑:如何理解TCP四次挥手?
期中复习周 (2讲)
期中大作业丨动手编写一个自己的程序吧!
免费
期中大作业丨题目以及解答剖析
免费
第三模块:性能篇 (12讲)
20 | 大名⿍⿍的select:看我如何同时感知多个I/O事件
21 | poll:另一种I/O多路复用
22 | 非阻塞I/O:提升性能的加速器
23 | Linux利器:epoll的前世今生
24 | C10K问题:高并发模型设计
25 | 使用阻塞I/O和进程模型:最传统的方式
26 | 使用阻塞I/O和线程模型:换一种轻量的方式
27 | I/O多路复用遇上线程:使用poll单线程处理所有I/O事件
28 | I/O多路复用进阶:子线程使用poll处理连接I/O事件
29 | 渐入佳境:使用epoll和多线程模型
30 | 真正的大杀器:异步I/O探索
31丨性能篇答疑:epoll源码深度剖析
第四模块:实战篇 (4讲)
32 | 自己动手写高性能HTTP服务器(一):设计和思路
33 | 自己动手写高性能HTTP服务器(二):I/O模型和多线程模型实现
34 | 自己动手写高性能HTTP服务器(三):TCP字节流处理和HTTP协议实现
35 | 答疑:编写高性能网络编程框架时,都需要注意哪些问题?
结束语 (1讲)
结束语丨我相信这不是结束,让我们江湖再见
网络编程实战
登录|注册

31丨性能篇答疑:epoll源码深度剖析

盛延敏 2019-10-18
你好,我是盛延敏,今天是网络编程实战性能篇的答疑模块,欢迎回来。
在性能篇中,我主要围绕 C10K 问题进行了深入剖析,最后引出了事件分发机制和多线程。可以说,基于 epoll 的事件分发能力,是 Linux 下高性能网络编程的不二之选。如果你觉得还不过瘾,期望有更深刻的认识和理解,那么在性能篇的答疑中,我就带你一起梳理一下 epoll 的源代码,从中我们一定可以有更多的发现和领悟。
今天的代码有些多,建议你配合文稿收听音频。

基本数据结构

在开始研究源代码之前,我们先看一下 epoll 中使用的数据结构,分别是 eventpoll、epitem 和 eppoll_entry。
我们先看一下 eventpoll 这个数据结构,这个数据结构是我们在调用 epoll_create 之后内核侧创建的一个句柄,表示了一个 epoll 实例。后续如果我们再调用 epoll_ctl 和 epoll_wait 等,都是对这个 eventpoll 数据进行操作,这部分数据会被保存在 epoll_create 创建的匿名文件 file 的 private_data 字段中。
/*
* This structure is stored inside the "private_data" member of the file
* structure and represents the main data structure for the eventpoll
* interface.
*/
struct eventpoll {
/* Protect the access to this structure */
spinlock_t lock;
/*
* This mutex is used to ensure that files are not removed
* while epoll is using them. This is held during the event
* collection loop, the file cleanup path, the epoll file exit
* code and the ctl operations.
*/
struct mutex mtx;
/* Wait queue used by sys_epoll_wait() */
//这个队列里存放的是执行epoll_wait从而等待的进程队列
wait_queue_head_t wq;
/* Wait queue used by file->poll() */
//这个队列里存放的是该eventloop作为poll对象的一个实例,加入到等待的队列
//这是因为eventpoll本身也是一个file, 所以也会有poll操作
wait_queue_head_t poll_wait;
/* List of ready file descriptors */
//这里存放的是事件就绪的fd列表,链表的每个元素是下面的epitem
struct list_head rdllist;
/* RB tree root used to store monitored fd structs */
//这是用来快速查找fd的红黑树
struct rb_root_cached rbr;
/*
* This is a single linked list that chains all the "struct epitem" that
* happened while transferring ready events to userspace w/out
* holding ->lock.
*/
struct epitem *ovflist;
/* wakeup_source used when ep_scan_ready_list is running */
struct wakeup_source *ws;
/* The user that created the eventpoll descriptor */
struct user_struct *user;
//这是eventloop对应的匿名文件,充分体现了Linux下一切皆文件的思想
struct file *file;
/* used to optimize loop detection check */
int visited;
struct list_head visited_list_link;
#ifdef CONFIG_NET_RX_BUSY_POLL
/* used to track busy poll napi_id */
unsigned int napi_id;
#endif
};
你能看到在代码中我提到了 epitem,这个 epitem 结构是干什么用的呢?
每当我们调用 epoll_ctl 增加一个 fd 时,内核就会为我们创建出一个 epitem 实例,并且把这个实例作为红黑树的一个子节点,增加到 eventpoll 结构体中的红黑树中,对应的字段是 rbr。这之后,查找每一个 fd 上是否有事件发生都是通过红黑树上的 epitem 来操作。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《网络编程实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(10)

  • 贺荣伟
    这篇文章写得非常棒!之前学 epoll,只会流于表面,也很少去深度剖析底层的数据结构,多读几遍!
    另外分享一个大佬同学 epoll 内核源码详解发的帖子:https://www.nowcoder.com/discuss/26226

    作者回复: 赞分享。

    2019-10-26
    1
  • 鱼向北游
    select这种是把等待队列和就绪队列混在一起,epoll根据这两种队列的特性用两种数据结构把这两个队列分开,果然在程序世界没有解决不了的事情,如果有,就加一个中间层

    作者回复: 你这个理解倒是比较有趣,程序是伟大的。

    2019-10-18
    1
  • godtrue
    功力不够,读起来有些费劲,好似拿到了九阴真经,不过需要黄姑娘翻译一下!
    如果能来个图,就好了😁

    作者回复: 待我找黄姑娘来:)

    2019-12-01
  • haozhang
    老师 进程阻塞的形式是什么呢 是for死循环吗 还是加入到等待队列呢,我看for循环前面不是加入到等待队列了吗?

    作者回复: 进程阻塞其实就是把CPU时间调度给了其他的进程,因为得不到CPU时间,所以当前进程的代码没有办法再执行下去,表现的行为就是当前程序执行"休眠"在那里了。

    这里的等待队列是LInux内核把等待调度的进行按照自己内部的数据结构,形成了一个队列。按照LInux内核任务调度算法进行调度,来唤醒对应的"休眠"进程。

    2019-11-06
  • 刘晓林
    老师,我请教两个问题:
    1.ep_send_events_proc这个函数在把events列表拷贝到用户空间前会调用ep_item_poll函数,已确定对应的fd上的事件依旧有效。那ep_item_poll是根据什么来确定事件的有效性呢?
    2.在ep_send_events_proc处理level-triggered的时候,有这么一段话“At this point, no one can insert into ep->rdllist besides us. The epoll_ctl() callers are locked out by ep_scan_ready_list() holding "mtx" and the poll callback will queue them in ep->ovflist.”意思是说epoll_ctl()的调用者也被锁在外面了。这个锁是说在ep_send_events_proc还没处理完的时候,epoll_ctl()无法操纵rdllist,但是之后是可以的,以实现再次注册感兴趣的时间。是这样吗?

    作者回复: 看得好自信,汗,我试着回答哈:

    1.根据套接字文件上的poll接口,套接字文件上记录了它有效的事件:
    static inline unsigned int ep_item_poll(struct epitem *epi, poll_table *pt)
    {
    pt->_key = epi->event.events;

    return epi->ffd.file->f_op->poll(epi->ffd.file, pt) & epi->event.events;
    }

    2.是的。

    static int ep_scan_ready_list(struct eventpoll *ep,
    int (*sproc)(struct eventpoll *,
    struct list_head *, void *),
    void *priv, int depth, bool ep_locked)
    {
    int error, pwake = 0;
    unsigned long flags;
    struct epitem *epi, *nepi;
    LIST_HEAD(txlist);

    /*
    * We need to lock this because we could be hit by
    * eventpoll_release_file() and epoll_ctl().
    */

    if (!ep_locked)
    mutex_lock_nested(&ep->mtx, depth);
            ...
    }

    2019-10-29
  • fackgc17
    可以提供一下分析用的 kernel 版本吗

    作者回复: linux 4.14.4

    2019-10-26
  • 初见
    老师,我之前面试被问到过说,epoll 更适合连接很多,但活跃的连接较少的情况

    那么,连接很多,活跃连接也很多的情况下,用什么方案呢? 堆机器嘛

    作者回复: 不知道为什么会有这样的说法,我觉得这个说法不正确,epoll适合活跃连接很多的场景。

    2019-10-25
  • 影帝
    我发现看留言学到的更多。🤓

    作者回复: 这就是social的力量 :)

    2019-10-20
  • 沉淀的梦想
    缺乏C语言和linux内核基础的人读起这些源码来相当吃力,虽然老师讲得很好

    作者回复: 主要是理解整体的概念和设计,细节不用理解太深。可以读理解几遍,肯定会有收获的。

    2019-10-19
  • TM
    hi 老师您好,有个问题想咨询下。把 redis 的 backlog 设置为 1,然后在 redis 里 debug sleep 50,然后发起两个请求,一个成功连接,另一个会出 『opration timeout』 ,而不是 connect timeout,然后大概是 26 s ,反复试了几次、都是26s左右的时间。很奇怪这个报错是内核爆出来的吗?为什么是26s这个时间呢?扩展是 phpredis,php 底层 socket 超时是 60s。

    作者回复: 你好像问过一次了吧,这个我认为不是内核报出来的,我建议你debug一下。

    2019-10-18
收起评论
10
返回
顶部