UNIX环境高级编程——进程控制

news2024/11/27 2:44:34

8.1 引言

本章介绍UNIX系统的进程控制,包括:

  • 创建新进程、执行程序、进程终止
  • 进程属性ID——实际有效保存用户ID组ID
  • 解释器文件
  • system函数
  • 进程会计机制

8.2 进程标识

进程ID:一个非负整数,进程的唯一标识

  • 进程ID可复用:当某个进程终止后,它的进程ID可被之后创建的进程复用;
  • 几个专用进程
    (1)进程ID为0的是调度进程,也称交换进程,它是内核中的系统进程;
    (2)进程ID为1的是init进程,此进程负责在自举内核后启动一个UNIX系统,init进程绝不会终止,它是一个以超级用户特权运行的普通用户进程;
    (3)进程ID为2的是页守护进程,负责支持虚拟存储器系统的分页操作。

UNIX系统提供了返回进程某些标识符的函数:

#include <unistd.h>

pid_t getpid(void);
										// 返回值:调用进程的进程ID
pid_t getppid(void);
										// 返回值:调用进程的父进程ID
uid_t getuid(void);
										// 返回值:调用进程的实际用户ID
uid_t geteuid(void);
										// 返回值:调用进程的有效用户ID
gid_t getgid(void);
										// 返回值:调用进程的实际组ID
gid_t getegid(void);
										// 返回值:调用进程的有效组ID

8.3 函数fork

fork函数用于创建一个新进程:

#include <unistd.h>

pid_t fork(void);
										// 子进程返回0,父进程返回子进程ID;若出错,返回-1
  • fork调用一次,返回两次,子进程返回0,父进程返回子进程ID;
  • 一个进程的子进程可以有多个,但父进程只有一个;
  • 子进程是父进程的副本,子进程得到父进程的数据空间、堆和栈的副本,子进程和父进程共享正文段;
  • 由于在fork之后经常跟随着exec,所以很多实现并不执行一个父进程数据段、栈和堆的完全副本,而是采用写时复制(Copy-On-Write,COW)技术,这些区域由父进程和子进程共享,且内核将它们的访问权限改为只读,如果父进程和子进程中的任一个试图修改这些区域,则内核只为修改区域的那块内存制作一个副本,通常是虚拟内存系统中的“一页”;
  • fork之后是父进程还是子进程先执行是不确定的;
  • 父进程的所有打开文件描述符都被复制到子进程,每个相同的打开描述符共享一个文件表项;
    在这里插入图片描述

8.4 函数vfork

vfork函数的调用序列和返回值与fork相同,但二者语义不同:

  • vfork用于创建一个新进程,该新进程的目的是执行一个新程序,vfork不会将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec(或exit),所以也就不会引用该地址空间,不过子进程在调用execexit前,它在父进程的空间中执行;
  • vfork保证子进程先运行,在它execexit之后父进程才可能被调度运行,当子进程调用这两个函数中的任意一个时,父进程会恢复运行。

8.5 函数exit

进程有5种正常终止及3种异常终止方式。

5种正常终止方式:

  • main函数内执行return语句,等效于调用exit
  • 调用exit函数;
  • 调用_exit_Exit函数;
  • 进程的最后一个线程在其启动例程中执行return语句,但该线程的返回值不用作进程的返回值,进程以终止状态0返回;
  • 进程的最后一个线程调用pthread_exit函数,进程以终止状态0返回。

3种异常终止方式:

  • 调用abort,产生SIGABRT信号;
  • 当进程接收到某些信号,信号可由进程自身(如调用abort函数)、其他进程或内核产生;
  • 最后一个线程对“取消”请求做出响应,默认情况下,“取消”以延迟方式产生:一个线程要求取消另一个线程,若干时间后,目标线程终止。

关于父进程、子进程先后终止的两种情况:

  • 对于父进程已经终止的所有进程,它们的父进程都改变为init进程,称这些进程由init进程收养;
  • 内核为每个终止子进程保存了一定量的信息,所以当终止进程的父进程调用waitwaitpid时,可以得到这些信息;
  • 一个已经终止、但是其父进程尚未对其进行善后处理(获取终止子进程的有关信息、释放它仍占用的资源)的进程被称为僵死进程
  • 只要有一个由init收养的子进程终止,init就会调用一个wait函数取得其终止状态,所以init的子进程永远不会成为僵死进程。

