IO模型与reactor模式

news/2024/7/9 17:24:10 标签: 网络, epoll

文章目录

  • 前言
  • 一、IO模型的种类及如何理解
  • 二、reactor模式与IO模拟的proactor模式


前言

主要讲解五种IO模型


一、IO模型的种类及如何理解

unix系统的网络IO模型根本上分为五种:
1、阻塞IO;
在这里插入图片描述

2、非阻塞式IO;
在这里插入图片描述

3、I/O复用
在这里插入图片描述

4、信号驱动式I/O
在这里插入图片描述

5、异步I/O
在这里插入图片描述

可能很多人都在unix网络编程中看过这五种IO的介绍,但却对这几种IO的区别点在哪体会不深。所以这里也是主要侧重于比较各种IO。
首先来说阻塞IO
其实就是用户进程使用阻塞模式调用recv接口,如果此时内核有数据可读,就从内核拷贝到用户空间(用户自己开辟的内存)。与其他IO比较可能会让人疑问的是,我IO复用也是阻塞呀,为什么不把IO复用也叫阻塞IO呢,区别就在于,IO复用是阻塞在fd,而阻塞IO相当于把fd和数据的拷贝都阻塞了,起到一个解耦合的作用,这样解耦的效果就在于,我可以同时监控(单个线程中或单个进程中)多个fd,而阻塞IO你就只能同时监控一个fd,处理完一个再监控另外一个。
非阻塞IO则是系统调用使用非阻塞模式,无数据也返回,我不一直傻等着,但是不阻塞就意味着,处理不好CPU占用就会很高,比如一个任务,接收对端数据,你不知道数据多久来一次,你想一直用个while(1)来检测,那CPU估计一下子老高了,你要想用个sleep,你又不知道sleep多久合适,时间短了cpu降不下来,长了数据可能就不能及时收。
IO复用:上面已经提到一些IO复用的内容,IO复用就是将对fd事件和数据事件解耦的一种模型,它是以事件为基础,而不再是以I/O为基础,所谓事件,从,应用层面来说,我们的连接的接入算事件,数据的接收算事件,数据的发送算事件,这样说可能不太准确,从原理上来说,当有数据到来时,会产生中断,CPU中断函数会从网卡存数据的内存拷贝到对应fd的内存,因为每一个fd都对应一个接收缓冲区,CPU根据端口来确定,当进程阻塞在fd上时,操作系统会把进程的引用加入socket的等待队列中,当任何一个socket收到数据后,中断程序将唤起进程,即将进程从所有的等待队列中移出,对于select模式的IO复用,我们一般说它比较低效,一个原因就是它将维护等待队列和阻塞进程两个步骤合二为一了,而大多数应用场景,需要监视的socket相对固定,不需要每次都修改,(select每次都需要传入fd集),epoll则不一样,epoll将维护等待队列,阻塞进程分开。另外一个原因导致select低效在于select需要遍历整个select集才能获得有哪些fd就绪(有待处理事件),而epoll不用,在epoll_wait返回后,你拿到的就是就绪事件集。这是源于epoll的实现,像nginx中,就是通过红黑树将一个个fd作为结点存储起来,而如果当有事件时,对应的fd会被假如一个链表rdlist,这个链表结点其实也是原来的红黑树结点,因此并没有拷贝操作,一个个就绪结点链接起来就是一个就绪事件集。因此不需要我们上层再通过遍历去获取就绪事件集。而对于就绪事件集 的处理,实际上不管select还是epoll都需要去遍历的,因此我们说当IO少及活跃的IO比例高的时候,用select会比较高效,因为怎么都得遍历。这里需要多说一点的是epoll的ET模式和LT模式,通常我们将它成为边沿触发和水平触发,其实很容易理解,所谓LT,就是当缓冲区的数据还没取完时,内核会一直通知我们,所谓的ET就是我只在数据从无到有的时候通知一次,后面我就不通知了。处理上我们对ET一次接收和对LT多次接收直到数据全部收完效果是一样的,但这也决定了,ET模式不适用于大量的数据接收,因为大块数据接收会阻塞较长时间。并且像调用Listen函数的时候,我们希望是每连接一次触发一次,而不是对于同样的fd,连接一次之后,就不再触发了,因此listen的处理需要用LT.

