linux中poll系统调用实现了对文件描述符的轮询,由于poll的实现问题,每当一个或者多个文件描述符上有事件发生的时候,poll的核心并没有什么好的办法可以知道到底是哪些文件描述符上发生了事件,于是不得不采用遍历所有的fd_set中的文件描述符的办法,但是这种方式很低效,如果有很多的描述符但是只有最后一个上发生了事件,那么将会消耗很多的时间,于是出现了epoll,epoll本质就是应用唤醒回调函数,只将被唤醒的wait队列元素加入到一个表中,然后只需要遍历该表的元素就可以了,如果还是上面的情况,那么只有一个wait元素被加入到表中,只要在这个表中的元素上poll一下就完成了。

事情到此就结束了吗?没有!现有的epoll已经定位到了发生事件的具体的文件描述符,但是一个描述符上可以监控很多的事件,如果监控的是read,然而write事件到来的时候也会将该描述符唤醒,那么按照epoll的设计思想,是否可以定位到事件呢?是的,可以,这就是keyed-epoll补丁的思想,但是在引入这个补丁之前linux的方式就是一步一步来,先在传统的poll机制上实现keyed扩展,这样就做到了影响最小化,keyed就可以从一个扩展抽象成了一个机制,它就不再和poll机制绑定,其实它也没有必要和poll机制绑定,于是单纯的keyed补丁就被提出来了,它就是实现了另外一套唤醒函数,这个系列函数的参数中添加了一个key参数,也就是加了一层判断,只有当key值符合一定条件时才会进行真正的唤醒,具体怎么判断key的逻辑,就由用户来制定。千言万语敌不过几行代码:

-static int pollwake(wait_queue_t *wait, unsigned mode, int sync, void *key)

+static int __pollwake(wait_queue_t *wait, unsigned mode, int sync, void *key)

{

struct poll_wqueues *pwq = wait->private;

DECLARE_WAITQUEUE(dummy_wait, pwq->polling_task);

@@ -194,6 +194,16 @@ static int pollwake(wait_queue_t *wait, unsigned mode, int sync, void *key)

return default_wake_function(&dummy_wait, mode, sync, key);

}

+static int pollwake(wait_queue_t *wait, unsigned mode, int sync, void *key)

+{

+ struct poll_table_entry *entry;

+

+ entry = container_of(wait, struct poll_table_entry, wait);

+ if (key && !((unsigned long)key & entry->key)) //如果事件与该entry无关,就不再执行唤醒操作

+ return 0;

+ return __pollwake(wait, mode, sync, key);

+}

+

/* Add a new entry */

static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,

poll_table *p)

@@ -205,6 +215,7 @@ static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,

get_file(filp);

entry->filp = filp;

entry->wait_address = wait_address;

+ entry->key = p->key;

init_waitqueue_func_entry(&entry->wait, pollwake);

entry->wait.private = pwq;

add_wait_queue(wait_address, &entry->wait);

@@ -418,8 +429,16 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)