8.6 函数wait和waitpid

当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号,因为子进程终止是个异步事件(这可能在父进程运行的任何时候发生),所以这种信号也是内核向父进程发送的异步通知。

调用waitwaitpid的进程可能会:

  • 如果其所有子进程都还在运行,则阻塞;
  • 如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回;
  • 如果它没有任何子进程,则立即出错返回。
#include <sys/wait.h>

pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
										// 两个函数返回值:若成功,返回进程ID;若出错,返回0或-1

wait函数等待任一终止子进程:

  • 如果子进程已经终止,并且是一个僵死进程,则wait立即返回并取得该子进程的状态;否则wait使其调用者阻塞,直到一个子进程终止。如果调用者阻塞而且它有多个子进程,则在其某一个子进程终止时,wait就立即返回其终止子进程的进程ID;
  • 参数statloc是一个整型指针,若不为空,则终止进程的终止状态存放其中,若不关心终止状态,可将该指针指定为空指针;终止状态用定义在<sys/wait.h>中的各个宏来查看,有4个互斥的宏可用来取得进程终止的原因,基于这4个宏中哪一个值为真,就可选用其他宏来取得退出状态、信号编号等:
    在这里插入图片描述

waitpid函数等待一个特定的进程,其pid参数的作用解释如下:

  • pid == -1:等待任一子进程,等效于wait
  • pid > 0:等待进程ID与pid相等的子进程;
  • pid == 0:等待组ID等于调用进程组ID的任一子进程;
  • pid < -1:等待组ID等于pid绝对值的任一子进程。

options参数可进一步控制waitpid的操作,此参数或者是0,或者是下面常量按位或运算的结果:
在这里插入图片描述
waitpid函数提供了wait函数没有提供的3个功能:

  • waitpid函数可等待一个特定进程,而wait则返回任一终止子进程的状态;
  • waitpid提供了一个wait的非阻塞版本;
  • waitpid通过WUNTRACEDWCONTINUED选项支持作业控制。

8.7 函数waitid

waitid函数也用于获取进程终止状态,它比waitpid更灵活:

#include <sys/wait.h>

int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
										// 返回值:若成功,返回0;若出错,返回-1
  • id参数指定要等待的子进程,它的作用与idtype的值有关:
    在这里插入图片描述
  • options参数是下图中个标志的按位或运算,这些标志指示调用者关注哪些状态变化,且WCONTINUEDWEXITEDWSTOPPED这3个常量之一必须在options参数中指定;
    在这里插入图片描述
  • infop参数是指向siginfo结构的指针,该结构包含了造成子进程状态改变有关信号的详细信息。

8.8 函数wait3和wait4

wait3wait4函数提供了获取终止进程及其所有子进程使用资源概况的功能,资源统计信息包括用户CPU时间总量、系统CPU时间总量、缺页次数、接收到信号的次数等。

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>

pid_t wait3(int *statloc, int options, struct rusage *rusage);
pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage);
										// 两个函数返回值:若成功,返回进程ID;若出错,返回-1

8.9 竞争条件

当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,就发生了竞争条件。

8.10 函数exec

fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序,有7种exec函数可供使用:

#include <unistd.h>

int execl(const char *pathname, const char *arg0, ... /* (char *)0 */);
int execv(const char *pathname, char *const argv[]);
int execle(const char *pathname, const char *arg0, ... /* (char *)0, char *const envp[] */);
int execve(const char *pathname, char *const argv[], char *const envp[]);
int execlp(const char *filename, const char *arg0, ... /* (char *)0 */);
int execvp(const char *filename, char *const argv[]);
int fexecve(int fd, char *const argv[], char *const envp[]);
										// 7个函数返回值:若出错,返回-1;若成功,不返回

这些函数之间的第一个区别是前4个函数取路径名作为参数,后两个函数则取文件名作为参数,最后一个取文件描述符作为参数。当指定filename作为参数时:

  • 如果filename种包含/,则就将其视为路径名;
  • 否则就按PATH环境变量,在它所指定的各目录中搜寻可执行文件。

