Linux——进程控制

Linux——进程控制

  • fork()
    • 缺页中断
  • 进程终止
  • 进程异常
  • exit
  • _exit
  • 进程等待
    • wait
    • waitpid
      • status
      • WIFEXITED
  • 多进程等待
  • 阻塞等待和非阻塞等待
  • 进程替换
    • 单进程的进程替换
      • execl
      • execlp
      • execv
      • execle

fork()

我们之前是接触过这个函数的,这个函数我们之前是要来创建子进程的函数,我们今天来深入学习一下这个函数:

在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

#include <unistd.h>
pid_t fork(void);
//返回值:自进程中返回0,父进程返回子进程id,出错返回-1

返回值:子进程中返回0,父进程返回子进程id,出错返回-1。

那么fork做了哪些事情呢?

  1. 创建子进程: 调用 fork() 函数时,操作系统会创建一个几乎与父进程完全相同的子进程。这意味着子进程继承了父进程的内存、文件描述符、环境变量等。
  2. 返回值: fork() 函数在父进程中返回子进程的进程ID(PID),而在子进程中返回0。这是为了让父进程和子进程能够区分彼此。通常,父进程可以通过检查返回值来确定它是父进程还是子进程。
  3. 复制父进程: 子进程是通过复制父进程的地址空间来创建的,这包括父进程的代码、数据和堆栈。子进程独立于父进程运行,但它们之间的数据不会共享,除非使用进程间通信(Inter-Process Communication,IPC)机制。
  4. 并行执行: 一旦子进程创建成功,父进程和子进程可以并行执行,各自独立运行。

我们可以写段代码来验证一下:
在这里插入图片描述在这里插入图片描述
我们可以看到第一个29374是父进程的pid,返回值29375是子进程的id,而下面的29375是子进程,返回值是0。

这个过程是怎样的呢?
在这里插入图片描述
在调用 fork() 函数后,父进程会继续执行其后的代码。fork() 函数创建了一个新的子进程,该子进程是在调用 fork() 时父进程的几乎完全复制。父进程和子进程将几乎同时开始执行下面的代码。

缺页中断

我们之前讲过进程地址空间,我们说父进程和子进程都是一个相同的虚拟地址,然后映射到不同的物理内存中。
但在子进程修改自己的值之前,父进程和子进程都是映射到相同的物理内存上,那就有一个问题:操作系统怎样知道在哪个时间完成对子进程的修改呢?

其实父子进程任意一方想修改的时候,这个时候会发生写时拷贝,同时父子进程的权限都会改为只读

这个时候我们修改,就会报错,但是操作系统过来看看,发现原本是读写的权限,被改为了只读,就会判定这个不是错误,会在物理内存中重新找一块空间重新建立映射关系到父或子进程。但这个过程我们是不知道的。
在这里插入图片描述
我们称这样的机制为缺页中断

缺页中断(Page Fault)是计算机操作系统中的一个重要概念,它发生在程序试图访问虚拟内存中的一个页面(或页)时,但该页面当前不在物理内存中。当发生缺页中断时,操作系统会介入处理,以便将需要的页面加载到物理内存中,从而使程序能够继续执行。

下面是关于缺页中断的一些重要信息:

  1. 虚拟内存和分页系统: 大多数现代操作系统使用虚拟内存和分页系统来管理内存。这意味着每个进程看到的内存都是虚拟内存,而不是物理内存。虚拟内存被划分成固定大小的页面(通常为4KB),这些页面被映射到物理内存。
  2. 页面不在物理内存中: 当程序尝试访问一个在虚拟内存中但不在物理内存中的页面时,会触发缺页中断。这可能是因为该页面从物理内存中换出(置换)以腾出空间给其他页面,或者是因为该页面是程序首次访问的部分。
  3. 操作系统响应: 当发生缺页中断时,操作系统会介入,根据程序访问的虚拟地址,找到相应的页面,并将其加载到物理内存中的合适位置。这通常涉及从磁盘上的页面文件或其他存储设备中读取数据。一旦页面被加载到物理内存中,程序可以继续执行,就好像该页面一直在内存中一样。
  4. 页表: 缺页中断的处理依赖于页表,页表是操作系统维护的数据结构,它将虚拟地址映射到物理地址。操作系统使用页表来确定缺页中断的地址映射关系,以便正确地加载页面。
    性能影响: 缺页中断是计算机性能的重要因素之一。频繁的缺页中断会导致程序执行速度减慢,因为加载页面需要时间。因此,操作系统会尽量减少缺页中断的发生,通过使用高效的页面置换算法和合理的内存管理策略来优化性能。

