linux网络编程 - epoll边沿触发/水平触发内核实现代码分析

1、listen socket水平触发的poll函数调用

        以服务器端epoll为例,加入监听、等待并接受连接、再次等待,会有3次检查是否有连接就绪的操作,分别是epoll_ctl、epoll_wait、epoll_wait。

1.1、epoll_wait(第1次调用)

        等待就绪链表相关内核看前面发布的文章《linux网络编程 - epoll内核实现代码分析》。这里介绍水平触发下,如果还有等待接受的连接,epoll_wait是如何触发或者说获取到读事件的。

        ep_send_events_proc代码:

        如上图代码1533行,这行代码将就绪epitem从就绪链表删掉,然后调用ep_item_poll检查获取该epitem的就绪事件,监听的就绪事件保存到revents,如果没有就绪的监听事件,那么revents就为0,1543行就是检查epitem是否有就绪事件,如果没有,那么if分支代码就不会执行(if代码块之后没有代码将epitem再次加入就绪链表),下次再次调用epoll_wait就不会再检查该epitem。

        接下来看1543行if分支里面的代码,有就绪的监听事件就调用__put_user拷贝事件到用户态,最后判断触发方式,ep_send_events_proc代码如下所示:

         1554行检查是否是边沿触发EPOLLET,如果不是边沿触发,就执行if里面的代码,调用list_add_tail将epitem再次加入到就绪链表里面,也就是水平触发模式,epoll_wait获取到就绪事件之后并不会将就绪事件删除,用户程序获取到就绪事件之后,不管是接受所有连接还是接受部分连接,listen的socket还是"可读"的。

1.2、epoll_wait(第2次调用)

        epoll_wait调用返回之后,用户程序可以不处理事件,也可以只接受部分连接,然后再次调用epoll_wait:

  • 如果用户程序接受所有连接之后再次调用epoll_wait,此时epitem虽然还在就绪链表里面,但是实际上epitem是不可读的,因为epitem还在就绪链表里面,所以内核还会再次调用ep_item_poll对socket事件再次进行检查,正如前面介绍的内核会先将epitem从就绪链表删除,因为当前epitem实际上不可读,所以不会返回就绪事件,也就是revents为0,那么epitem就不会被再次加入就绪链表,也就是水平触发的epoll_wait多做了一次无用的检查;
  • 如果用户程序没有接受连接或者接受部分连接,那么再次调用epoll_wait的时候epitem自然是就绪的,也就是前面为什么获取到就绪事件之后,还要再次将epitem加入到就绪链表的原因。

2、listen socket边沿触发的poll函数调用

        边沿触发在前面实际已经介绍到了,水平触发epitem从就绪链表删除之后还会再添加到就绪链表,但是边沿触发就不会,如果epoll_wait获取事件之后不读取连接或者读取部分连接,那么再次调用epoll_wait,此时epitem已经不再就绪链表里面了,那么就检查不到就绪事件(虽然此时有连接等待接受),从前面的文章《linux网络编程 - epoll内核实现代码分析》可以看到,如果有新的连接,那么会调用sock_def_readable,sock_def_readable一级级调用下去就会检查epitem是否在就绪链表里面,如果不在,那么就会添加到就绪链表并唤醒阻塞线程,

        ep_poll_callback添加就绪链表唤醒阻塞线程代码如下:

        函数调用栈如下:

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值