背景

在文章《unix网络编程》(12)五种I/O模型中提到了五种I/O模型,其中前四种:阻塞模型、非阻塞模型、信号驱动模型、I/O复用模型都是同步模型;还有一种是异步模型。

想写一个系列的文章,介绍从I/O多路复用到异步编程和RPC框架,整个演进过程,这一系列可能包括:

  1. I/O多路复用模型
  2. epoll介绍与使用
  3. Reactor和Proactor模型
  4. 为什么需要异步编程
  5. enable_shared_from_this用法分析
  6. 网络通信库和RPC

为什么有多路复用?

多路复用技术要解决的是“通信”问题,解决核心在于“同步事件分离器”(de-multiplexer),linux系统带有的分离器select、poll、epoll网上介绍的比较多,大家可以看看这篇介绍的不错的文章:我读过的最好的epoll讲解。通信的一方想要知道另一方的状态(以决定自己做什么),有两种方法: 一是轮询,二是消息通知。

轮询

轮询的一种典型的实现可能是这样的:当然这里的epoll_wait()也可以使用poll()或者select()替换。

while (true) {
active_stream[] = epoll_wait(epollfd)
for i in active_stream[] {
read or write till
}
}

轮询方式主要存在以下不足:

  • 增加系统开销。无论是任务轮询还是定时器轮询都需要消耗对应的系统资源。
  • 无法及时感知设备状态变化。在轮询间隔内的设备状态变化只有在下次轮询时才能被发现,这将无法满足对实时性敏感的应用场合。
  • 浪费CPU资源。无论设备是否发生状态改变,轮询总在进行。在实际情况中,大多数设备的状态改变通常不会那么频繁,轮询空转将白白浪费CPU时间片。

消息通知

其实现方式通常是: "阻塞-通知"机制。阻塞会导致一个任务(task_struct,进程或者线程)只能处理一个"I/O流"或者类似的操作,要处理多个,就要多个任务(需要多个进程或线程),因此灵活性上又不如轮询(一个任务足够),很矛盾。

select、poll、epoll对比

矛盾的根源就是"一"和"多"的矛盾: 希望一个任务处理多个对象,同时避免处理阻塞-通知机制的内部细节。解决方案是多路复用(muliplex)。多路复用有3种基本方案,select()/poll()/epoll(),都是来解决这一矛盾的。

  • 通知代理: 用户把需要关心的对象注册给select()/poll()/epoll()函数。
  • 一对多: 所有的被关心的对象,只要有一个对象有了通知事件,select()/poll()/epoll()就会结束阻塞状态。
  • 方便性: 用户(程序员)不用再关心如何阻塞和被通知,以及哪些情况下会有通知产生。这件事情已经由上述几个系统调用做了,用户只需要实现"通知来了我该做什么"。

那么上面3个系统调用的区别是什么呢?
第一个select(),结合了轮询和阻塞两种方式,没有问题,每次有一个对象事件发生的时候,select()只是知道有事件发生了,具体是哪个对象发生的,不知道,需要从头到尾轮询一遍,复杂度是O(n)。poll函数相对select函数变化不大,只是提升了最大的可轮询的对象个数。epoll函数把时间复杂度降到O(1)。

为什么select慢而epoll效率高?
select()之所以慢,有几个原因: select()的参数是一个FD数组,意味着每次select调用,都是一次新的注册-阻塞-回调,每次select都要把一个数组从用户空间拷贝到内核空间,内核检测到某个对象状态变化并写入后,再从内核空间拷贝回用户空间,select再把这个数组读取一遍,并返回。这个过程非常低效。

epoll的解决方案相当于是一种对select()的算法优化: 它把select()一个函数做的事情分解成了3步,首先epoll_create()创建一个epollfd对象(相当于一个池子),然后所有被监听的fd通过epoll_ctrl()注册到这个池子,也就是为每个fd指定了一个内部的回调函数(这样,就没有了每次调用时的来回拷贝,用户空间的数组到内核空间只有这一次拷贝)。epoll_wait阻塞等待。在内核态有一个和epoll_wait对应的函数调用,把就绪的fd,填入到一个就绪列表中,而epoll_wait读取这个就绪列表,做到了快速返回(O(1))。

详细的对比可以参考select、poll、epoll之间的区别总结:https://www.cnblogs.com/Anker/p/3265058.html?spm=ata.13261165.0.0.4ec468f3ruw05F

有了上面的原理介绍,这里举例来说明下epoll到底是怎么使用的,加深理解。举两个例子:

一个是比较简单的父子进程通信的例子,单个小程序,不需要跑多个应用实例,不需要用户输入。https://www.cnblogs.com/goya/p/11925954.html
一个是比较实战的socket+epoll,毕竟现实案例中哪有两个父子进程间通讯这么简单的应用场景。

有了多路复用,难道还不够?

有了I/O复用,有了epoll已经可以使服务器并发几十万连接的同时,维持高TPS了,难道这还不够吗?答案是,技术层面足够了,但在软件工程层面却是不够的。例如,总要有个for循环去调用epoll,总来处理epoll的返回,这是每次都要重复的工作。for循环体里面写什么----通知返回之后,做事情的程序最好能以一种回调的机制,提供一个编程框架,让程序更有结构一些。另一方面,如果希望每个事件通知之后,做的事情能有机会被代理到某个线程里面去单独运行,而线程完成的状态又能通知回主任务,那么"异步"的进制就必须被引入。