那么什么时候会发生缺页中断呢?

  1. 首次访问页面: 当程序首次访问虚拟内存中的一个页面时,该页面通常不在物理内存中,因此会触发缺页中断。这是因为操作系统采用了一种"延迟加载"策略,只有在页面被首次访问时才将其加载到物理内存,从而节省内存空间。
  2. 页面被置换出: 如果操作系统需要腾出物理内存空间以容纳其他页面,它会选择一些不常用的页面进行置换(将其移出物理内存)。当程序再次访问这些页面时,会触发缺页中断,因为它们不再在物理内存中。
  3. 页面被交换到磁盘或其他存储设备: 某些操作系统具有交换(Swap)机制,这允许将页面从物理内存交换到磁盘或其他存储设备以释放内存。当程序尝试访问已经交换到磁盘上的页面时,会触发缺页中断,因为它们不在物理内存中。
  4. 多任务操作系统切换进程: 在多任务操作系统中,当操作系统决定切换执行另一个进程时,当前进程的页面可能会被置换出物理内存。当再次切换回该进程时,会触发缺页中断,因为所需的页面不再在内存中。
  5. 共享内存: 如果多个进程共享某些内存区域,而其中一个进程对共享内存进行了修改,而其他进程需要访问这个修改后的数据,可能会触发缺页中断。这是因为修改后的数据需要被加载到其他进程的物理内存中。

我们的父子进程属于第5种情况。

进程终止

我们之前写程序的时候,总会在最后一行写:
在这里插入图片描述
其实我们都大概可以猜得到,这个0可能代表了状态,我们的父进程bash会接收这个返回值,我们可以用**$?**来查看这个返回值:
在这里插入图片描述
其实我们返回值,通常是想知道这个程序到底运行的怎么样。我们一般有三种状态:

  1. 运行完毕,结果正确。
  2. 运行完毕,结果不正确。
  3. 运行异常。

我们先来看前两种情况,我们一般来说,返回值为0表示运行完毕结果正确,非0值表示运行完毕,结果不正确。

其中,不同的值对应不同的状态,我们可以用strerror来查看不同数字所对应的不同的退出状态。
在这里插入图片描述
在这里插入图片描述
果然和我们猜的一样,0代表成功。其他数字都代表了不同的错误状态。
我们把这些代表不同状态的数字叫做退出码

其中 ** ?**这个字符会保存上一个操作的退出码:
在这里插入图片描述
但是这个可能不准,所以不能太依赖这个。

有时候,程序运行并不一定会成功,这个时候我们可以把程序在执行过程中返回的数字储存起来,到最后我们可以打印出来看:

这个用来储存的东西叫做errno
在这里插入图片描述
在这里插入图片描述

进程异常

我们之前讨论过,程序的三种状态跑完 正确,跑完 程序不正确,程序异常,这里我们来看看程序异常:
程序异常,跟前面两种情况都不是很一样,前面两种情况,起码还有一个返回值(成功为0,不成功为非0),但是如果程序异常,直接结束,下面的代码直接不执行。
我们来模拟一下最常见的异常:
在这里插入图片描述
在这里插入图片描述我们发现我们之前写的printf那一句话,没有机会执行,因为a /= 0的异常,程序直接终止掉了。
这里程序终止的原因是因为,这个进程在出现异常时,会向操作系统发出异常的信号,让操作系统直接杀掉这个进程。
我们看一下,会有哪些信号会让操作系统杀掉进程:
在这里插入图片描述这些信号发给操作系统,操作系统就会杀掉这个进程。前面的数字代表了相应的异常,我们可以向操作系统发出相应的异常信号,帮我们杀掉某些进程:
我们模拟一个正确的场景:
在这里插入图片描述在这里插入图片描述
我们向操作系统发送了25127有11号异常,操作系统会以这个11号异常的名义杀掉25127这个进程。
最后退出码和信号就是两个数字,通过这两个数字我们可以推断这个程序的状态。

