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

注:之前写过两篇关于epoll实现的文章,但是感觉懂得了实现原理并不一定会使用,所以又决定写这一系列文章,希望能够对epoll有比较清楚的认识。是请大家转载务必注明出处,算是对我劳动成果的一点点尊重吧。另外,文中如果有不全面或者不正确的地方还请大家指出。也可以私信或者发邮件:lvyilong316@163.com

1. ET模式实现分析

1.1 ET和LT的实现区别

    首先给出下面一张图,这张图是从我之前的一篇博文——epoll实现分析中摘取并细化的。这张图对理解ET模式已经epoll的工作过程只管重要,当然我自己总结出来后也感觉有的小成就,在这里与大家分享。

注:上图的poll不要理解成和select相似那个poll,这是通过epoll_ctl调用的。

下面简要分析一下epoll的工作过程:

(1) epoll_wait调用ep_poll,当rdlist为空(无就绪fd)时挂起当前进程,知道rdlist不空时进程才被唤醒。

(2) 文件fd状态改变(buffer由不可读变为可读或由不可写变为可写),导致相应fd上的回调函数ep_poll_callback()被调用。

(3) ep_poll_callback将相应fd对应epitem加入rdlist,导致rdlist不空,进程被唤醒,epoll_wait得以继续执行。

(4) ep_events_transfer函数将rdlist中的epitem拷贝到txlist中,并将rdlist清空。

(5) ep_send_events函数(很关键),它扫描txlist中的每个epitem,调用其关联fd对用的poll方法(图中蓝线)。此时对poll的调用仅仅是取得fd上较新的events(防止之前events被更新),之后将取得的events和相应的fd发送到用户空间(封装在struct epoll_event,从epoll_wait返回)。之后如果这个epitem对应的fd是LT模式监听且取得的events是用户所关心的,则将其重新加入回rdlist(图中蓝线),否则(ET模式)不在加入rdlist。

具体代码:

/* 扫描整个txlist链表... */

for (eventcnt = 0, uevent = esed->events;

     !list_empty(head) && eventcnt < esed->maxevents;) {

/* 取出第一个成员 */

epi = list_first_entry(head, struct epitem, rdllink);

/* 然后从链表里面移除 */

list_del_init(&epi->rdllink);

/* 读取events, 

 * 注意events我们ep_poll_callback()里面已经取过一次了, 为啥还要再取?

 * 1. 我们当然希望能拿到此刻的最新数据, events是会变的~

 * 2. 不是所有的poll实现, 都通过等待队列传递了events, 有可能某些驱动压根没传

 * 必须主动去读取. */

revents = epi->ffd.file->f_op->poll(epi->ffd.file, NULL) &

epi->event.events;

 

if (revents) {

/* 将当前的事件和用户传入的数据都copy给用户空间,

 * 就是epoll_wait()后应用程序能读到的那一堆数据. */

if (__put_user(revents, &uevent->events) ||

    __put_user(epi->event.data, &uevent->data)) {

/* 如果copy过程中发生错误, 会中断链表的扫描,

 * 并把当前发生错误的epitem重新插入到ready list.

 * 剩下的没处理的epitem也不会丢弃, 在ep_scan_ready_list()

 * 中它们也会被重新插入到ready list */

list_add(&epi->rdllink, head);

return eventcnt ? eventcnt : -EFAULT;

}

eventcnt++;

uevent++;

if (epi->event.events & EPOLLONESHOT)

epi->event.events &= EP_PRIVATE_BITS;

else if (!(epi->event.events & EPOLLET)) {

/*

 * If this file has been added with Level

 * Trigger mode, we need to insert back inside

 * the ready list, so that the next call to

 * epoll_wait() will check again the events

 * availability. At this point, noone 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.

 */

/* 嘿嘿, EPOLLET和非ET的区别就在这一步之差呀~

 * 如果是ET, epitem是不会再进入到readly list,

 * 除非fd再次发生了状态改变, ep_poll_callback被调用.

 * 如果是非ET, 不管你还有没有有效的事件或者数据,

 * 都会被重新插入到ready list, 再下一次epoll_wait

 * 时, 会立即返回, 并通知给用户空间. 当然如果这个

 * 被监听的fds确实没事件也没数据了, epoll_wait会返回一个0,

 * 空转一次.

 */

list_add_tail(&epi->rdllink, &ep->rdllist);

}

}

}

说明:

l epoll_wait返回的条件是rdlist不空,而使rdlist不空的途径有两个,分别对应图中的红线和蓝线。

