前言

网络I/O,可以理解为网络上的数据流。通常我们会基于socket与远端建立一条TCP或者UDP通道,然后进行读写。单个socket时,使用一个线程即可高效处理;然而如果是10K个socket连接,或者更多,我们如何做到高性能处理?

  • 基本概念介绍
  • 网络I/O的读写过程
  • linux下的五种网络I/O模型
  • 多路复用I/O深入理解一波
  • Reactor模型
  • Proacotr模型

关注公众号,一起交流 :潜行前行

基本概念介绍

  • 进程(线程)切换

    • 所有系统都有调度进程的能力,它可以挂起一个当前正在运行的进程,并恢复之前挂起的进程
  • 进程(线程)的阻塞
    • 运行中的进程,有时会等待其他事件的执行完成,比如等待锁,请求I/O的读写;进程在等待过程会被系统自动执行阻塞,此时进程不占用CPU
  • 文件描述符

    • 在Linux,文件描述符是一个用于表述指向文件引用的抽象化概念,它是一个非负整数。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符
  • linux信号处理
    • Linux进程运行中可以接受来自系统或者进程的信号值,然后根据信号值去运行相应捕捉函数;信号相当于是硬件中断的软件模拟

在零拷贝机制篇章已介绍过 用户空间和内核空间缓冲区,这里就省略了

网络IO的读写过程

  • 当在用户空间发起对socket套接字的读操作时,会导致上下文切换,用户进程阻塞(R1)等待网络数据流到来,从网卡复制到内核;(R2)然后从内核缓冲区向用户进程缓冲区复制。此时进程切换恢复,处理拿到的数据
  • 这里我们给socket读操作的第一阶段起个别名R1,第二阶段称为R2
  • 当在用户空间发起对socket的send操作时,导致上下文切换,用户进程阻塞等待(1)数据从用户进程缓冲区复制到内核缓冲区。数据copy完成,此时进程切换恢复

linux五种网络IO模型

阻塞式I/O (blocking IO)

ssize_t

  • 最基础的I/O模型就是阻塞I/O模型,也是最简单的模型。所有的操作都是顺序执行的
  • 阻塞IO模型中,用户空间的应用程序执行一个系统调用(recvform),会导致应用程序被阻塞,直到内核缓冲区的数据准备好,并且将数据从内核复制到用户进程。最后进程才被系统唤醒处理数据
  • 在R1、R2连续两个阶段,整个进程都被阻塞

非阻塞式I/O (nonblocking IO)

  • 非阻塞IO也是一种同步IO。它是基于轮询(polling)机制实现,在这种模型中,套接字是以非阻塞的形式打开的。就是说I/O操作不会立即完成,但是I/O操作会返回一个错误代码(EWOULDBLOCK),提示操作未完成
  • 轮询检查内核数据,如果数据未准备好,则返回EWOULDBLOCK。进程再继续发起recvfrom调用,当然你可以暂停去做其他事
  • 直到内核数据准备好,再拷贝数据到用户空间,然后进程拿到非错误码数据,接着进行数据处理。需要注意,拷贝数据整个过程,进程仍然是属于阻塞的状态
  • 进程在R2阶段阻塞,虽然在R1阶段没有被阻塞,但是需要不断轮询

多路复用I/O (IO multiplexing)

  • 一般后端服务都会存在大量的socket连接,如果一次能查询多个套接字的读写状态,若有任意一个准备好,那就去处理它,效率会高很多。这就是“I/O多路复用”,多路是指多个socket套接字,复用是指复用同一个进程
  • linux提供了select、poll、epoll等多路复用I/O的实现方式
  • select或poll、epoll是阻塞调用
  • 与阻塞IO不同,select不会等到socket数据全部到达再处理,而是有了一部分socket数据准备好就会恢复用户进程来处理。怎么知道有一部分数据在内核准备好了呢?答案:交给了系统系统处理吧
  • 进程在R1、R2阶段也是阻塞;不过在R1阶段有个技巧,在多进程、多线程编程的环境下,我们可以只分配一个进程(线程)去阻塞调用select,其他线程不就可以解放了吗