exit

我们之前讨论过退出码这个概念,那么如果在不同的部分退出,会有什么不同的现象呢?
在这里插入图片描述现在我们函数中有一个退出码,主函数中有一个退出码,我们看最后程序是哪个退出码?
在这里插入图片描述
我们看到最后程序是以主函数的退出码退出的,那表明了一件事就是:函数中的退出码只是表示函数调用的结束,并不能代表最后程序的状态。

我们之前也接触过** exit() **这个函数,我们来看看这个函数是不是和return n的效果是一样的:在这里插入图片描述
在这里插入图片描述
我们发现退出码变成了0,其实我们可以查一下exit的手册:
在这里插入图片描述
手册上面提到,exit可以引起一个正常进程的结束,我们exit中的这个参数就是我们程序返回的退出码,程序遇到exit直接结束而且以exit中的这个参数作为退出码的。

_exit

其实我们除了exit,还有一个函数跟它长得很像,_exit(),我们可以查一下它的手册:
在这里插入图片描述
这个_exit也是可以立即结束一个进程,我们来试试:
在这里插入图片描述那么这个函数和exit有什么区别呢?我们来看看:
在这里插入图片描述注意一下我们这里的printf是带有行刷新的,这个对于观察之后的现象有比较关键的作用。
在这里插入图片描述
现象很正常,我们现在把换行符去掉:
在这里插入图片描述这时候一开始是什么也没有的:
在这里插入图片描述
三秒钟过后:
在这里插入图片描述

我们发现I am a process被刷出来了,说明exit是会强制刷新缓冲区的。
那我们来看看_exit:
在这里插入图片描述在这里插入图片描述也符合我们的预期,我们现在来试试将换行符去掉:
在这里插入图片描述一开始也是什么也没有:
在这里插入图片描述三秒钟过后:
在这里插入图片描述我们发现了,_exit不会强制刷新缓冲区。

其实exit是由_exit封装的,两者的区别一个是库函数,一个是系统函数之所以exit会刷新缓冲区,是因为缓冲区就在这个库里面,exit可以直接对这个库进行操作。

进程等待

进程等待呢,在我的理解下,就是进程等待相应资源的过程。
那么为什么要进行进程等待呢?以我们现有的知识水平来看,有那么以下几点:

  1. 解决相应的僵尸进程的内存泄漏问题。
  2. 可以收集对应的子进程完成任务的状态。

了解到上面两点之后,我们再来看看进程等待的函数wait和waitpid

wait

我们先来看看wait的手册:
在这里插入图片描述返回值:
在这里插入图片描述

我们发现wait要传一个int*的指针参数,我们暂时先不管,先传NULL值使用一下:
我们来写一段测试代码:
在这里插入图片描述
这里子进程结束了之后,父进程会休眠5秒,这个时候子进程就会变成僵尸进程,我们写一段监控脚本来监视一下:
在这里插入图片描述我们看到在某个时刻子进程变成了僵尸进程。因为这时候父进程在睡眠,没有回收子进程。
在这里插入图片描述在五秒结束之后,父进程回收了子进程,这个时候子进程的僵尸状态会被父进程回收。僵尸状态消失。
这里顺便说一下,如果这里我们的父进程不睡眠的话,这时候我们的父进程会阻塞等待。直到回收子进程。

waitpid

了解了wait之后,我们来了解一下waitpid:
在这里插入图片描述

waitpid的一个参数是pit_t pid 这个可以表示我们要指定回收哪个进程,比如这个数字是12345,我就要回收12345号进程。
后面两个参数我们暂时先不用管,用NULL和0先顶替着。
我们测试只需改变这一行:
在这里插入图片描述
我们来测试一下:
我们看到是和wait一模一样的:
在这里插入图片描述

status

我们之前在wait和waitpid中有一个int* status 的指针,这个参数是干嘛的呢?其实这个参数是输出型参数,意思是我们先把这个参数输进去,之后它又会返回给我们:
在这里插入图片描述我们可以来试试,这里为了测试,我们将子进程的退出码设为10:
在这里插入图片描述
测试一下:
在这里插入图片描述
我们看到status的返回值是2560,这跟我们的10差的有点远啊,这是为什么呢?
其实我们的status被输入进去之后,被划分了区域:
在这里插入图片描述
我们的次低8位描述的是退出状态,第七位是core dump标志,剩下的位数表示终止信号。
我们的0这个数字有32个比特位,就被分成了这样几个区域:

