Linux 5种网络模型

news2025/1/11 7:52:30

[参考]:《黑马程序员Redis》https://www.bilibili.com/video/BV1cr4y1671t/?p=166&share_source=copy_web&vd_source=9e65300ccca322aeb367bb1eb677b0fc

[参考]:《操作系统》

[参考]:《UNIX网络编程》

为了避免用户应用导致冲突甚至内核崩溃,用户应用与内核是分离的:
进程的寻址空间会划分为两部分:内核空间、用户空间
用户空间只能执行受限的命令(Ring3),而且不能直接调用系统资源,必须通过内核提供的接口来访问内核空间可以执行特权命令 (Ringo),调用一切系统资源 

 

必要的前置知识:

编译:由编译程序将用户源代码编译成若千个目标模块(编译就是把高级语言翻译为机器语言)

链接:由链接程序将编译后形成的一组目标模块,以及所需库函数链接在一起,形成一个完整的装入模块装入 (装载) : 由装入程序将装入模块装入内存运行

1、程序、内存、与寻址

程序代码通过(预处理、编译、汇编、链接)等步骤,形成可执行的机器指令后,这些指令会告诉CPU去内存的哪个地址读/写数据,然后与寄存器进行交互,进行一些计算操作,等等。

程序生成的指令中指明的地址,是逻辑地址(相对地址),而我们的数据真实所在的内存是物理地址

在C语言中,可执行文件最终是以装入模块的形式,进入内存。

1.1 程序的装入

装入方式有三种:

1、绝对装入

只适用于单道程序环境,程序驻留在内存的实际位置是已知的,程序中的逻辑地址与内存的实际地址完全相同。由程序员直接赋予,或程序中采用符号地址,在编译或汇编时转换为绝对地址。

2、可重定位装入(静态重定位)

多道程序环境下,多个目标模块的起始地址都是0,程序中的其他地址都是相对于起始地址的。装入时对目标程序的指令和数据地址进行修改的过程 称为重定位,因其地址变换通常是在进程装入时一次完成的,故称为【静态重定位】

3、动态运行时装入(动态重定位)

程序若在内存中会发生移动,则使用动态重定位,即重定位的过程并不是在装入时完成,而是推迟到了程序运行时进行,需要重定位寄存器支持。

1.2 逻辑地址与物理地址

编译后,每个目标模块都是从0号单元开始编址这称为该目标模块的相对地址(或逻辑地址),当链接程序将各个模块链接成一个完整的可执行的目标程序时,链接程序的顺序按照各个模块的相对地址构成统一的从0号地址单元开始编址的逻辑地址空间(或虚拟地址空间)。

对于32位系统,逻辑地址空间的范围是 0~2^32-1。进程在运行时,看到的和使用的都是逻辑地址。用户程序和程序员只需要知道逻辑地址,而内存管理的具体机制则是完全透明的(对用户不可视)。不同的进程可以有相同的逻辑,因为他们会被隐射到不同的主存位置。

寻址空间与计算机的位数有关系,因为每个地址单元的大小是1B,如果是32位系统,那么寻址空间大小是 2^32=4GB次方字节 。

2^10 =1024B =1K字节(1KByte),  2^20 =1MB,2^30 =1GB, 

2^32 =(2^30)*(2^2) =4GB;

因此地址编号长度要能表示出4GB/1B =2^32个内存单元

32位2进制 可以转换成8位16进制表达

0地址

0x0000 0000

高地址

0xFFFF FFFF

涉及内存地址增长、存储的问题还有一个大小端的情况:

大端存储:地址位存储数据位,地址位存储数据

小端存储:地址位存储数据位,地址位存储数据

物理地址就是内存中物理单元的集合,是地址转换的最终地址。

逻辑地址通过页表映射到物理内存,页表由操作系统维护并被处理器引用。

1.3 进程的内存映像