l ET和LT模式下的epitem都可以通过红线方式加入rdlist从而唤醒epoll_wait,但LT模式下的epitem还可以通过蓝线方式重新加入rdlist唤醒epoll_wait。所以ET模式下,fd就绪(通过红线加入rdlist)只会被通知一次,而LT模式下只要满足相应读写条件就返回就绪(通过蓝线加入rdlist)。

l ET事件发生仅通知一次的原因是只被添加到rdlist中一次,而LT可以有多次添加的机会。

1.2 两种加入rdlist途径的不同

下面我们来分析一下图中两种将epitem加入rdlist方式(也就是红线和蓝线)的区别。

l 红线:fd状态改变是才会触发。那么什么情况会导致fd状态的改变呢?

对于读取操作:

(1) 当buffer由不可读状态变为可读的时候,即由空变为不空的时候。

(2) 当有新数据到达时,即buffer中的待读内容变多的时候。

对于写操作:

(1) 当buffer由不可写变为可写的时候,即由满状态变为不满状态的时候。

(2) 当有旧数据被发送走时,即buffer中待写的内容变少得时候。

 

l 蓝线:fd的events中有相应的时间(位置1)即会触发。那么什么情况下会改变events的相应位呢?

对于读操作:

(1) buffer中有数据可读的时候,即buffer不空的时候fd的events的可读为就置1。

对于写操作:

(1) buffer中有空间可写的时候,即buffer不满的时候fd的events的可写位就置1。

 

说明:红线是时间驱动被动触发,蓝线是函数查询主动触发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384936.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OPENSSL X509证书验证

openssl实现了标准的x509v3数字证书&#xff0c;其源码在crypto/x509和crypto/x509v3中。其中x509目录实现了数字证书以及证书申请相关的各种函数&#xff0c;包括了X509和X509_REQ结构的设置、读取、打印和比较&#xff1b;数字证书的验证、摘要&#xff1b;各种公钥的导入导出…

linux网络编程九:splice函数,高效的零拷贝

1. splice函数 #include <fcntl.h> ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags); splice用于在两个文件描述符之间移动数据&#xff0c; 也是零拷贝。 fd_in参数是待输入描述符。如果它是一个管道文件…

sys/queue.h

概述 sys/queue.h是LINUX/UNIX系统下面的一个标准头文件&#xff0c;用一系列的数据结构定义了一队列。包括singly-lined list, list, simple queue(Singly-linked Tail queue), tail queue, circle queue五种。 引用此头文件对这五种数据结构的描述&#xff1a; A singly-lin…

sys/queue.h分析(图片复制不过来,查看原文)

这两天有兴趣学习使用了下系统头文件sys/queue.h中的链表/队列的实现&#xff0c;感觉实现的很是优美&#xff0c;关键是以后再也不需要自己实现这些基本的数据结构了&#xff0c;哈哈&#xff01; 我的系统环境是 正好需要使用队列&#xff0c;那么本篇就以其中的尾队列&…

线程池原理及C语言实现线程池

备注&#xff1a;该线程池源码参考自传直播客培训视频配套资料&#xff1b; 源码&#xff1a;https://pan.baidu.com/s/1zWuoE3q0KT5TUjmPKTb1lw 密码&#xff1a;pp42 引言&#xff1a;线程池是一种多线程处理形式&#xff0c;大多用于高并发服务器上&#xff0c;它能合理有效…

iptables 的mangle表

mangle表的主要功能是根据规则修改数据包的一些标志位&#xff0c;以便其他规则或程序可以利用这种标志对数据包进行过滤或策略路由。 内网的客户机通过Linux主机连入Internet&#xff0c;而Linux主机与Internet连接时有两条线路&#xff0c;它们的网关如图所示。现要求对内网进…

Linux常用命令(一)

history 查看历史命令 ctrlp 向上翻历史纪录 ctrln 向下翻历史纪录 ctrlb 光标向左移动 ctrlf 光标向右移动 ctrla 光标移动到行首 ctrle 光标移动到行尾 ctrlh 删除光标前一个 ctrld 删除光标后一个 ctrlu 删除光标前所有 ctrlL clear命令 清屏 tab键可以补全命令/填充路径…

ip route / ip rule /iptables 配置策略路由

Linux 使用 ip route , ip rule , iptables 配置策略路由 要求192.168.0.100以内的使用 10.0.0.1 网关上网&#xff0c;其他IP使用 20.0.0.1 上网。 首先要在网关服务器上添加一个默认路由&#xff0c;当然这个指向是绝大多数的IP的出口网关。 ip route add default gw 20.0.0.…

iptables:tproxy做透明代理