第二个区别与参数表的传递有关(l表示列表listv表示矢量vector),函数execlexeclpexecle要求将新程序的每个命令行参数都说明为一个单独的参数,这种参数表以空指针结尾;对于另外4个函数(execvexecvpexecvefexecve),则应先构造一个指向各参数的指针数组,然后将该数组地址作为这4个函数的参数;

最后一个区别与向新程序传递环境表相关,以e结尾的3个函数(execleexecvefexecve)可以传递一个指向环境字符串指针数组的指针,其他4个函数则使用调用进程中的environ变量为新程序复制现有的环境。

7个exec函数的参数记忆方法:

  • 字母p表示该函数取filename作为参数,并且用PATH环境变量寻找可执行文件;
  • 字母l表示该函数取一个参数表,它与字母v互斥;
  • 字母v表示该函数取一个argv[]矢量;
  • 字母e表示该函数取envp[]数组,而不使用当前环境。

exec新程序对打开文件的处理与每个描述符的执行时关闭(close-on-exec)标志值有关,进程中每个打开描述符都有一个执行时关闭标志(FD_CLOEXEC)标志,若设置了此标志,则在执行exec时关闭该描述符。

在很多UNIX实现中,这7个函数中只有execve是内核的系统调用,另外6个只是库函数,它们最终都要调用execve,这7个函数之间的关系如下:
在这里插入图片描述

8.11 更改用户ID和更改组ID

setuid函数设置实际用户ID和有效用户ID,setgid函数设置实际组ID和有效组ID:

#include <unistd.h>

int setuid(uid_t uid);
int setgid(gid_t gid);
										// 两个函数返回值:若成功,返回0;若出错,返回-1

更改用户ID的规则(关于用户ID所说明的一切适用于组ID):

  • 若进程具有超级用户特权,则setuid函数将实际用户ID、有效用户ID以及保存的设置用户ID设置为uid
  • 若进程没有超级用户特权,但是uid等于实际用户ID或保存的设置用户ID,则setuid只将有效用户ID设置为uid,不更改实际用户ID和保存的设置用户ID;
  • 如果上述两个条件都不满足,则errno设置为EPERM,并返回-1。

关于内核所维护的3个用户ID的注意事项:

  • 只有超级用户进程可以更改实际用户ID;
  • 仅当程序文件设置了设置用户ID位时,exec函数才设置有效用户ID;
  • 保存的设置用户ID是由exec复制有效用户ID而得到的。

下图总结了更改这3个用户ID的不同方法:
在这里插入图片描述
setreuid函数功能是交换实际用户ID和有效用户ID的值:

#include <unistd.h>

int setreuid(uid_t ruid, uid_t euid);
int setregid(gid_t rgid, gid_t egid);
										// 两个函数返回值:若成功,返回0;若出错,返回-1
  • 如若两个参数中任一个的值为-1,则表示相应的ID保持不变。

seteuidsetegid函数用于更改有效用户ID和有效组ID:

#include <unistd.h>

int seteuid(uid_t uid);
int setegid(gid_t gid);
										// 两个函数返回值:若成功,返回0;若出错,返回-1
  • 一个非特权用户可将其有效用户ID设置为实际用户ID或保存的设置用户ID;
  • 一个特权用户可将有效用户ID设置为uid

更改3个不同用户ID函数之间的关系:
在这里插入图片描述

8.12 解释器文件

解释器文件是一个文本文件,其起始行的形式是:

#! pathname [optional-argument]

在感叹号和pathname之间的空格和optional-argument都是可选的,最常见的解释器文件以下列行开始:

! /bin/sh
  • pathname通常是绝对路径名,对它不进行什么特殊的处理(不使用PATH进行路径搜索);
  • 内核使调用exec函数的进程实际执行的并不是该解释器文件,而是在该解释器文件第一行中pathname所指定的文件。

8.13 函数system

system函数用于在程序中执行一个命令字符串:

#include <stdlib.h>