if (file) {

f_op = file->f_op;

mask = DEFAULT_POLLMASK;

- if (f_op && f_op->poll)

+ if (f_op && f_op->poll) {

+ if (wait) { //在进行调用vfs的poll之前,先将需要监控的事件加入到key,在唤醒的时候要作为参考

+ wait->key = POLLEX_SET;

+ if (in & bit)

+ wait->key |= POLLIN_SET;

+ if (out & bit)

+ wait->key |= POLLOUT_SET;

+ }

mask = (*f_op->poll)(file, retval ? NULL : wait);

+ }

fput_light(file, fput_needed);

if ((mask & POLLIN_SET) && (in & bit)) {

res_in |= bit;

这个补丁正如作者所说,节省了不少开销,避免了不少唤醒操作,我个人认为,它的意义要比epoll还要大。如果一个进程监控fd1的read事件,另一个进程监控fd1的write事件,那么这两个进程都要加入到该fd1的vfs底层的的wait队列中,一旦fd1上发生事件,则这两个进程都要被唤醒而不管是什么事件。打上这个补丁之后,如果是read事件,那么就只用唤醒监控read事件的那个进程就可以了,节省了一半的唤醒动作,唤醒操作是一件很耗时的操作,因为涉及到抢占和切换,特别是毫无意义的唤醒更是要避免的,没有事情无故唤醒别人是一件很不好的事,这个补丁就是细化了事件检测机制。如果将这个机制加入到epoll中,那更是如虎添翼,不但精确到了文件描述符,更是精确到了文件描述符的事件,在文件描述符之下再检测一个事件,这里该补丁和epoll的区别在于epoll节省了轮询遍历的开销但是避免不了唤醒,而keyed机制节省不了轮询但是可以最小化唤醒操作。传统的poll一旦被唤醒之后必须遍历所有poll列表的文件描述符从而确定哪一个上有事件发生,而epoll不用;传统的poll在底层,只要有事件发生就会唤醒其睡眠队列的所有进程而不管进程是否关心该事件,而keyed机制可以避免这种鲁莽。两个机制在诸多文件描述符和诸多事件中定位到了一个文件描述符的一个事件,可谓妙。

最后看一个linux中的层次问题,总有人说linux没有实现内核级别的线程而只有进程,可是clone中克隆的是什么?是task_struct,task_struct是什么?是进程吗?不是,是线程吗?不是,那么它是什么?它是进程和线程的超集,它比进程和线程的层次要高,包含进程和线程而不能说它是进程或者线程,因此不要按照windows中线程的意义来理解linux的实现,linux的架构其实很松散,打破了传统操作系统理论对操作系统的规定。linux中统一的进程线程实现方式确实很好,很灵活,做到了正交化,意义就是进程和线程不再具有从属关系,而是没有关系,linux内核中没有定义进程和线程,只有task_struct,如果一个task_struct独享资源,那么它就是进程,如果很多task_struct共享资源,那么每个task_struct就是一个线程然后它们组成一个进程,到底是什么由是否共享资源这个第三方的开关而不是它们本身来定义,这样很不错,将内涵和外延分离开来。linux的方式可以实现很多的进程/线程的实现方式,这种低耦合高内聚的正交化机制是很强大的。内涵没有意义,就是一个task_struct,加上一个有意义的“资源使用方式”这个第三方的策略就构成了外延--进程和线程

本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1273488

poll/epoll/keyed-poll/keyed-epoll的唤醒--分层次的解决方案相关推荐

  1. Linux下多路复用IO接口epoll/select/poll的区别

    select比epoll效率差的原因:select是轮询,epoll是触发式的,所以效率高. Select: 1.Socket数量限制:该模式可操作的Socket数由FD_SETSIZE决定,内核默认 ...

  2. epoll和poll的C++11多线程练习

    一个epoll和poll的多线程练习 https://github.com/StudentErick/EpollServerTask

  3. Java网络编程与NIO详解13:epoll、poll、select面试题汇总

    文章目录 一.文件描述符与IO模型 二.端口和地址复用 三.select 四.poll 五.epoll 六.相关面试题 1.epoll读到一半又有新事件来了怎么办? 一.文件描述符与IO模型   文件 ...

  4. netty的epoll和linux的epoll是如何实现的

    1.linux的epoll epoll 是Linux内核中的一种可扩展IO事件处理机制,最早在 Linux 2.5.44内核中引入,可被用于代替POSIX select 和 poll 系统调用,并且在 ...

  5. 处理大并发之一 对epoll的理解,epoll客户端服务端代码

    http://blog.csdn.net/zhuxiaoping54532/article/details/56494313 处理大并发之一 对epoll的理解,epoll客户端服务端代码 序言: 该 ...

  6. 处理大并发之二 对epoll的理解,epoll客户端服务端代码

    http://blog.csdn.net/wzjking0929/article/details/51838370 序言: 该博客是一系列的博客,首先从最基础的epoll说起,然后研究libevent ...

  7. 新版本glib使用epoll代替poll

    新版本的glib支持使用外部的事件循环代替内部的poll,这篇文章使用的glib版本是V2.72.0, 理解还很粗浅,但是demo能跑起来,还需要再详细研究一下参考的两个链接,多线程下使用及效率是怎样 ...

  8. 【epoll】形象的解释epoll|一句话讲透epoll

    目录 形象的解释epoll| 多路复用 一句话讲透epoll 1. epoll概念 2. 水平触发与边缘触发 3. epoll接口介绍 形象的解释epoll| 转自:https://blog.csdn ...

  9. linux线程同步 epoll,Linux网络编程--epoll 模型原理详解以及实例

    1.简介 Linux I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数.Linux 2.6内核中有提高网络I/O性能的新方法,即epoll . epoll是什么?按 ...

最新文章

  1. 你必须掌握的 21 个 Java 核心技术!
  2. 30个 Web 设计者 必备的免费 PSD UI 工具包
  3. metasploit 快速入门(二)信息收集和扫描-续
  4. 【EventBus】事件通信框架 ( 发送事件 | 判断发布线程是否是主线程 | 子线程切换主线程 | 主线程切换子线程 )
  5. www.javaei.com网站建设手记——(15)h2p被开源中国收录为开源项目
  6. python爬取今日头条的文章_Python3爬取今日头条有关《人民的名义》文章
  7. Spring基础——AOP
  8. MogDB/openGauss 手动部署(非OM工具)单机、主备、主备级联架构
  9. Java Thread等待,通知和notifyAll示例
  10. TCP协议的三次握手+四次断开
  11. MySQL redhat7 安装mysql8
  12. 死磕Mosek!新mosek学习笔记1:VS项目配置。
  13. 用vbs写九九乘法表
  14. 18-HTML标签的居中
  15. 记一次某制造业ERP系统 CPU打爆事故分析
  16. 地图可视化 - 气泡点图
  17. Python实现图片黑白化
  18. 满足4G/5G基站覆盖测试、频谱扫描和清频测试功能的扫频仪 TFN FGT系列扫频仪
  19. Unity shader新手入门教程:实现汽车氮气加速特效
  20. 利用jQuery定制日历(含时分秒时区功能)

热门文章

  1. 取消预约的c语言代码大全,C语言机房机位预约系统课设(附源码).doc
  2. 一个社交电商小程序配套的平台接
  3. DDOS压力测试系统
  4. 两个SEO技巧让你的网站排名靠前
  5. Centos7访问本地电脑共享文件夹遇到的问题
  6. tensorflow代码中的tf.app.run()
  7. 神奇的python(四)之logging日志文件系统
  8. 匿名内部类编译时生成多个class文件
  9. 【AI视野·今日Robot 机器人论文速览 第八期】Wed, 16 Jun 2021
  10. 【今日CS 视觉论文速览】Thu, 6 Dec 2018