当一个程序调入内存运行时,就构成了进程的内存映像。

  • 代码段:即程序的二进制代码,代码段是只读的,可以被多个进程共享
  • 数据段:即程序运行时加工处理的对象,包括全局变量和静态变量
  • 进程控制块PCB:存放在系统区。操作系统通过PCB来控制和管理进程
  • 堆:用来存放动态分配的变量,通过调用malloc函数动态的向高地址分配空间。
  • 栈:用来实现函数调用,从用户控件的最大地址往低地址增长。

(补充一句:高级语言运行思路都是类似的,即使是Java这种运行逻辑依靠JVM的,其内部运行时数据区的设计思路都是源于操作系统管理的算法和逻辑)。

代码段和数据段在程序调入内存时就指定了大小。(其实我们在学C++时就说了这些东西其实是在编译期,运行之前就确定的,Java也有类似的方法区,其具体落地实现中,final 静态变量的值也是在编译期就已经确定值了)而堆和栈不同,当调用 malloc free 这样C标准库函数时,堆可以在运行时动态扩缩。用户栈也是随着程序中函数调用和返回,进行入栈弹栈操作。(java的堆和栈略有不同,可以参考我的JVM篇知识)。

1.4 内存的分配管理

详细内容可以看我的【操作系统】专栏

        1、连续分配管理
  1.         单一连续分配
  2.         固定分区分配
  3.         动态分区分配

2、离散分配管理

1、分页存储

2、分段存储

3、段页式存储

3、虚拟内存管理

2、用户发起IO的基本流程

3、Linux五种IO模型

在《UNIX网络编程》一书中,总结归纳了5种IO模型:

  • 阻塞I0 (Blocking IO)
  • 非阻塞I0 (Nonblocking IO)
  • IO多路复用 (IO Multiplexing)
  • 信号驱动IO (SignalDriven IO)
  • 异步IO(AsynchronousIO)

五种不同的IO模型其实关注点都是在这个区域

1、阻塞IO

过程:用户发起recvfrom调用,若当前无法获得数据,用户会一直阻塞等待,直到内核完成数据获取、拷贝,并返回ok给用户,通知用户处理,用户进程才被唤醒。

阻塞IO,用户进程在用户在【内核尝试获取数据】,和【内核从内核缓存拷贝数据到用户缓存】,这两个阶段都是阻塞状态。

2、非阻塞IO

用户进程发起recvfrom调用之后会立即返回,而不是阻塞进程。数据拷贝阶段,用户进程仍然是阻塞的。

第一阶段一直轮询,用户进程并没有去做其他的事,并没有提高效率,忙等反而会导致CPU空转,整个系统效率反而不高。

3、IO多路复用

无论是阻塞IO还是非阻塞IO,用户应用在一阶段都需要调用recvfrom来获取数据,差别在于无数据时的处理方案:

如果调用recvfrom时,恰好没有数据,阻塞IO会使进程阻塞,非阻塞IO使CPU空转,都不能充分发挥CPU的作用。

如果调用recvfrom时,恰好有数据,则用户进程可以直接进入第二阶段,读取并处理数据

比如服务端处理客户端Socket请求时,在单线程情况下,只能依次处理每一个socket,如果正在处理的sket恰好未就绪(数据不可读或不可写),线程就会被阻塞,所有其它客户端socket都必须等待,性能自然会很差。

如果用户进程去监听多个Socket,只要某个套接字数据就绪了,可以开始真正的读写了,我们再去调用recvfrom呢

原本是一个服务员窗口,几十个顾客排队点餐。这种情况下,某一顾客一纠结吃啥,后面的人都只能等待。

现在是大家都坐在自己位置上,某个顾客想好了点什么,就叫服务员。

具体实现:

文件描述符(File Descriptor):简称FD,是一个从0 开始递增的无符号整数,用来关联Linux中的一个文件。

在Linux中,一切皆文件,例如常规文件、视频、硬件设备等,当然也包括网络套接字 (Socket)

IO多路复用:是利用单个线程来同时监听多个FD,并在某个FD可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。

第二阶段调用recvfrom之所以要循环,是因为第一阶段可能有多个socketFD准备就绪,