所以,还有两个问题要解决,一是"编程框架",一是"异步"。我们先看几个目前流行的框架,大部分框架已经包含了某种异步的机制。我们接下来的篇章将介绍“编程框架”和“异步I/O模型”。

I/O多路复用模型的更多相关文章

  1. Java基础(一):I/O多路复用模型及Linux中的应用

    IO多路复用模型广泛的应用于各种高并发的中间件中,那么区别于其他模式他的优势是什么.其核心设计思想又是什么.其在Linux中是如何实现的? I/O模型 I/O模型主要有以下五种: 同步阻塞I/O:I/ ...

  2. 网络编程学习——Linux epoll多路复用模型

    前言 后端开发的应该都知道Nginx服务器,Nginx是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器.后端部署中一般使用的就是Nginx反向代理技术. ...

  3. linux下多路复用模型之Select模型

    Linux关于并发网络分为Apache模型(Process per Connection (进程连接) ) 和TPC , 还有select模型,以及poll模型(一般是Epoll模型) Select模 ...

  4. Linux网络通信编程(套接字模型TCP\UDP与IO多路复用模型select\poll\epoll)

    Linux下测试代码: http://www.linuxhowtos.org/C_C++/socket.htm TCP模型 //TCPClient.c #include<string.h> ...

  5. IO多路复用模型之select()函数详解

    IO复用 我们首先来看看服务器编程的模型,客户端发来的请求服务端会产生一个进程来对其进行服务,每当来一个客户请求就产生一个进程来服务,然而进程不可能无限制的产生,因此为了解决大量客户端访问的问题,引入 ...

  6. Nio学习3——基础模型:多路复用模型

    Reactor模式和NIO 本文可看成是对Doug Lea Scalable IO in Java一文的翻译. 当前分布式计算 Web Services盛行天下,这些网络服务的底层都离不开对socke ...

  7. 阻塞IO,非阻塞IO,IO多路复用模型

    #服务端 import socket sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen() while True: conn, ad ...

  8. IO多路复用模型之epoll实现机制

    设想一下如下场景:有100万个客户端同时与一个服务器进程保持着TCP连接.而每一时刻,通常只有几百上千个TCP连接是活跃的(事实上大部分场景都是这种情况).如何实现这样的高并发? 在select/po ...

  9. 浅谈网络I/O多路复用模型 select & poll & epoll

    http://blog.csdn.net/nk_test/article/details/50662946

随机推荐

  1. 百万年薪python之路 -- 模块三

    logging 日志模块 loggin模块参数 灵活配置日志级别,日志格式,输出位置: import logging logging.basicConfig(level=logging.DEBUG, ...

  2. Mutex vs Semaphore vs Monitor vs SemaphoreSlim

    C#开发者(面试者)都会遇到Mutex,Semaphore,Monitor,SemaphoreSlim这四个与锁相关的C#类型,本文期望以最简洁明了的方式阐述四种对象的区别. 线程安全 教条式理解 如 ...

  3. [NOI2001]食物链(并查集拓展域)&& [HAOI2006]旅行(Kruskal)

    题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的一种,但是我 ...

  4. java 时间戳 、时间差计算(秒、分钟、小时、天数、月份、年)

    以下代码就是时间差计算(秒.分钟.小时.天数.月份.年) package me.zhengjie; import java.text.ParseException; import java.text. ...

  5. Django实现WebSSH操作物理机或虚拟机

    我想用它替换掉xshell.crt之类的工具 WebSSH操作物理机或虚拟机 Django实现WebSSH操作Kubernetes Pod文章发布后,有小伙伴说咖啡哥,我们现在还没有用上Kuberne ...

  6. VMware问题--无法获得 VMCI 驱动程序的版本: 句柄无效

    关于VMware问题:无法获得 VMCI 驱动程序的版本: 句柄无效.驱动程序“vmci.sys”的版本不正确 问题截图: 解决 1.根据配置文件路径找到对应的.vmx文件: 2.用编辑器打开,找到v ...

  7. 《JavaScript设计模式与开发实践》-- 迭代器模式

    详情个人博客:https://shengchangwei.github.io/js-shejimoshi-diedaiqi/ 迭代器模式 1.定义 迭代器模式: 是指提供一种方法顺序访问一个聚合对象中 ...

  8. 正则表达式和python中的re模块

    ---恢复内容开始--- 常用的正则匹配规则 元字符 量词 字符组 字符集 转义符 贪婪匹配 re模块使用正则表达式 实例引入(是否使用re模块和正则表达式的区别) # 不使用正则表达式 phone_ ...

  9. CentOS6.5下安装JDK1.7+MYSQL5.5+TOMCAT7+nginx1.7.5环境安装文档

    ----------------CentOS6.5下安装JDK1.7+MYSQL5.5+TOMCAT7+nginx1.7.5环境安装文档----------------------- [JDK1.7安 ...

  10. lua程序设计(一)

    摘要:lua程序设计第二版学习笔记 脚本语言的基础语法大都比较简单,这里只列举一些lua独有,或者需要特别注意的语法点. 书中前三章的内容是一些惯常的引言,基础数据类型,运算符等内容,相对简单,这里就 ...