如果我们想拿到我们的退出状态和终止信号的话我们得对我们的status进行一些位运算来帮助我们拿到真实的数字。

在这里插入图片描述测试一下:
在这里插入图片描述我们看到end state退出状态是10,退出信号是0,跟我们的逻辑是符合的。
我们来试试程序异常会怎么样:
在这里插入图片描述
在这里插入图片描述

WIFEXITED

如果上面的方法有点过于复杂了,我们定义了一个WIFEXITED的宏帮我们判断退出状态:
在这里插入图片描述在这里插入图片描述如果有异常,返回的值就不为1:
在这里插入图片描述在这里插入图片描述

多进程等待

我们进程等待,我们可以多进程等待:
在这里插入图片描述
在这里插入图片描述

阻塞等待和非阻塞等待

我们waitpid还有最后一个参数没有讲解:options

在这里插入图片描述
这里我们的options我们有两种选择:

0:阻塞等待
WNOHANG:以非阻塞方式等待。

在这里插入图片描述
啥叫阻塞等待呢,阻塞等待就是操作系统很老实,在等待资源的过程中,它不会去做其他的事情,就乖乖的等待资源。非阻塞就不一样,在等待资源的过程中,抽空去做其他的事情。

我们可以写一段代码来验证:
在这里插入图片描述
在这里插入图片描述
我们也可以把父进程具体在做什么写的清楚一点:
在这里插入图片描述

进程替换

我们之前创建的子进程都是父进程的一部分,现在我们想要让子进程独立于父进程,是一个全新的进程,那该怎么办呢?

单进程的进程替换

在进行进程替换前,我们的了解一下,进程替换的接口:
在这里插入图片描述这些都是进程替换的接口,我们一个一个来试:

execl

我们看到execl是要指定文件路径,加上可变参数列表:
在这里插入图片描述

在这里插入图片描述
我们发现我们写的程序可以和ls指令有相同的效果,并且我们发现我们最后一行的printf并没有打印出来,这是为什么呢?这个我们暂时先放在一边,我们先来看看多进程的进程替换:
在这里插入图片描述在这里插入图片描述
这里程序替换之后,我们的子程序就和原来的程序没有任何的关系了,变成了一个不依赖父进程的独立程序,所以在被替换之后,之后的代码子进程也不会管,所以最后一个printf没有打印,但是,子进程虽然不依赖父进程了。但是依然存在父子关系,所以父进程可以等待成功。

我们可以获取一下execl的退出值:
在这里插入图片描述在这里插入图片描述这个时候我们发现,没有返回值,因为进程都被替换了,不执行后面的代码了。
如果我们输入指令的地址根本就是错的,或者这个指令本来就是错的:
在这里插入图片描述这个时候就有返回值了,并且是-1:
在这里插入图片描述

execlp

除了我们的execl这种方法之外,我们还有其他的方法来进行进程替换,比如,我们来看execlp:
在这里插入图片描述这个的名字中带了个p,表示它是在环境变量中去找的
在这里插入图片描述在这里插入图片描述

execv

在这里插入图片描述
这里的argv是一个指针数组,用于存放我们要执行的命令:
在这里插入图片描述
在这里插入图片描述
现在我们都替换的是系统的程序,我们可不可以调用我们自己写的程序呢?
我们创建一份C++代码:
在这里插入图片描述
修改一下我们的Makefile
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

execle

我们已经了解了带 l 的,还有带v的,现在我们来看看带e的:
在这里插入图片描述看这个样子的话,应该是把父进程的环境变量导入到子进程中,这个过程中我们会用到putenv这个函数:
在这里插入图片描述
这个函数是方便我们在程序之中添加我们的环境变量:
在这里插入图片描述
这个时候我们不调用execle这个函数,我们看看子进程mytest的变化:
在这里插入图片描述我们发现在环境变量表的最后发现了我们添加的环境变量,诶?但是我们还没有向子进程导入我们的环境变量呀?
这里要注意一下子进程本身就会继承父进程的环境变量,不受进程替换的影响,其实不需要我们调用什么接口去继承。但是既然提供了,我们还是可以用一下:
在这里插入图片描述顺便可以把命令行参数打印出来:
在这里插入图片描述执行一下:
在这里插入图片描述我么看到子进程继承了父进程的环境变量。
注意我们也可以传我们自己的表,但这会覆盖我们原有的环境变量表,所以如果我们想新增的话,我们的先提前加好才可以。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

