当前位置: 首页 > news >正文

手写muduo笔记

网络IO的两个阶段:数据准备和数据读写

数据准备:根据系统IO操作的就绪状态,int size=recv(sockfd,buf,1024,0);

size>0有数据到来,字节数

size=-1 &&   errno=EAGAIN 内部错误

size==0  网络对端关闭连接

阻塞:调用IO方法的线程一直阻塞,直到sockfd有数据到来

非阻塞:没有数据到来,直接返回

数据读写:根据应用程序和内核的交互方式,指的是IO的同步和异步,

同步:把recv接受的数据(在TCP数据缓冲区中,往buf里搬,应用程序自己花时间搬)

异步:sockfd帮忙把数据放到buf里(操作系统搬的),当sigio通知应用程序时,buf的数据已经准备好了

在处理IO的时候,阻塞和非阻塞都是同步IO,只有使用了特殊的API才是异步IO

同步:A操作等待B操作做完事情,得到返回值,继续处理

异步:A操作告诉B操作它感兴趣的事件以及通知方式,A操作继续执行自己的业务逻辑,等到B监听到相应事件发生后,B会通知A,A开始相应的数据处理逻辑

Linux上的五种IO模型

阻塞

非阻塞

IO复用 进程阻塞于select/poll/epoll等待套接字变为可读,用一个线程调用IO复用接口,可以监听很多很多个socket套接字,其他和非阻塞差不多

信号驱动(signal-driven)调用进程注册SIGIO的信号处理程序,系统来调用sigaction来告诉数据是否就绪,相当于异步,异步也是信号,但是是操作系统把数据接受好然后搬到buf再通知应用程序

也就是相当于上面的数据就绪是异步过程,下面还是需要应用程序自己从tcp 数据缓冲区中将数据读到buffer中。与非阻塞IO的区别在于它提供了消息通知机制,不需要用户进程不断的轮询检查,减少了系统API的调用次数,提高了效率

异步

异步非阻塞IO是最典型的,整个过程,从数据就绪到数据拷贝,都不耗费应用进程的时间,只要是事先通过airead来告诉内核,应用程序的buffer,通知信号,以及对哪个socket感兴趣

好的网络服务器的设计

赞同libev作者的观点:one loop per thread is usually a good model 。在一个线程里有一个事件循环

好的服务器一般都是IO复用来操作非阻塞的socket再加上线程池

reactor模型

比较出名的muduo网络库,lib-event都有基于事件驱动的reactor模型

reactor模型重要组件:Event事件,Reactor反应堆,Demultiplex事件分发器,Evanthandler事件处理器

Demultiplex事件分发器:基于IO多路复用的事件分发器

首先把事件注册到反应堆上,请求反应堆来监听我所感兴趣的事件,并且在事件发生的时候调用预制的回调Handler。启动反应堆后,反应堆的后端就会驱动这个事件分发器的启动,开启epoll_wait,整个服务器处于阻塞状态,来等待新用户的连接或者是已连接用户的读写事件 ,如果多路复用监听到有新事件发生,就会给反应堆返回,因为事件发生了,我们在reactor模型里面就要调用事件对应的处理器,reactor就会找到event对应的handler,是用一个map表来存储

reactor z主要就是存储了事件以及事件对应的处理器

Demultiplex事件分发器 可以有多进程,一个专门用来监听新用户的连接,另外可以用来监听已连接用户的读写事件,如果再有耗时的IO事件,比如说传输文件,再专门开一个事件。

epoll

select 和 poll的缺点

select的缺点:

1.单个进程能够监视的文件描述符的数量存在最大限制,通常是1024,当然可以更改数量,但由于select采用轮询的方式扫描文件描述符,文件描述符数量越多,性能越差

2.内核/用户空间内存拷贝问题,select需要复制大量的句柄数据结构,产生巨大的开销

3.select返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能发现哪些句柄发生了事件

4.select的触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么之后每次select调用还是会将这些文件描述符通知进程