信号驱动式I/O (SIGIO)

  • 需要提供一个信号捕捉函数,并和socket套接字关联;发起sigaction调用之后进程就能解放去处理其他事
  • 当数据在内核准备好后,进程会收到一个SIGIO信号,继而中断去运行信号捕捉函数,调用recvfrom把数据从内核读取到用户空间,再处理数据
  • 可以看出用户进程是不会阻塞在R1阶段,但R2还是会阻塞等待

异步IO (POSIX的aio_系列函数)

  • 相对同步IO,异步IO在用户进程发起异步读(aio_read)系统调用之后,无论内核缓冲区数据是否准备好,都不会阻塞当前进程;在aio_read系统调用返回后进程就可以处理其他逻辑
  • socket数据在内核就绪时,系统直接把数据从内核复制到用户空间,然后再使用信号通知用户进程
  • R1、R2两阶段时进程都是非阻塞的

多路复用IO深入理解一波

select

int

  • 1)使用copy_from_user从用户空间拷贝fd_set到内核空间
  • 2)注册回调函数__pollwait
  • 3)遍历所有fd,调用其对应的poll方法(对于socket,这个poll方法是sock_poll,sock_poll根据情况会调用到tcp_poll,udp_poll或者datagram_poll)
  • 4)以tcp_poll为例,其核心实现就是__pollwait,也就是上面注册的回调函数
  • 5)__pollwait的主要工作就是把current(当前进程)挂到设备的等待队列中,不同的设备有不同的等待队列,对于tcp_poll来说,其等待队列是sk->sk_sleep(注意把进程挂到等待队列中并不代表进程已经睡眠了)。在设备收到一条消息(网络设备)或填写完文件数据(磁盘设备)后,会唤醒设备等待队列上睡眠的进程,这时current便被唤醒了
  • 6)poll方法返回时会返回一个描述读写操作是否就绪的mask掩码,根据这个mask掩码给fd_set赋值
  • 7)如果遍历完所有的fd,还没有返回一个可读写的mask掩码,则会调用schedule_timeout是调用select的进程(也就是current)进入睡眠
  • 8) 当设备驱动发生自身资源可读写后,会唤醒其等待队列上睡眠的进程。如果超过一定的超时时间(timeout指定),还是没人唤醒,则调用select的进程会重新被唤醒获得CPU,进而重新遍历fd,判断有没有就绪的fd
  • 9)把fd_set从内核空间拷贝到用户空间

select的缺点

  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  • 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
  • select支持的文件描述符数量太小了,默认是1024

epoll

int

  • 调用epoll_create,会在内核cache里建个红黑树用于存储以后epoll_ctl传来的socket,同时也会再建立一个rdllist双向链表用于存储准备就绪的事件。当epoll_wait调用时,仅查看这个rdllist双向链表数据即可
  • epoll_ctl在向epoll对象中添加、修改、删除事件时,是在rbr红黑树中操作的,非常快
  • 添加到epoll中的事件会与设备(如网卡)建立回调关系,设备上相应事件的发生时会调用回调方法,把事件加进rdllist双向链表中;这个回调方法在内核中叫做ep_poll_callback

epoll的两种触发模式

  • epoll有EPOLLLT和EPOLLET两种触发模式,LT是默认的模式,ET是“高速”模式(只支持no-block socket)

    • LT(水平触发)模式下,只要这个文件描述符还有数据可读,每次epoll_wait都会触发它的读事件
    • ET(边缘触发)模式下,检测到有I/O事件时,通过 epoll_wait 调用会得到有事件通知的文件描述符,对于文件描述符,如可读,则必须将该文件描述符一直读到空(或者返回EWOULDBLOCK),否则下次的epoll_wait不会触发该事件

epoll相比select的优点

  • 解决select三个缺点

    • 对于第一个缺点:epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次(epoll_wait不需要复制)
    • 对于第二个缺点:epoll为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(不需要遍历)
    • 对于第三个缺点:epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,一般来说这个数目和系统内存关系很大
  • epoll的高性能
    • epoll使用了红黑树来保存需要监听的文件描述符事件,epoll_ctl增删改操作快速
    • epoll不需要遍历就能获取就绪fd,直接返回就绪链表即可
    • 之后使用了mmap技术,数据不在需要从内核复制到用户空间,零拷贝