在第二阶段里被循环依次被处理

select系统调用可以接收多个被监听的套接字FD

而recvfrom只能监听一个FD

** 所以前面阻塞IO 和非阻塞IO其实都是逮着一只羊薅(只服务一个FD),而IO多路复用虽然第一阶段也是阻塞的,但是select本质上是一种批处理的思想,同时监听多个FD,只要有一个FD就绪,就先处理它。

监听FD的方式、通知的方式有多种,常见的方式:

  • select
  • poll
  • epoll

差异

  • select和poll只会通知用户进程有FD就绪,但不确定具体是哪个FD,需要用户进程逐个遍历FD来确认
  • epoll则会在通知用户进程FD就绪的同时,把已就绪的FD写入用户空间

1、Select模型

Select是Linux中最早的IO多路复用实现方案

fd_set 使用了位示图来表示fd状态。32个long型元素 即 32*32 =1024bit=1kB

select模式存在的问题:

  • 需要将整个fd_set从用户空间拷贝到内核空间,select结束还要再次拷贝回用户空间
  • select无法得知具体是哪个fd就绪,需要遍历整个fd_set
  • fd_set监听的fd数量不能超过1024

2、poll模式

fds 是一个结构体指针,同时它也是能构成一个自定义大小的结构体数组,fds+1即指向下一个数组元素。

该结构体内部由 fd,当前要监听的事件类型,和该事件真实发生的类型状态组成。

用户态调用poll函数,传入若干需要被监听fd结构体(每一个结构体实例就代表一个fd)构成结构体数组。

当系统进入内核态后,如果这些fd就绪,内核会修改这些fd对应的结构体实例的revent,最终把整个结构体数组返回给用户态。

IO流程

  1. 创建pollfd数组,向其中添加关注的fd信息,数组大小自定义调用poll函数,将pollfd数组拷贝到内核空间,转链表存储,无上限
  2. 内核遍历fd,判断是否就绪
  3. 数据就绪或超时后,拷贝pollfd数组到用户空间,返回就绪fd数量n
  4. 用户进程判断n是否大于0
  5. 大于0则遍历pollfd数组,找到就绪的fd

poll和Select 

1、poll没用监听fd数量限制,Select限制为1024,因为二者记录fd的数据结构不同。

2、Select和poll 都需要把 记录fd的数组 从用户态传入内核态。内核处理完后,同样从内核态拷贝回用户态

3、二者从内核态返回的fd数组,都没有直接指明具体是哪个fd就绪,需要用户逐一遍历,找到就绪fd

这就产生一个问题:数组容量变大了,但是任然需要遍历,样本空间变大的情况下,效率反而下降了。

3、epoll

红黑树  的特点 :有序、按序插入删除时间复杂度相对链表要更低。(O(lg2N)) 

epfd是 eventpoll实例的唯一标识,用户端调用几次epoll_crate, 内核就会创建几个eventpoll,并返回对应的epfd 标识。

select模式存在的三个问题

  • 能监听的FD最大不超过1024
  • 每次select都需要把所有要监听的FD都拷贝到内核空间
  • 每次都要遍历所有FD来判断就绪状态

poll模式的问题:


poll利用链表解决了select中监听FD上限的问题,但依然要遍历所有FD,如果监听较多,性能会下降

epoll模式中如何解决这些问题的?

  • 基于epoll实例中的红黑树保存要监听的FD,理论上无上限,而且增删改查效率都非常高,性能不会随监听的FD数量增多而下降
  • 每个FD只需要执行一次epoll ctl添加到红黑树,以后每次epol_wait无需传递任何参数,无需重复拷贝FD到内核空间
  • 内核会将就绪的FD直接拷贝到用户空间的指定位置,用户进程无需遍历所有FD就能知道就绪的FD是谁

lO多路复用-事件通知机制


当FD有数据可读时,我们调用epoll_wait就可以得到通知。但是事件通知的模式有两种:

LevelTriggered:简称LT。当FD有数据可读时,会重复通知多次,直至数据处理完成。是Epoll的默认模式