什么是透明代理 客户端向真实服务器发起连接&#xff0c;代理机冒充服务器与客户端建立连接&#xff0c;并以客户端ip与真实服务器建立连接进行代理转发。因此对于客户端与服务器来说&#xff0c;代理机都是透明的。 如何建立透明代理 本地socket捕获数据包 nat方式 iptables…

编译参数(-D)

程序中可以使用#ifdef来控制输出信息 #include<stdio.h> #define DEBUGint main() {int a 10;int b 20;int sum a b; #ifdef DEBUGprintf("%d %d %d\n",a,b,sum); #endifreturn 0; } 这样在有宏定义DEBGU的时候就会有信息输出 如果注销掉宏定义就不会有输…

libpcap讲解与API接口函数讲解

ibpcap&#xff08;Packet Capture Library&#xff09;&#xff0c;即数据包捕获函数库&#xff0c;是Unix/Linux平台下的网络数据包捕获函数库。它是一个独立于系统的用户层包捕获的API接口&#xff0c;为底层网络监测提供了一个可移植的框架。 一、libpcap工作原理 libpcap…

Linux常用命令(三)

man 查看帮助文档 alias ls : 查看命令是否被封装 echo &#xff1a; 显示字符串到屏幕终端 echo $PATH : 将环境变量打印出来 poweroff&#xff1a;关机 rebot&#xff1a;重启 需要管理员权限 vim是从vi发展过来的文本编辑器 命令模式&#xff1a;打开文件之后默认进入命令模…

浅谈iptables防SYN Flood攻击和CC攻击

何为syn flood攻击&#xff1a; SYN Flood是一种广为人知的DoS&#xff08;拒绝服务攻击&#xff09;是DDoS&#xff08;分布式拒绝服务攻击&#xff09;的方式之一&#xff0c;这是一种利用TCP协议缺陷&#xff0c;发送大量伪造的TCP连接请求&#xff0c;从而使得被攻击方资源…

Linux之静态库

命名规则&#xff1a; lib 库的名字 .a 制作步骤 生成对应.o文件 .c .o 将生成的.o文件打包 ar rcs 静态库的名字&#xff08;libMytest.a&#xff09; 生成的所有的.o 发布和使用静态库&#xff1a; 1&#xff09; 发布静态 2&#xff09; 头文件 文件如下图所示&…

iptables详解和练习

防火墙&#xff0c;其实说白了讲&#xff0c;就是用于实现Linux下访问控制的功能的&#xff0c;它分为硬件的或者软件的防火墙两种。无论是在哪个网络中&#xff0c;防火墙工作的地方一定是在网络的边缘。而我们的任务就是需要去定义到底防火墙如何工作&#xff0c;这就是防火墙…

Linux之动态库

命令规则 lib 名字 .so 制作步骤 1&#xff09;生成与位置无关的代码&#xff08;生成与位置无关的代码&#xff09; 2&#xff09;将.o打包成共享库&#xff08;动态库&#xff09; 发布和使用共享库 动态库运行原理&#xff1a; 生成动态库&#xff1a; gcc -fPIC -c *.c -…

linux下源码安装vsftpd-3.0.2

1&#xff09;在http://vsftpd.beasts.org/网站中查找并下载 vsftpd-3.0.2.tar.gz源码包 2)如果自己的机器上安装有yum可以用yum grouplist | less指令查看以下开发环境&#xff0c;当然这一步不做也行 3&#xff09;拆解源码包 4&#xff09;查看源码包 5&#xff09;编辑…

Linux之GDB调试命令

gdb启动 gdb 程序名 l 查看源代码&#xff08;默认显示十行&#xff09; l 文件名&#xff1a;行数 l 文件名&#xff1a;函数名 添加断点 break 行数 &#xff08;b 也行&#xff09; b 15 if i 15 条件断点 i b 查看断点信息 start 程序执行一步 n 单步调试 s 单步&#xf…

Gdb 调试core文件详解

一&#xff0c;什么是coredump 我们经常听到大家说到程序core掉了&#xff0c;需要定位解决&#xff0c;这里说的大部分是指对应程序由于各种异常或者bug导致在运行过程中异常退出或者中止&#xff0c;并且在满足一定条件下&#xff08;这里为什么说需要满足一定的条件呢&#…

Linux之GDB命令(二)

gdb命令&#xff1a; 前提条件&#xff1a;可执行文件必须包含调试信息 gcc -ggdb 文件名 –启动gdb调试查看代码命令 当前文件&#xff1a; list 行号&#xff08;函数名&#xff09; 指定文件&#xff1a; list 文件名&#xff1a;行号&#xff08;函数名&#x…