生命科学领域 - FAIR原则和如果使数据FAIR化

2016年&#xff0c;《Scientific Data》发表了《科学数据管理和监督的FAIR指导原则》&#xff08;FAIR Guiding Principles for scientific data management and stewardship&#xff09;。文章旨在提供指导方针&#xff0c;以提高数字资产的可发现性、可访问性、互操作性和重用…

一些RLHF的平替汇总

卷友们好&#xff0c;我是rumor。 众所周知&#xff0c;RLHF十分玄学且令人望而却步。我听过有的小道消息说提升很大&#xff0c;也有小道消息说效果不明显&#xff0c;究其根本还是系统链路太长自由度太高&#xff0c;不像SFT一样可以通过数据配比、prompt、有限的超参数来可控…

【论文解读】FFHQ-UV:用于3D面部重建的归一化面部UV纹理数据集

【论文解读】FFHQ-UV 论文地址&#xff1a;https://arxiv.org/pdf/2211.13874.pdf 0. 摘要 我们提出了一个大规模的面部UV纹理数据集&#xff0c;其中包含超过50,000张高质量的纹理UV贴图&#xff0c;这些贴图具有均匀的照明、中性的表情和清洁的面部区域&#xff0c;这些都是…

【数据预处理2】数据预处理——数据标准化

数据标准化 1. 什么是标准化&#xff1f;   数据标准化是一个常用的数据预处理操作&#xff0c;目的是将不同规格的数据转换到统一规格或不同分布的数据转换到某个特定范围&#xff0c;以减少规模、特征、分布差异等对模型的影响。这种操作也叫作无量纲化。   除了用作模型…

【【萌新的SOC学习之 VDMA 彩条显示实验之一】】

萌新的SOC学习之 VDMA 彩条显示实验之一 实验任务 &#xff1a; 本章的实验任务是 PS写彩条数据至 DDR3 内存中 然后通过 VDMA IP核 将彩条数据显示在 RGB LCD 液晶屏上 下面是本次实验的系统框图 VDMA 通过 HP接口 与 PS端的 DDR 存储器 进行交互 因为 VDMA 出来的是 str…

【数据预处理3】数据预处理 - 归一化和标准化

处理数据之前&#xff0c;通常会使用一些转换函数将「特征数据」转换成更适合「算法模型」的特征数据。这个过程&#xff0c;也叫数据预处理。 比如&#xff0c;我们在择偶时&#xff0c;有身高、体重、存款三个特征&#xff0c;身高是180、体重是180、存款是180000&#xff1…

SpringBoot 整合 Freemarker

通过 Freemarker 模版&#xff0c;我们可以将数据渲染成 HTML 网页、电子邮件、配置文件以及源代码等。 Freemarker 不是面向最终用户的&#xff0c;而是一个 Java 类库&#xff0c;我们可以将之作为一个普通的组件嵌入到我们的产品中。 Freemarker 模版后缀为 .ftl(FreeMarke…

python算法例10 整数转换为罗马数字

1. 问题描述 给定一个整数&#xff0c;将其转换为罗马数字&#xff0c;要求返回结果的取值范围为1~3999。 2. 问题示例 4→Ⅳ&#xff0c;12→Ⅻ&#xff0c;21→XⅪ&#xff0c;99→XCIX。 3. 代码实现 def int_to_roman(num):val [1000, 900, 500, 400,100, 90, 50, 40…

【DevOps】Git 图文详解(四):Git 使用入门

Git 图文详解&#xff08;四&#xff09;&#xff1a;Git 使用入门 1.创建仓库2.暂存区 add3.提交 commit 记录4.Git 的 “指针” 引用5.提交的唯一标识 id&#xff0c;HEAD~n 是什么意思&#xff1f;6.比较 diff 1.创建仓库 创建本地仓库的方法有两种&#xff1a; 一种是创建…