int system(const char *cmdstring);
  • 如果cmdstring是一个空指针,则仅当命令处理程序可用时,system返回非0值,这一特征可以确定在一个给定的操作系统上是否支持system函数,在UNIX种,system总是可用的;
  • system在其实现中调用了forkexecwaitpid,因此有3种返回值:
    (1)fork失败或者waitpid返回除EINTR之外的出错,则system返回-1,并且设置errno以指示错误类型;
    (2)如果exec失败(表示不能执行shell),则其返回值如同shell执行了exit(127)
    (3)否则所有3个函数(forkexecwaitpid)都成功,那么system的返回值是shell的终止状态。

8.14 进程会计

大多数UNIX系统提供了一个选项以进行进程会计处理,启用该选项后,每当进程结束时内核就写一个会计记录,典型的会计记录包含总量较小的二进制数据,一般包括命令名、所使用的CPU时间总量、用户ID和组ID、启动时间等。

8.15 用户标识

系统通常记录用户登陆时使用的名字,用getlogin函数可以获取此登录名:

#include <unistd.h>

char *getlogin(void);
										// 返回值:若成功,返回指向登录名字符串的指针;若出错,返回NULL						

8.16 进程调度

  • 进程可以通过调整友好值选择以更低优先级运行,只有特权进程允许提高调度权限;
  • Single UNIX Specification 中友好值的范围:0~(2*NZERO-1),NZERO是系统默认的友好值,默认20;
  • 友好值越小,优先级越高。

进行可以通过nice函数获取或更改它的友好值:

#include <unistd.h>

int nice(int incr);
										// 返回值:若成功,返回新的友好值;若出错,返回-1
  • incr参数被增加到调用进程的友好值上;
  • 如果incr太大,系统直接把它降到最大合法值,不给出提示;如果太小,也会把它调整到最小合法值;
  • 由于-1是合法的成功返回值,在调用nice函数之前需要清除errno,在nice函数返回-1时,需要检查它的值;
  • 进程只能影响自己的友好值,不能影响任何其他进程的友好值。

getpriority函数可以用于获取进程、一组相关进程的友好值:

#include <sys/resource.h>

int getpriority(int which, id_t who);
										// 返回值:若成功,返回-NZERO~NZERO-1之间的友好值;若出错,返回-1
  • which参数可取值:PRIO_PROCESS表示进程,PRIO_PGRP表示进程组,PRIO_USER表示用户ID;
  • who参数含义由which参数决定:
    (1)如果who参数为0,表示调用进程、进程组或者用户(取决于which参数的值);
    (2)当which设为PRIO_USER并且who0时,使用调用进程的实际用户ID;
    (3)如果which参数作用于多个进程,则返回所有作用进程中优先级最高的(最小的友好值)。

setpriority函数用于为进程、进程组和属于特定用户ID的所有进程设置优先级:

#include <sys/resource.h>

int setpriority(int which, id_t who, int value);
										// 返回值:若成功,返回0;若出错,返回-1
  • 参数whichwhogetpriority函数中相同;
  • value增加到NZERO上,然后变为新的友好值。

8.17 进程时间

任一进程都可调用times函数获得它自己以及已终止子进程的墙上时钟时间、用户CPU时间和系统CPU时间:

#include <sys/times.h>

clock_t times(struct tms *buf);
									// 返回值:若成功,返回流逝的墙上时钟时间(以时钟滴答数为单位);若出错,返回-1
  • 此函数填写由buf指向的tm结构:

    struct tms {
    	clock_t		tms_utime;	// 用户CPU时间
    	clock_t 	tms_stime;		// 系统CPU时间
    	clock_t		tms_cutime;	// 终止子进程的用户CPU时间
    	clock_t		tms_cstime;	// 终止子进程的系统CPU时间
    };
    
  • 函数返回的墙上时钟是相对于过去的某一时刻度量的,不能使用其绝对值而必须使用其相对值。

8.18 实例代码

chapter8

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

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

相关文章

【群智能算法】一种改进的蜣螂优化算法IDBO[2]【Matlab代码#18】