关于epoll的IO模型是同步异步的疑问

  • 概念定义

    • 同步I/O操作:导致请求进程阻塞,直到I/O操作完成
    • 异步I/O操作:不导致请求进程阻塞,异步只用处理I/O操作完成后的通知,并不主动读写数据,由系统内核完成数据的读写
    • 阻塞,非阻塞:进程/线程要访问的数据是否就绪,进程/线程是否需要等待
  • 异步IO的概念是要求无阻塞I/O调用。前面有介绍到I/O操作分两阶段:R1等待数据准备好。R2从内核到进程拷贝数据。虽然epoll在内核之后采用mmap机制,使得其在R2阶段不需要复制,但是它在R1还是阻塞的。因此归类到同步IO

Reactor模型

Reactor的中心思想是将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程/进程阻塞在多路复用器上;一旦有I/O事件到来或是准备就绪,多路复用器返回,并将事先注册的相应I/O事件分发到对应的处理器中

相关概念介绍:

  • 事件:就是状态;比如:读就绪事件指的是我们可以从内核读取数据的状态
  • 事件分离器:一般会把事件的等待发生交给epoll、select;而事件的到来是随机,异步的,所以需要循环调用epoll,在框架里对应封装起来的模块就是事件分离器(简单理解为对epoll封装)
  • 事件处理器:事件发生后需要进程或线程去处理,这个处理者就是事件处理器,一般和事件分离器是不同的线程

Reactor的一般流程

  • 1)应用程序在事件分离器注册读写就绪事件读写就绪事件处理器
  • 2)事件分离器等待读写就绪事件发生
  • 3)读写就绪事件发生,激活事件分离器,分离器调用读写就绪事件处理器
  • 4)事件处理器先从内核把数据读取到用户空间,然后再处理数据

单线程 + Reactor

多线程 + Reactor

多线程 + 多个Reactor

Proactor模型的一般流程

  • 1)应用程序在事件分离器注册读完成事件读完成事件处理器,并向系统发出异步读请求
  • 2)事件分离器等待读事件的完成
  • 3)在分离器等待过程中,系统利用并行的内核线程执行实际的读操作,并将数据复制进程缓冲区,最后通知事件分离器读完成到来
  • 4)事件分离器监听到读完成事件,激活读完成事件的处理器
  • 5)读完成事件处理器直接处理用户进程缓冲区中的数据

Proactor和Reactor的区别

  • Proactor是基于异步I/O的概念,而Reactor一般则是基于多路复用I/O的概念
  • Proactor不需要把数据从内核复制到用户空间,这步由系统完成

欢迎指正文中错误

参考文章