信号驱动IO:所谓信号驱动IO,就是我们通过sigaction来让操作系统内核在有数据的时候,给进程返回一个SIGIO信号,进程拿到IO信号可以在我们提供给内核的处理函数中进行处理,也可以通知主循环处理。这里需要注意的是,我们一般不会在信号处理函数中去进行太复杂的处理,比如recv阻塞调用,这是因为信号处理期间,系统不会再次触发它,因此我们通常当信号函数被触发时,只是简单的通知主循环收到了信号,并把信号值传给主循环,主循环根据信号值执行相应逻辑。通常使用管道来传递信号值,所以一般为了统一事件源,我们也将信号事件和其他IO事件一样,使用IO复用来监听管道上的可读事件。
异步IO:这里,的异步IO,就只是单纯的指aio系统调用。它就是相当于把数据拷贝的操作交给操作系统,当上层返回时,数据已经完成了拷贝。一开始aio是希望能替代IO复用的,后来发觉效率其实没有IO复用高。需要说的一点是,很多人在后期容易把异步IO和并发编程例的异步搞混,并发编程里的异步,指的是不同的线程能独立地同时处理不同的任务,而并发的同步则是各线程之间互相依赖,并不能独立互不干扰地去处理任务,而是依赖于一种同步的机制,以在不同的线程之间获取同一资源不会产生冲突,这也就说到我们的reactor模式和proactor模式了。

二、reactor模式与IO模拟的proactor模式

这里把这两个模式单独拎出来,也是顺便让读者在后续遇到不至于又往前翻书。我们说reactor模式或proactor模式,其实都是对事件,或者说对epoll的一种管理模式。t通常我们使用epoll进行网络事件处理的时候,不可避免地必须经过这几步:
(1)添加监听socket;
(2)等待监听socket上的事件
(3)当有就绪事件时,判断就绪事件的类型与对应的fd,看是读事件还是写事件,不同fd的读写事件对应不同的处理
因为前两步是固定的,对于第三步,其实我们可以用策略模式的思路,将不同的处理看做变化的部分,将其封装起来,做成回调的形式。具体的处理就可以抛到上层去处理。
对于reactor模式来说,就绪事件插入请求队列中,对于模拟的proactor模式来说,就绪数据插入请求队列中,由主线程来进行数据的拷贝,之所以叫模拟reactor模式,就在于模拟操作系统内核拷贝数据这一过程,由主线程来做,对数据拷贝和数据的业务处理做了解耦。但两者都得进行就绪事件的分类处理。
在这里插入图片描述
在这里插入图片描述

所以通常我们实现一个reactor模式,其实就是在实现一个事件管理器,必不可少的不走就在于初始化事件管理器,添加监听,运行事件管理(等待事件,写入队列),将对应的fd的操作做成回调的模式,就可以将不同事件的处理与事件判断解耦


http://www.niftyadmin.cn/n/1289233.html

相关文章

python安装的推荐包

Anaconda(强烈推荐!) 下载地址 https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 安装方法 https://blog.csdn.net/ITLearnHall/article/details/81708148/

并发计数原理及其无锁实现

文章目录前言一、一个简单的多线程计数程序二、多线程计数的本质剖析1.预备知识,一些简单的汇编知识1.1寄存器简介及寄存器操作1.2简单汇编算术综合运算指令2.正题,多线程计数的计数变化的剖析3.使用锁解决多线程计数的问题3.1使用互斥锁3.2使用自旋锁4.…

单例设计模式深释--单例设计模式的N种写法

文章目录前言一、单例设计模式介绍二、单例模式的实现1、写法1,最简单的饿汉模式写法写法2--最简单的懒汉模式写法写法3--解决2中的内存泄漏问题写法4--解决2、3中的多线程同步问题问题内存模型与内存屏障简介内存模型与对象模型的区别CPU的高速缓存与内存可见性&am…

【bzoj5015】[Snoi2017]礼物

Description 热情好客的请森林中的朋友们吃饭,他的朋友被编号为 1~N,每个到来的朋友都会带给他一些礼物:。其中,第 一个朋友会带给他 1 个,之后,每一个朋友到来以后,都会带给他之前…

bzoj3670: [Noi2014]动物园

3670: [Noi2014]动物园Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 3518 Solved: 1910[Submit][Status][Discuss]Description近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气&#xff…

【51Nod1412】AVL树的种类

平衡二叉树(AVL树),是指左右子树高度差至多为1的二叉树,并且该树的左右两个子树也均为AVL树。 现在问题来了,给定AVL树的节点个数n,求有多少种形态的AVL树恰好有n个节点。 Input 一行,包含一个整数n。 (0…

【bzoj1589】[Usaco2008 Dec]Trick or Treat on the Farm 采集糖果

Description 每年万圣节,威斯康星的奶牛们都要打扮一番,出门在农场的N(1≤N≤100000)个牛棚里转悠,来采集糖果.她们每走到一个未曾经过的牛棚,就会采集这个棚里的1颗糖果. 农场不大,所以约翰要…