文章目录 1. 原始DBO算法2. 改进后的IDBO算法2.1 Bernoulli混沌映射种群初始化2.2 自适应因子2.3 Levy飞行策略2.4 动态权重系数 3. 部分代码展示4. 效果图展示5. 资源获取 1. 原始DBO算法 详细介绍此处略&#xff0c;可参考DBO算法介绍 2. 改进后的IDBO算法 2.1 Bernoulli混…

【Linux问题合集002】解决虚拟机里面的Linux系统部分无法上网情况,保姆级教程

&#x1f340;一、前言 正如标题所说&#xff0c;解决虚拟机里面的Linux系统部分无法上网情况&#xff0c;这个网络问题的原因有很多种可能&#xff0c;这篇博客不一定能够解决所有朋友的网络问题&#xff0c;但是如果遇到和我一样情况的&#xff0c;我保证解决步骤一定是非常详…

使用 Python 创建端到端聊天机器人

使用 Python 创建端到端聊天机器人 1. 效果图2. 原理2.1 什么是端到端聊天机器人&#xff1f;2.2 创建端到端聊天机器人步骤 3. 源码3.1 streamlit安装3.2 源码 参考 聊天机器人是一种计算机程序&#xff0c;它了解您的查询意图以使用解决方案进行回答。聊天机器人是业内最受欢…

《LKD3粗读笔记》(11)定时器和时间管理

文章目录 1、内核中的时间概念2、 节拍率&#xff1a;HZ3、jiffies4、硬时钟和定时器5、时钟中断处理程序6、实际时间7、定时器8、延迟执行 1、内核中的时间概念 硬件为内核提供了一个系统定时器用以计算流逝的时间&#xff0c;该时钟在内核中可看成是一个电子时间资源&#x…

Nginx安装删除JDK Tomcat Redis

1.卸载Nginx ps -ef|grep nginx 查询Nginx 进程pid 如上图 master是主进程, worker是工作进程, master负责维护worker进程 Nginx启动后默认启动master进程和worker进程 Nginx默认使用端口80 kill -9 7035 或者 kill -term 7035 kill -9 7036 查找根下所有名字包…

带你搞懂人工智能、机器学习和深度学习!

不少高校的小伙伴找我聊入门人工智能该怎么起步&#xff0c;如何快速入门&#xff0c;多长时间能成长为中高级工程师&#xff08;聊下来感觉大多数学生党就是焦虑&#xff0c;毕业即失业&#xff0c;尤其现在就业环境这么差&#xff09;&#xff0c;但聊到最后&#xff0c;很多…

07 Kubernetes 网络与服务管理

课件 Kubernetes Service是一个抽象层&#xff0c;用于定义一组Pod的访问方式和访问策略&#xff0c;其作用是将一组Pod封装成一个服务&#xff0c;提供一个稳定的虚拟IP地址和端口号&#xff0c;以便于其他应用程序或服务进行访问。 以下是Kubernetes Service YAML配置文件的…

FPGA时序约束(五)衍生时钟约束与I/O接口约束

系列文章目录 FPGA时序约束&#xff08;一&#xff09;基本概念入门及简单语法 FPGA时序约束&#xff08;二&#xff09;利用Quartus18对Altera进行时序约束 FPGA时序约束&#xff08;三&#xff09;时序约束基本路径的深入分析 FPGA时序约束&#xff08;四&#xff09;主时…

2023五一建模A题完整版本【原创首发】

已经完成五一数学建模全部内容&#xff0c;大家可以文末查看&#xff01;&#xff01;供参考使用&#xff01; 摘要 本文研究了喷气式无人机在执行空中物资投放和爆破任务过程中的数学建模问题。我们分析了无人机投放距离与飞行高度、飞行速度、空气阻力等因素之间的关系&…

【Mybatis源码分析】动态标签的底层原理,DynamicSqlSource源码分析

DynamicSqlSource 源码分析 一、DynamicSqlSource 源码分析&#x1f62f;DynamicContext源码分析&#x1f644;SqlNode源码分析&#xff08;动态SQL标签&#xff09;Mybatis 动态SQL标签举例、调试SqlNode源码分析MixedSqlNodeIfSqlNodeWhereSqlNode、SetSqlNode、TrimSqlNodeS…

区域医疗云his系统源码,具有可扩展、易共享、易协同的优势