EdgeTriggered:简称ET。当FD有数据可读时,只会被通知一次,不管数据是否处理完成

举例:

1、假设一个客户端socket对应的FD已经注册到了epoll实例中

2、客户端socket发送了2kb的数据
3、服务端调用epoll_wait,得到通知说FD就绪服务端从

4、FD读取了1kb数据
5、回到步骤3(再次调用epoll wait,形成循环)

结论:
ET模式避免了LT模式可能出现的惊群现象

ET模式最好结合非阻塞IO读取FD数据,相比LT会复杂一些

4、信号驱动IO

信号驱动10是与内核建立SIGI0的信号关联并设置回调,当内核有FD就绪时,会发出SIGI0信号通知用户,期间用户应用可以执行其它业务,无需阻塞等待。

 

当有大量IO操作时,信号较多,SIGIO处理函数不能及时处理可能导致信号队列溢出

而且内核空间与用户空间的频繁信号交互性能也较低 

5、异步IO

异步IO的整个过程都是非阻塞的,用户进程调用完异步API后就可以去做其它事情,内核等待数据就绪并拷贝到用户空间后才会递交信号,通知用户进程。

异步IO要做好限流,防止异步IO请求请求过多,造成系统负荷过高。

IO操作是同步还是异步,关键看数据在内核空间与用户空间的拷贝过程(数据读写的10操作),也就是阶段是同步还是异步:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1058824.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RabbitMQ核心总结

AMQP协议核心概念 RabbitMQ是基于AMQP协议的,通过使用通用协议就可以做到在不同语言之间传递。 server:又称broker,接受客户端连接,实现AMQP实体服务。 connection:连接和具体broker网络连接。 channel&#xff1a…

Linux文件系统及命令 | 实用操作指令汇总

目录 ctrl c 强制停止与ctrl d 退出或登出 history:历史命令搜索 clear:清屏 ln命令:创建软硬连接 cat命令:显示文件命令 less命令:查看大文件 grep命令:正则表达式使用 sort命令:排序 uniq命令…

世界前沿技术发展报告2023《世界航天技术发展报告》(五)太空探索技术

(五)太空探索技术 1. 概述2. 月球探索规划和探索活动2.1 美国推进“阿尔忒弥斯”项目实施,并公布月球候选着陆区2.2 NASA“猎户座”飞船成功发射并完成无人绕月飞行任务2.3 美国拟建立“地月空间高速公路巡逻系统”以监测地月空间2.4 美国成功…

kubernetes-工作负载-Deployment

文章目录 前言Deployment 的作用Deployment 语法查看 Deployment 状态管理模式DeploymeStatus参考 前言 Kubernetes 提供了几个内置的 API 来声明式管理工作负载及其组件。 最终,你的应用以容器的形式在 Pods 中运行; 但是,直接管理单个 Po…

SpringBoot第三方登录JustAuth

JustAuth流程 创建授权请求,并跳转到授权页面,以便用户进行认证和授权生成一个随机的 stateId,用于标识本次授权请求封装到 Map 中作为响应返回给客户端处理授权成功后回调的请求调用 AuthRequest 的 login() 方法完成授权AuthResponse 对象…

Servlet执行流程生命周期方法介绍体系结构、Request和Response的功能详解

🐌个人主页: 🐌 叶落闲庭 💨我的专栏:💨 c语言 数据结构 javaEE 操作系统 Redis 石可破也,而不可夺坚;丹可磨也,而不可夺赤。 Servlet 一、 Servlet执行流程二、Servlet生…

【Java】Stream的基本使用

Stream特点 Stream的一系列操作组成了Stream的流水线, Stream流水线包含: 数据源: 这里的数据源可能是集合/数组, 可能是生成器, 甚至可能是IO通道(Files.lines)零个或多个中间操作: 中间操作会导致流之间的转化, 如filter(Predicate)一个终端操作: 终端操作会产生最终所需要的…

德国自动驾驶卡车公司【Fernride】完成1900万美元A轮融资