相比select模型,pool使用链表保存文件描述符,因此没有了监视文件数量的限制,但其他三个缺点依然存在

epoll的实现机制和select/poll机制完全不同,它们的缺点在epoll上不复存在

设想如下:有100万个客户端同时与一个服务器进程保持着TCP连接。而每一时刻,通常只有几百上千个TCP连接是否活跃。如何实现这样的高并发?

在select/poll时代,服务器进程每次都把这100万个连接告诉操作系统(从用户态复制句柄数据结构到内核态),让操作系统内核去查询这些套接字上是否有事件发生,轮询完成后,再将句柄数据复制到用户态,让服务器应用程序轮询处理已发生的网络事件,这一过程资源消耗较大,因此,select/poll一般只能处理几千的并发连接

epoll的设计和实现select完全不同。epoll通过再Linux内核中申请一个简易的文件系统(文件系统一般用什么数据结构实现?B+数,磁盘IO消耗低,效率很高)。把原先的select/poll调用成一下三个部分

1.调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)(红黑树,双向链表)

2.调用epoll_ctl向epoll对象中添加这100万个连接的套接字

3.调用epoll_wait 收集发生的事件的fd资源

如此以来,要实现上面说是的场景,只需要在进程启动时建立一个epoll对象,然后在需要的时候像这个epoll对象中添加或者删除连接。同时,epoll_wait的效率也非常高,因为调用epoll_wait时,并没有向操作系统复制这100万个连接的句柄数据,内核也不需要去遍历全部的连接。

LT模式

内核数据没被读完,就会一直上报数据

ET模式

内核数据只上报一次

muduo采用的是LT

不会丢失数据或者消息:应用没有读取完数据,内核是会不断上报的

低延迟处理:每次读数据只需要一次系统调用,照顾多个连接的公平性,不会引文某个连接上的数据量过大而影响其他连接处理消息

跨平台处理:像select一样可以跨平台使用

http://www.dtcms.com/a/277166.html

相关文章:

  • package.json 与 package-lock.json
  • 笔记/sklearn中的数据划分方法
  • 流程控制:从基础结构到跨语言实践与优化
  • 博客项目 laravel vue mysql 第五章 标签功能
  • 前端性能优化利器:懒加载技术原理与最佳实践
  • JAVA JVM虚拟线程
  • 第三章-提示词-解锁Prompt提示词工程核销逻辑,开启高效AI交互(10/36)
  • Vue3 Diff 算法片段解析:新旧节点队列之乱序比对与更新策略
  • 7月13日星期日今日早报简报微语报早读
  • Python PyWhat详解:一款高效的数据类型识别与扫描工具
  • 把固态电池当“显卡”用:基于Li⁺迁移率的矩阵乘法加速器——电解质-权重共设计框架与FP8训练实战
  • C++--List的模拟实现
  • 深入解析Hadoop RPC:技术细节与推广应用
  • vue3 ref vs reactive值的修改
  • 自动润滑系统:从 “盲目养护“ 到智能精注的工业运维革命
  • 【芯片笔记】ADF4159
  • 详解Linux下多进程与多线程通信(一)
  • python:使用openpyxl库,实现excel表格的创建、查询(读取)、修改、插入数据
  • LVS负载均衡集群概述
  • 论文笔记:AnImitation Learning Approach for Cache Replacement
  • iOS高级开发工程师面试——架构设计
  • MailSpring
  • [Subtitle Edit] 语言文件管理.xml | 测试框架(VSTest) | 构建流程(MSBuild) | AppVeyor(CI/CD)
  • 006_测试评估与安全实践
  • 设计模式之工厂模式:对象创建的智慧之道
  • 设计模式-门面模式
  • CAU数据挖掘第四章 分类问题
  • 【论文阅读47】-微震事件的时间、空间和强度(能量)预测
  • Java设计模式之行为型模式(命令模式)
  • 单例模式:确保全局唯一实例