云HIS系统采用SaaS软件应用服务模式&#xff0c;提供软件应用服务多租户机制&#xff0c;实现一中心部署多机构使用。相对传统HIS单机构应用模式&#xff0c;它可灵活应对区域医疗、医疗集团、医联体、连锁诊所、单体医院等应用场景&#xff0c;并提升区域内应用的标准化与规范…

安装配置goaccess实现可视化并实时监控nginx的访问日志

一、业务需求 我们安装了nginx后,需要对nginx的访问情况进行监控(希望能够实时查看到访问nginx的情况),如下图所示: 二、goaccess的安装配置步骤 2.1、准备内容 需要先安装配置nginx或OpenResty - 安装 Linux环境对Nginx开源版源码下载、编译、安装、开机自启https://b…

从0开始利用Jenkins构建Maven项目(微服务)并自动发布

0. 前言 本文旨在帮助读者梳理如何从0开始利用Jenkins构建Maven项目&#xff08;微服务&#xff09;的自动发布任务 本文目录如下&#xff1a; 如何完成自动部署 0. 前言1. 配置工具类地址1.1 JDK1.2 Git1.3 Maven 2. 安装Jenkins3. 安装额外的工具插件4. 配置必要参数4.1 配…

TiDB实战篇-索引设计

简介 实战索引设计 数据映射原理 索引 唯一索引 二级索引 索引实例 索引设计 索引创建&#xff08;建表的时候创建&#xff09; 建表完以后添加 联合索引&#xff08;最左原则&#xff0c;索引覆盖&#xff09; 使用例子 索引覆盖 表达式索引 表达式索引的使用 不可见…

【youcans 的 OpenCV 学习课】23. 人脸检测:Haar 级联检测器

专栏地址&#xff1a;『youcans 的图像处理学习课』 文章目录&#xff1a;『youcans 的图像处理学习课 - 总目录』 【youcans 的 OpenCV 学习课】23. 人脸检测&#xff1a;Haar 级联检测器 4. Haar 级联分类器5. Haar 人脸/人眼检测器5.1 OpenCV 中的级联分类器5.2 Haar 级联检…

OPNET Modeler 调试简介

在使用 OPNET Modeler 软件运行仿真时&#xff0c;经常会遇到错误&#xff0c;发现和定位错误所在的地方是解决错误的第一步&#xff0c;那么怎么定位错误呢&#xff0c;这个时候就需要采用仿真调试器 (OPNET Simulation Debugger&#xff0c;ODB)进行调试了。 在 OPNET 中&…

【模电实验】电路元件伏安特性的测绘及电源外特性的测量

实验2电路元件伏安特性的测绘及电源外特性的测量 实验目的 学习测量线性和非线性电阻元件伏安特性的方法&#xff0c;并绘制其特性曲线学习测量电源外特性的方法掌握运用伏安法判定电阻元件类型的方法学习使用直流电压表、电流表&#xff0c;掌握电压、电流的测量方法 实验原…

Java BIO(Blocking IO:同步并阻塞式IO)

1.基本介绍 1>.Java BIO就是传统的java io编程,其相关的类和接口在"java.io"包下; 2>.BIO(Blocking I/O): 同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理; 如果这个连接不做任何事情会造成(服务器)不必…

【C++】STL——list深度剖析 及 模拟实现

文章目录 前言1. list的介绍及使用1.1 list的介绍1.2 list的使用遍历插入删除数据Operations迭代器的功能分类list 的sort性能测试 2. list的模拟实现2.1 STL_list源码浏览2.2 基本结构实现2.3 思考&#xff1a;list迭代器是否可以用原生指针2.4 list迭代器的实现&#xff08;重…

RocketMQ5.1 NameServer 路由管理

文章目录 1. 路由管理核心组件介绍2. RouteInfoManager 路由表3. 路由管理3.1 注册 Broker3.2 注销 Broker3.3 拼凑 TopicRouteData 此文章基于 RocketMQ 5.1 版本进行分析&#xff0c;与 4.x 版本相比此文章分析的部分源码有很大的区别 1. 路由管理核心组件介绍 路由管理是指…