模型描述的关系模式_框架篇:见识一下linux高性能网络IO+Reactor模型相关推荐

  1. 模型描述的关系模式_你的项目该用哪种编程模式?

    哎,一个1970年的问题,争论了快50年了,还有那么多引战的. 客观一点讲,对于玩过不少语言.大体上几种模式都上过项目的我来讲,几种编程模式的本质问题都是管理问题. 01 面向过程,本质是" ...

  2. 模型描述的关系模式_最常用的数据模型 - 关系模型

    关系模型是目前最常用地数据模型之一.关系型数据库系统采用关系模型作为数据的组织方式,在关系模型中用表格结构表达实体集,以及实体集之间的联系,其最大特点是描述的一致性.关系模型是由若干个关系模式组成的集 ...

  3. 模型描述的关系模式_商业模式9要素分析表:基础模型

    刘圻教授微信号:liuqiyxjxlink 投稿邮箱:liuqiyxjx@163.com 合作邮箱:liuqicooperate@163.com 原创声明 作者:刘圻|编辑:谢莹莹 版权:本文为公司价 ...

  4. 模型描述的关系模式_单位根检验、协整检验和格兰杰因果关系检验三者之间的关系...

    实证检验步骤:先做单位根检验,看变量序列是否平稳序列,若平稳,可构造回归模型等经典计量经济学模型:若非平稳,进行差分,当进行到第i次差分时序列平稳,则服从i阶单整(注意趋势.截距不同情况选择,根据P值 ...

  5. 视频教程-AssetBundle框架设计_框架篇视频课程-Unity3D

    AssetBundle框架设计_框架篇视频课程 二十多年的软件开发与教学经验IT技术布道者,资深软件工程师.具备深厚编程语言经验,在国内上市企业做项目经理.研发经理,熟悉企业大型软件运作管理过程.软件 ...

  6. mysql关系模式_数据库学习--关系模式

    最近开始做数据库的大实验,其中有一条实验要求如下: 通过网络查找相关文献并参考所给资料进行需求分析,画出系统的 E-R 图,给出实体或联系的属性,标明联系的种类,并写出关系模式. 画ER图没有什么问题 ...

  7. mysql关系模式_关系数据模型是什么

    关系数据模型是一种数据模型,以关系或表格的形式对数据进行建模,是用来表示数据在关系数据库中的存储和处理方式,在关系数据库中会以关系(表)的形式来存储数据. 关系数据模型中有三大要素,分别为:关系数据结 ...

  8. mysql图书管理数据库的三个关系模式_数据库 考虑如下关于图书馆的关系模式,用关系代数写出查询(数据库系统概念第六版6.14)...

    贝尔梅尔娜美 2019.03.15 采纳率:60%    等级:39 已帮助:91565人 数据库系统的基本概念 数据:实际上就是描述事物的符号记录. 数据的特点:有一定的结构,有型与值之分,如整型. ...

  9. uwsgi模式_一篇就弄懂WSGI、uwsgi和uWSGI的区别

    引言 最近基于Django Web框架在开发一个后端项目,在Web Server和Django应用程序交互的过程中总会碰到本文题目提及到的几个概念,笔者特意花了点时间研究了下,为方便以后温习特在此记录 ...

最新文章

  1. Linux安装CentOS6(图文详解)新手入门
  2. 使用PHP自带的过滤验证函数:Filter
  3. 音视频领域或将开启”外卷“之路
  4. 转换字符串为json对象的方法
  5. tcpcopy使用方法
  6. 17现代软件工程十五组第三次作业
  7. 用户计算机安全管理,关于加强用户计算机安全管理工作的通知
  8. java应用程序如何编译运作_开发Java应用程序的基本步骤是: 1 编写源文件, 2.编译源文件, 3.运行程序。_学小易找答案...
  9. inventor整理资源中心收藏夹
  10. B站(IT速成之JAVA速成班 300分钟算法面试)跟做笔记
  11. html圆角周角代码,CSS3实现DIV圆角效果完整代码
  12. Junit单元测试——如何正确测试异常
  13. cmd复制文件到其他目录
  14. C语言水电管理系统,小区水电费管理系统C语言.doc
  15. 软件测试创业公司和大厂外包如何选择?
  16. 怎么保护MacBook的电池?Mac电池最大充电限制工具AlDente Pro来帮您
  17. git 修改倒数二个 commit
  18. orange one plus 全志H6教程
  19. ftrace和tracepoint简单使用
  20. 打造自己的JavaScript武器库

热门文章

  1. android支付宝余额怎么做,android实现类似于支付宝余额快速闪动的效果 -电脑资料...
  2. mysql5.1免安装版配置_mysql5.1免安装版配置
  3. html弹窗_对付流氓广告弹窗:彻底告别,这一招最有效
  4. 如何判断模糊图像_深圳企业宣传片拍摄制作教你如何使用手动聚焦
  5. 现在的计算机电源都要经过,电脑待机后必须关闭电源才能开启
  6. 删除虚拟环境_手把手教你在Linux系统下使用Python虚拟环境
  7. php操作xml类,PHP实现的XML操作类【XML Library】
  8. 高品位登录页面,优秀案例给你灵感!
  9. 设计灵感|教会你海报设计套路
  10. 3D视界迎来全息视角,VR呈现效果海报,引领未来