当前位置: 首页 > 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.lryc.cn/news/587376.html

相关文章:

  • Clojure和Golang中的Channel有什么异同(TBC)
  • NumPy 中 np.c_ 的用法解析
  • Can I Trust Your Answer? Visually Grounded Video Question Answering
  • Python协程进阶:优雅终止与异常处理详解
  • Java 面向对象的特征(一)
  • Actor-Critic重要性采样原理
  • 在高并发场景下,仅依赖数据库机制(如行锁、版本控制)无法完全避免数据异常的问题
  • 用豆包AI云盘保存和分析录音文件
  • 维基艺术图片: 数据标注 (2)
  • java: DDD using oracle 21c
  • 树莓派5-ollama-linux-arm64.tgz 下载
  • KL散度:信息差异的量化标尺 | 从概率分布对齐到模型优化的核心度量
  • 强化学习初探及OREAL实践
  • Leaflet面试题及答案(61-80)
  • Flink数据流高效写入MySQL实战
  • XCZU2CG-2SFVC784I Xilinx FPGA AMD Zynq UltraScale+ MPSoC
  • Vivado ILA抓DDR信号(各种IO信号:差分、ISERDES、IOBUFDS等)
  • 六、深度学习——NLP
  • 无缝衔接直播流体验
  • 早期 CNN 的经典模型—卷积神经网络(LeNet)
  • 板凳-------Mysql cookbook学习 (十一--------8)
  • 【深度学习新浪潮】什么是新视角合成?
  • STM32-第五节-TIM定时器-1(定时器中断)
  • JAVA并发——synchronized的实现原理
  • 特征选择方法
  • 一文打通MySQL任督二脉(事务、索引、锁、SQL优化、分库分表)
  • GraphRAG Docker化部署,接入本地Ollama完整技术指南:从零基础到生产部署的系统性知识体系
  • AEC线性处理
  • 【iOS】方法与消息底层分析
  • 【设计模式】命令模式 (动作(Action)模式或事务(Transaction)模式)宏命令