(Matalb时序预测)PSO-BP粒子群算法优化BP神经网络的多维时序回归预测

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、部分程序&#xff1a; 四、完整程序数据说明文档下载&#xff1a; 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matalb平…

Java Swing算术我最棒

内容要求 1) 本次程序设计是专门针对 Java 课程的,要求使用 Java 语言进行具有一定代码量的程序开发。程序的设计要结合一定的算法&#xff0c;在进行代码编写前要能够设计好自己的算法。 本次程序设计涉及到 Java 的基本语法&#xff0c;即课堂上所介绍的变量、条件语句、循…

vuedraggable拖拽列表设置某一条元素禁止被拖拽

直接上代码 <draggable filter".unDrag"><div class"unDrag">不能拖拽</div><div class"canDrag">可以拖拽</div> </draggable>一、设置filter 在draggable节点的属性filter设置不可拖拽的class名&#…

3D全景视角,足不出户感知真实场景的魅力

近年来&#xff0c;随着科技的快速发展&#xff0c;普通的平面静态视角已经无法满足我们了&#xff0c;不管是视角框架的限制还是片面的环境展示&#xff0c;都不足以让我们深入了解场景环境。随着VR全景技术的日益成熟&#xff0c;3D全景技术的出现为我们提供了全新的视觉体验…

uni-app(1)pages. json和tabBar

第一步 在HBuilderX中新建项目 填写项目名称、确定目录、选择模板、选择Vue版本&#xff1a;3、点击创建 第二步 配置pages.json文件 pages.json是一个非常重要的配置文件&#xff0c;它用于配置小程序的页面路径、窗口表现、导航条样式等信息。 右键点击pages&#xff0c;按…

Kafka(四)消费者消费消息

文章目录 如何确保不重复消费消息&#xff1f;消费者业务逻辑重试消费者提交自定义反序列化类消费者参数配置及其说明重要的参数session.time.ms和heartbeat.interval.ms和group.instance.id增加消费者的吞吐量消费者消费的超时时间和poll()方法的关系 消费者消费逻辑启动消费者…

遗传算法GA-算法原理与算法流程图

本站原创文章&#xff0c;转载请说明来自《老饼讲解-BP神经网络》bp.bbbdata.com 目录 一、遗传算法流程图 1.1. 遗传算法流程图 二、遗传算法的思想与机制 2.1 遗传算法的思想 2.2 遗传算法的机制介绍 三、 遗传算法的算法流程 3.1 遗传算法的算法…

PXE高效批量网络装机

目录 一.PXE 1. 系统装机的三种引导方式 2. 系统安装过程 3. 光盘安装相关文件 4. PXE简介 5. 实现过程 6. PXE优点 二.PXE实现过程 1. 实验准备 2. 搭建DHCP服务器 3. 配置TFTP服务器 4. 准备pxelinu.0文件 5. 挂载镜像准备内核、驱动文件 6. 手写配置文件 7. 准…

强烈 推荐 13 个 Web前端在线代码IDE

codesandbox.io&#xff08;国外&#xff0c;提供免费空间&#xff09; 网址&#xff1a;https://codesandbox.io/ CodeSandbox 专注于构建完整的 Web 应用程序&#xff0c;支持多种流行的前端框架和库&#xff0c;例如 React、Vue 和 Angular。它提供了一系列增强的功能&…

springboot项目中获取业务功能的导入数据模板文件

场景: 在实际业务场景中,经常会遇到某些管理功能需要数据导入共功能,但既然是导入数据,肯定会有规则限制,有规则就会有数据模板,但这个模板一般是让客户自己下载固定规则模板,而不是让客户自己随便上传模板。下面介绍直接下载模板 一、下载模板示例 1、在项目的…

信安.网络安全.UDP协议拥塞

第一部分 如何解决UDP丢包问题 一、UDP 报文格式 每个 UDP 报文分为 UDP 报头和 UDP 数据区两部分。报头由 4 个 16 位长&#xff08;2 字节&#xff09;字段组成&#xff0c;分别说明该报文的源端口、目的端口、报文长度和校验值。UDP 报文格式如图所示。 UDP 报文中每个…