背景

在文章《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. php能在手机上运行吗

    php能在手机上运行吗? php能在手机上运行.但是需要你的手机具有PHP运行环境. 比如一款安卓手机上面的php运行环境软件:anmpp. ANMPP是Android NGINX MYSQL PGS ...

  2. C#方法的定义、调用与调试

    本节内容 1.方法的由来: 2.方法的定义与调用: 3.构造器(一种特殊的方法): 4.方法的重载(Override): 5.如何对方法进行debug: 6.方法的调用与栈* *推荐书目:CLR vi ...

  3. ASP.NET Core中的配置

    配置 参考文件点击跳转 配置来源 命令行参数 自定义提供程序 目录文件 环境变量 内存中的.NET 对象 文件 默认配置 CreateDefaultBuilder方法提供有默认配置,在这个方法中会接收 ...

  4. 用FILE*指针对象读文件的方式。

      先直接上代码: ]; ]; char* ptr1; ]; FILE* fh; QString strpath = getenv("GCDIR"); QString str_in ...

  5. Markdown进阶(1)

    对于工科生来说,在书写Markdown文本时,免不了要和上下标打交道,网上的博客大多良莠不齐,不太友好,本文想尽可能地解决一些在看完基础教程后再来书写Markdown文本时容易遇到的问题. 1.上下标 ...

  6. django-数据库之连接数据库

    1.连接数据库出现的一些问题 基本目录如下: 首先我们pip install pymysql 然后在项目中,进行配置settings.py: 然后在__init__.py中进行输入: 启动服务器: 报 ...

  7. 【C#多线程】2.线程池简述+两种传统的异步模式

    线程池简述+两种传统的异步编程模式 1.线程池简述 首先我们要明确一点,编程中讲的线程与平时我们形容CPU几核几线程中的线程是不一样的,CPU线程是指逻辑处理器,比如4核8线程,讲的是这个cpu有8个 ...

  8. 根据多个成对的cron表达式生成的时间段,合并

    场景:数据库一张表,有个startcron 和endcron 两个字段,根据表达式计算今天的所有时间段. 例:startcron :0 30 20 ? * * endcron :0 30 21 ? * ...

  9. 学习笔记60_python面向对象

    **** //可以给函数参数设置默认值 def stu_register(name,age,course,country="CN"): 若你的函数在定义时不确定用户想传入多少个参数 ...

  10. [2018-03-06] 基于Django的轻量级CMS Mezzanine搭建笔记

    一丶什么是Mezzanine? 它是基于django的内容管理平台(),组成简单,可扩展性和定制性强,特别是个小型的个人博客系统.它也提供了类似wordpress的管理页面.博客发布.图片展示等功能, ...