来源:猛兽财经 作者:猛兽财经 猛兽财经获悉,总部位于德国沃尔夫斯堡的自动驾驶卡车公司【Fernride】今日宣布已完成1900万美元A轮融资,本轮融资完成后Fernride的融资金额已经达到了达到5000万美元。 本轮融资由Deep Tech and Cli…

初步了解nodejs语法和web模块

在此, 第一个Node.js实例_js firstnode-CSDN博客 通过node运行一个简单的server.js,实现了一个http服务器; 但是还没有解析server.js的代码,下面看一下; require 指令 在 Node.js 中,使用 require 指令来…

MySQL-基础

MySQL 1.SQL语句 1.1数据库 -- 创建数据库 create database review character set 字符集 create database if not exists review charset 字符集 collate 比较规则 -- 查看表的创建细节 show create database review -- 修改数据库 alter database review charset utf8mb4 …

Springboot——关于Springboot线程池时使用ThreadLocal 类的一个小小的漏洞

问题描述 前端的使用ajax发送了一个请求到后端 后端自定义了一个线程上下文和实现了一个拦截器Interceptor public class BaseContext {public static ThreadLocal<Integer> threadLocal new ThreadLocal<>();public static void setCurrentId(int id) {threadL…

javaWeb医疗管理系统

一、引言 1.1 系统背景 医疗行业一直是一个高度复杂和信息密集的领域。现代医院需要有效管理患者信息、医生信息、药物信息以及医疗记录等。本项目旨在通过开发一个JavaWeb医疗管理系统来满足这些需求。 1.2 目的和范围 这个系统的主要目标是帮助医院提高患者管理和医疗记录…

竞赛 机器视觉opencv答题卡识别系统

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 答题卡识别系统 - opencv python 图像识别 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分…

解决u盘在我的电脑中重复显示两个

删除注册表&#xff1a; [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\DelegateFolders\{F5FB2C77-0E2F-4A16-A381-3E560C68BC83}]

800*B. Long Long(贪心)

解析&#xff1a; 因为可以无限操作&#xff0c;所以最大值即为全部数字的绝对值&#xff0c;次数为连续负数区间的个数。 #include<bits/stdc.h> using namespace std; #define int long long const int N2e55; int t,n,a[N]; signed main(){scanf("%lld",&a…

力扣-367.有效的完全平方数

暴力 class Solution { public:bool isPerfectSquare(int num) {for(long i 1; i * i < num; i) {if(i * i num) return true;}return false;} };二分查找 class Solution { public:bool isPerfectSquare(int num) {int left 1, right num;while(left < right) {in…

Linux用户和权限

目录 1、root用户&#xff08;超级管理员&#xff09; su和exit命令 sudo命令 2、用户和用户组管理 用户组管理 用户管理 getent命令 3、查看权限控制信息 4、修改权限控制&#xff1a;chmod命令 5、修改权限控制&#xff1a;chown命令 1、root用户&#xff08;超级管…

Django之十三、添加用户之原始方法实现

修改urls.py path("user/add/", views.user_add),添加user_add.html {% extends layout.html %} {% block content %}<div class"container"><div class"panel panel-default"><div class"panel-heading"><h3 c…

AAD基础知识(identity/token/PRT)

简介 AAD(Azure Active Directory/Azure AD)是微软基于云身份验证和访问控制的解决方案&#xff0c;通过SSO登录其他o365应用(word/outlook/teams…) 微软在2023年7月把AAD重命名为Microsoft Entra ID&#xff0c;官网&#xff1a;https://www.microsoft.com/zh-cn/security/b…

LabVIEW开发带式谱感测技术

LabVIEW开发带式谱感测技术 如今&#xff0c;通过无线网络传输的数据量正在迅速增加&#xff0c;并导致频谱稀缺。超过数十亿的无线设备将被连接起来&#xff0c;并需要互联网接入。因此&#xff0c;无线电频谱管理方案的效率不足以授予对所有设备的访问权限。在频谱分配中&am…