29 | 渐入佳境:使用epoll和多线程模型
29 | 渐入佳境:使用epoll和多线程模型
讲述:冯永吉
时长08:06大小7.41M
如何切换到 epoll
样例程序
样例程序结果
epoll 的性能分析
总结
思考题
赞 7
提建议
精选留言(22)
- 沉淀的梦想2019-10-15在ET的情况下,write ready notification只会在套接字可写的时候通知一次的话,那个时候应用还没准备好数据,等到应用准备好数据时,却又没有通知了,会不会导致数据滞留发不出去?这种情况是怎么解决的呢?
作者回复: 你可以再次注册这个write ready的事件啊,不是说只能注册一次就结束了,而是你注册了一次,它就通知你一次;而LT的情况下,可能你注册了一次,它通知你好多次。
17 - LiYanbin2020-01-29源代码看起来有点花了点时间,将这部分的代码从抽离了出来,便于大家跟踪代码理解,同时写了简单的makefile。代码地址:https://github.com/kevinrsa/epoll_server_multithreads 。如有不妥,联系删除
作者回复: makefile写得不错:)
12 - JJ2019-10-14边缘条件,当套接字缓冲区可写,会不断触发ready notification事件,不是应该条件触发才是这样吗?
作者回复: 笔误,已经让编辑勘误了,感谢指正。
共 2 条评论7 - rongyefeng2020-05-22如果应用程序只读取了 50 个字节,边缘触发就会陷入等待; 这里的陷入等待是什么意思呢
作者回复: 不会再继续发送read_notification事件,必须等所有的100个字节被读完,才会发送下一个read_notification事件。
共 2 条评论5 - 张三说2019-12-13老师,一直没搞懂ET和LT的性能区别,仅仅因为LT会多提醒一些次数就与ET相差明显的性能吗?一直很纠结这个问题
作者回复: 有没有跑例子程序呢?其实不用纠结,最新的测试表明,两者差别其实没有那么大。但是非要比一个差距的话,ET还是效率好一些,但是对应用程序开发者的要求高一些。
共 2 条评论5 - 流浪地球2019-10-17细读了下老师git上的代码,套接字都是设置为非阻塞模式的,但并没有对返回值做判断处理,看上去好像是阻塞式的用法,求解?
作者回复: 可能是考虑不周,有可能的话麻烦提一个MR或者issue,大家一起来改。
4 - 郑祖煌2020-07-0827章以及以后源代码的难度提升了一个等级了。看了相当吃力呀。
作者回复: 多读两遍会好很多
共 2 条评论1 - Joker2020-04-16老师,这个就绪列表是建立在事件集合之上的对吧。
作者回复: 是的,是所有感兴趣的事件集合。
1 - ray2020-04-12老师好, 针对第2题,目前想到onMessage函数应该要注意,如果当前程序无法处理该通知,应该要想办法再次注册该事件。 只是具体程序实现就不知道应该怎么写了,可能还要请老师说明一下 哈哈XD 谢谢老师^^展开
作者回复: 当前的实现并不会主动把I/O读写事件从事件通道上摘除哦,所以并不需要重新注册该事件,onMessage就是一个简单的报文解析函数,所要做到的就是在条件触发情况下读完所有的字节,避免不断的再次被事件驱动。
共 2 条评论1 - 丁小明2020-03-10为什么 socket已经有缓冲区了,应用层还要缓冲区呢,比如发送,socket也会合并发送
作者回复: 很简单,应用层需要对接收到的byte字节流进行编解码,为了方便,在应用层进行缓冲,之后进行编解码的操作,再送给业务逻辑层来处理。
共 2 条评论1 - 传说中的成大大2019-10-16看到CMake我就完全懵逼。。。。
作者回复: 还好吧,看一下CMake的文档,以前我一直用的Makefile, CMake也是现学的。
1 - Steiner2019-10-14老师能不能为这个框架写一份README.md,我对这个实现很感兴趣
作者回复: 你需要什么样的README.md呢?第四篇会详细讲解这个框架的设计,也行你读完之后,可以写一个README.md push到git上呢?
共 3 条评论1 - P2023-02-12 来自浙江只提一点,所有关于Reactor的图片都不太准确。流程应该是client->Acceptor->Poller(select/poll/epoll),然而文章中所有的Acceptor都放在了后面,令人疑惑。
- Running man2022-09-30 来自浙江event_loop.c编译链接不上pthread库,有哪位朋友知道如何修改cmakelist,gcc版本是11.2.0 ubuntu系统版本是11.2.0,对应内核版本5.15.0-41
- vv_test2021-06-28性能对比第一点,是否可以这样理解。select、poll在用户态声明的事件拷贝(我在这里理解拷贝,不是注册,因为下一次调用依旧要传入)到内核态,大量操作copy的情况下耗时不容小觑。而epoll是已经注册到对应的epoll实例。主要是省去了这个copy的时间
作者回复: 嗯,也是有这方面的考虑,不过更多的还是事件处理的机制和效率的问题。
- Steiner2021-02-18有个疑问,这个程序与下一章的HTTP服务器的设计,处理连接的时候,服务器什么时候会关闭对端的连接? 是不断与客户端交互,客户端发送关闭请求才关闭;还是处理完客户端的请求后,发送响应,再关闭
作者回复: 第一种。代码如下: int handle_read(void *data) { struct tcp_connection *tcpConnection = (struct tcp_connection *) data; struct buffer *input_buffer = tcpConnection->input_buffer; struct channel *channel = tcpConnection->channel; if (buffer_socket_read(input_buffer, channel->fd) > 0) { //应用程序真正读取Buffer里的数据 if (tcpConnection->messageCallBack != NULL) { tcpConnection->messageCallBack(input_buffer, tcpConnection); } } else { handle_connection_closed(tcpConnection); } }
共 2 条评论 - нáпの゛2020-09-10老师,所以不删除写事件,就不需要重新注册是吗?每次缓冲区由满变成可写都会通知一次,是这样理解吗?
作者回复: 是的,这样的写效率会变低。
- fedwing2020-08-17第一个角度是事件集合。在每次使用 poll 或 select 之前,都需要准备一个感兴趣的事件集合,系统内核拿到事件集合,进行分析并在内核空间构建相应的数据结构来完成对事件集合的注册。而 epoll 则不是这样,epoll 维护了一个全局的事件集合,通过 epoll 句柄,可以操纵这个事件集合,增加、删除或修改这个事件集合里的某个元素。要知道在绝大多数情况下,事件集合的变化没有那么的大,这样操纵系统内核就不需要每次重新扫描事件集合,构建内核空间数据结构。 老师,这个不是很理解,看了下,前面的epoll实例代码,epoll_wait时,还是需要传入一个events(看起来是初始化了下)的,这个是做什么用的,我理解,epoll对象本身不是已经有它所关联的事件信息了吗(通过epoll_ctrl add进去)展开
作者回复: epoll_wait返回给用户空间需要处理的 I/O 事件,用这个events来表示,这样我们才知道具体发生了什么事件。具体的例子可以参考第23章。
- fedwing2020-08-17老师,请问下,我看用poll实现里的结构配图,可以用threadpool来解耦具体业务逻辑,epoll里的配图,没有这个,其实也是可以加的吧,本质上线程池解耦业务这部分应该是通用吧,只是在事件触发, 事件分发机制上的差别吧?
作者回复: 是的,完全正确。
- 林林2019-12-02文稿中的框架示意图,我看到main reactor 和 sub reactor都各自运行了epoll,请问是否各自处理不同的socket? 如果处理了相同的socket会发生什么吗?
作者回复: main reactor处理的是监听套接字上的事件,sub reactor处理的是已连接套接字上的事件,两个是不重合的。 如果处理了相同的socket,那么肯定需要通过锁-并发来控制,无形中就增加了处理的开销,降低了程序处理的效率。