计算机系统大作业:软件人生-Hello’s P2P

news/2025/11/24 14:57:51/文章来源:https://www.cnblogs.com/yangykaifa/p/19264217

计算机系统

大作业

题 目 程序人生-Hello’s P2P
专 业 未来技术学院
学 号 2023110756
班 级 23WLR13(23WL015)
学 生 隋博文
指 导 教 师 吴锐

计算机科学与技术学院
2025年5月
摘 要
本论文以“脚本人生-Hello’s P2P”为主题,利用分析C程序hello.c从源代码到进程执行的全生命周期,系统揭示了计算机系统的核心机制与设计思想。基于Linux程序链(GCC、GDB、readelf等),论文详细探讨了代码构建流程:预处理展开头文件与宏定义生成.i文档,编译生成汇编代码(.s),汇编转换为可重定位目标资料(.o),链接解析符号生成可执行文件(hello),最终借助Shell的fork-exec机制加载为进程。通过分析操作系统的进程管理(虚拟地址空间、页式内存管理、动态链接PLT/GOT、信号处理与缺页中断)与硬件协同机制(TLB、Cache、MMU地址转换),阐明了软硬件如何协同提升执行效率并保障资源隔离。实践上,生成了中间文件(.i、.s、.o、hello)并经过反汇编与调试验证各阶段正确性;理论上,系统梳理了代码从静态代码到动态进程的完整生命周期,为理解计算机系统抽象层次、研发高效健壮软件及优化系统性能献出了底层视角参考。
关键词:P2P;编译与链接;进程管理;虚拟内存;动态链接;

目 录

第1章 概述 - 4 -
1.1 Hello简介 - 4 -
1.2 环境与软件 - 5 -
1.3 中间结果 - 6 -
1.4 本章小结 - 6 -
第2章 预处理 - 7 -
2.1 预处理的概念与作用 - 7 -
2.2在Ubuntu下预处理的命令 - 7 -
2.3 Hello的预处理结果解析 - 7 -
2.4 本章小结 - 8 -
第3章 编译 - 9 -
3.1 编译的概念与作用 - 9 -
3.2 在Ubuntu下编译的命令 - 9 -
3.3 Hello的编译结果解析 - 9 -
3.4 本章小结 - 14 -
第4章 汇编 - 15 -
4.1 汇编的概念与作用 - 15 -
4.2 在Ubuntu下汇编的命令 - 15 -
4.3 可重定位目标elf格式 - 15 -
4.4 Hello.o的结果解析 - 17 -
4.5 本章小结 - 19 -
第5章 链接 - 20 -
5.1 链接的概念与作用 - 20 -
5.2 在Ubuntu下链接的命令 - 20 -
5.3 可执行目标文件hello的格式 - 20 -
5.4 hello的虚拟地址空间 - 23 -
5.5 链接的重定位过程分析 - 25 -
5.6 hello的执行流程 - 26 -
5.7 Hello的动态链接分析 - 27 -
5.8 本章小结 - 28 -
第6章 hello进程管理 - 29 -
6.1 进程的概念与作用 - 29 -
6.2 简述壳Shell-bash的作用与处理流程 - 29 -
6.3 Hello的fork进程创建过程 - 29 -
6.4 Hello的execve过程 - 30 -
6.5 Hello的进程执行 - 30 -
6.6 hello的异常与信号处理 - 31 -
6.7本章小结 - 34 -
第7章 hello的存储管理 - 35 -
7.1 hello的存储器地址空间 - 35 -
7.2 Intel逻辑地址到线性地址的变换-段式管理 - 35 -
7.3 Hello的线性地址到物理地址的变换-页式管理 - 36 -
7.4 TLB与四级页表支持下的VA到PA的变换 - 37 -
7.5 三级Cache拥护下的物理内存访问 - 38 -
7.6 hello进程fork时的内存映射 - 38 -
7.7 hello进程execve时的内存映射 - 38 -
7.8 缺页故障与缺页中断处理 - 39 -
7.9本章小结 - 39 -
结论 - 40 -
附件 - 42 -
参考文献 - 43 -

第1章 概述
1.1 Hello简介
1.1.1.P2P(Program to Process)
Hello的P2P过程描述了程序从源代码到运行进程的全过程,涉及预处理、编译、汇编、链接、加载、执行等关键步骤:
1.预处理(Preprocessing):
编译器(如gcc)处理hello.c中的宏定义(如#include)、条件编译等,生成hello.i(纯C代码)。
关键操控:头文件展开、宏替换、删除注释。

2.编译(Compilation):
将hello.i翻译为汇编代码hello.s,涉及语法分析、语义分析、优化等。
关键操作:C代码转为机器相关的汇编指令(如x86-64架构的mov、call等)。

3.汇编(Assembly):
汇编器将hello.s转换为机器码(二进制目标文件hello.o),但尚未解决外部函数(如printf)的地址。
关键操控:生成可重定位目标文件(ELF格式),包括代码段(.text)、数据段(.data)等。

4.链接(Linking)
链接器(ld)合并hello.o与库资料(如libc.so),解析外部符号(如printf的地址),生成可执行文件hello。
关键处理:符号解析、重定位、地址空间分配。

5.加载与执行(Loading & Execution)
Shell通过fork()创建子进程,调用execve()加载hello到内存,形成进程映像。
关键操作:虚拟内存映射、动态链接库加载、进程上下文初始化。

1.1.2. O2O(Zero to Zero)
Hello的O2O过程描述了程序从无(磁盘材料)到有(进程运行),最终又回归无(终止)的完整生命周期:
1.Zero(磁盘存储)
hello.c作为文本文件存储在磁盘上,此时程序处于“静态”状态,仅占用存储空间。

2.Process(内存运行)
用户输入命令./hello 学号 姓名 手机号 秒数,Shell通过fork()和execve()将其加载为进程。
关键行为:
循环打印信息(printf),调用sleep()休眠。
响应信号(如Ctrl-C触发SIGINT终止进程,Ctrl-Z触发SIGTSTP挂起进程)。

3.Zero(进程终止)
进程依据return 0正常退出,或由信号强制终止。
关键操作:
释放内存、关闭记录描述符、内核清除进程描述符(PCB)。
总结:
P2P:聚焦程序如何从代码转换为进程,涉及计算机系统的编译、链接、加载机制。
O2O:强调进程从创建到终止的完整生命周期,体现操作系统的进程管理、存储管理、信号处理功能。
通过Hello的案例,可以深入理解计算机系统中软硬件协同的工作原理,包括编译器、链接器、操作系统内核等组件的交互。

1.2 环境与工具
在本次大作业中,使用的软硬件环境及开发调试工具如下:

硬件环境:
处理器:Intel Core i7-10750H(x86-64架构)
内存:16GB DDR4
存储:512GB NVMe SSD
软件环境:
操作系统:Ubuntu 22.04 LTS(内核版本 5.15.0-76-generic)
编译器与工具链:GCC版本:gcc 11.3.0(编译选项:-m64 -Og -no-pie -fno-stack-protector -fno-PIC)
汇编器:GNU as 2.38
链接器:GNU ld 2.38

研发与调试工具:
静态分析工具:
readelf:用于解析ELF资料结构(命令:readelf -S/-s/-r)
objdump:反汇编目标文件(命令:objdump -d -r)
动态调试程序:
GDB:调试执行流程(版本 12.1)
EDB:图形化查看进程虚拟地址空间(版本 0.9.21)
辅助工具:
strace:跟踪系统调用(用于分析execve和fork行为)
LD_DEBUG:观察动态链接过程(命令:LD_DEBUG=bindings ./hello)
1.3 中间结果
在Hello程序的编写与论文撰写过程中,生成以下中间结果记录:

hello.i
作用:预处理后的C代码文件,包含所有头文件展开和宏替换结果。
生成命令:gcc -E hello.c -o hello.i

hello.s
作用:编译生成的x86-64汇编代码文件,保留符号和标签信息。
生成命令:gcc -S hello.i -o hello.s

hello.o
作用:可重定位目标材料(ELF格式),包含未解析符号的机器码。
生成命令:gcc -c hello.s -o hello.o

hello
作用:可执行目标文件,利用链接器合并hello.o与C标准库生成。
生成命令:gcc hello.o -o hello

反汇编文件
hello_o_disasm.txt:hello.o的反汇编结果(命令:objdump -d -r hello.o)
hello_disasm.txt:hello的反汇编结果(命令:objdump -d hello)
1.4 本章小结
本章概述了Hello程序的开发环境与工具链,明确了硬件配置、操作系统版本及核心软件(如GCC、GDB、readelf)的具体使用方式。同时,列出了从预处理到链接过程中生成的关键中间文件(如hello.i、hello.s、hello.o),并说明了每个记录的作用及生成命令。这些中间档案为后续章节的深入分析(如预处理结果解析、汇编代码优化、ELF格式研究)给予了基础资料。借助本章的准备工作,确保后续实验步骤的可复现性与结果的可验证性

第2章 预处理
2.1 预处理的概念与作用
2.1.1概念
预处理(Preprocessing)是C应用编译过程的第一阶段,由预处理器(Preprocessor)对源代码(.c档案)进行文本级处理,生成一个纯C代码记录(.i文件)。预处理器不检查语法或逻辑错误,仅执行文本替换和文件合并。

2.1.2作用
1.头文件包含(#include):将指定的头文件(如stdio.h、unistd.h)内容直接插入到源文件中。避免重复声明库函数(如printf、sleep),确保程序能正确调用外部代码。

2.宏替换(#define):将代码中的宏(如#define PI 3.14)替换为定义的值或表达式。提高代码可读性,避免魔法数字(Magic Numbers),简化代码修改。

3.条件编译(#ifdef、#if、#endif等):根据条件决定是否编译某段代码(如调试代码、平台适配代码)。实现跨平台兼容性,灵活控制代码版本。

还有其他的一些作用,如:
删除注释:移除所有注释(//和/* */),减少后续编译阶段的处理负担。
行号标记(#line):为调试器提供原始代码的行号信息(可选功能)。
2.2在Ubuntu下预处理的命令
在终端输入gcc -E hello.c -o hello.i,按下回车键,这样就在原目录生成了hello.i资料。
在这里插入图片描述
图 1 预处理指令
2.3 Hello的预处理结果解析
被修改过了。就是我们在liunx中点击打开hello.i文件,对比了源程序和预处理后的代码。结果显示,观察发现,其中的注释已经消失,前一部分的代码为被加载到程序中的头文件;程序的最后一部分与hello.c中的main函数完全相同。除了预处理指令被扩展成了三千多行之外,源程序的其他部分都保持不变,说明.c文件的确
在这里插入图片描述

图 2 hello.i文件

能够看出,有如下的变化:

1.头文件展开:
#include <stdio.h>被替换为stdio.h的全部内容(数百行代码)。
例如,printf的函数声明会被插入到资料开头。

2.宏和条件编译处理:
若源代码中有#define DEBUG 1,则所有DEBUG会被替换为1。
未满足条件的#ifdef代码块会被直接删除。

3.注释消失:
源代码中的注释在.i文件中完全不存在
2.4 本章小结
预处理是C软件编译的第一步,它通过文本替换和资料合并,将分散的代码整合为一个完整的、无注释的中间文件(.i),为后续的编译阶段做好准备。理解预处理有助于调试宏错误、优化头文件引用,并编写可移植性更强的代码。

第3章 编译
3.1 编译的概念与作用
3.1.1概念
编译(Compilation) 是将预处理后的中间文件(.i)转换为汇编语言文件(.s)的过程。编译器(如gcc)在此阶段执行以下核心任务:
1.语法与语义分析:检查代码是否符合C语言规范(如类型匹配、函数声明)。
2.代码优化:对代码进行逻辑优化(如删除冗余计算、简化表达式)。
3.生成汇编代码:将C语言翻译为与目标机器架构(如x86-64)相关的汇编指令。

3.1.2作用
1.将高级语言转换为低级机器相关指令,为后续汇编阶段提供输入。
2.暴露潜在代码逻辑错误(如未定义变量、类型不匹配)。

注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序

3.2 在Ubuntu下编译的命令
在终端输入gcc -S hello.i -o hello.s,按下回车键,这样就在原目录生成了hello.s文件。
在这里插入图片描述
图 3 编译指令
3.3 Hello的编译结果解析
3.3.1对文件信息的记录
在这里插入图片描述

图 4 汇编代码
起初是记录文件相关信息的汇编代码,为之后链接过程利用。其中.file表明了源文件,.text代码段,.section .radata只读代码段,.align对齐方式为8字节对齐,.string字符串,.global全局变量,.type声明main是函数类型。

3.3.2对局部变量的操作
在这里插入图片描述

图 5 L2
局部变量存储在栈中,当进入函数main的时候,会根据局部变量的需求,在栈上申请一段空间供局部变量使用。当局部变量的生命周期结束后,会在栈上释放。可以看到在L2处局部变量i被存储在-4(%rbp)处,并赋值为0,即i的初始化。

3.3.3对字符串常量的处理
在这里插入图片描述

图 6 对字符串常量的操作
在main函数前,在.rodata处的.LC0和.LC1已经存储了字符串常量,标记该位置是代码是只读的。在main函数中使用字符串时,得到字符串的首地址。

3.3.4对立即数的操作
立即数直接用“$+数字”来表示,根据不同指令让立即数参与不同的运算。
在这里插入图片描述
图 7 对立即数的操作

3.3.5 main函数参数的传递
在这里插入图片描述

图 8 main函数参数的传递
在main函数的开始部分,因为后面还会使用到%rbp数组,所以先将%rbp压栈保存起来。21行将栈指针减少32位,然后分别将%rdi和%rsi的值存入栈中。
所以可知,%rbp-20和%rbp-32的位置分别存了argv数组和argc的值。

3.3.6对数组的操作
对数组的操控,都是先找到数组的首地址,然后加上偏移量即可。例如在main中,调用了argv[1]和argv[2],在汇编代码中,每次将%rbp-32的的值即数组首地址传%rax,然后将%rax分别加上偏移量24和16,得到了argv[1]和argv[2],再分别存入对应的寄存器%rsi和%rdx作为第二个参数和第三个参数,之后调用printf函数时应用。调用完printf后同样,在偏移量为32时,取得argv[3]并存入%rdi作为第一个参数在调用函数atoi运用。
在这里插入图片描述

图 9 对数组的运行

3.3.7对函数的调用与返回
函数的前六个参数有寄存器传参,返回值存在%rax寄存器中。在函数调用时,先将相应的值存入相应的寄存器,然后应用call指令调用函数和ret指令返回函数。注意,由于函数是公用一套寄存器的,在调用一个函数之前,要先将当前函数的一些值保存起来,调用完再恢复。
对printf函数的调用,在3.3.6中已经介绍过,取得argv数组的第二个和第三个元素放入寄存器%rsi和%rdx,然后45行取得了字符串的地址,并存入了2%rdi中作为第一个参数,这样三个参数都准备好后,用call指令调用了printf函数。
在这里插入图片描述

图 10 对printf函数的调用
对atoi函数和sleep函数的调用,先取得argv存入%rdi作为第一个参数,然后第50行call指令调用了atoi函数,接着atoi的返回值存入了%rax中,再将其存入%rdi中作为sleep的第一个参数,随后用call调用sleep函数。
在这里插入图片描述

图 11 对atoi函数和sleep函数的调用

3.3.8 for循环
对于for循环,将循环变量存入一个寄存器中,然后当执行完一个循环体之后,更新循环变量(一般是用add指令进行自增),随后用cmp指令将其与条件进行比较,满足则继续,否则退出循环。
在这里插入图片描述

图 12 for循环

3.3.9赋值操作
赋值操作很简单,用movq指令即可,例如将a寄存器的值赋值给b寄存器,用movq a b(以8字节为例)。在hello.s中很多地方都用到了赋值语句,比如说对局部变量i的赋值。
在这里插入图片描述

图 13 赋值操作
3.4 本章小结
编译阶段将预处理后的C代码转换为汇编代码,涉及语法检查、优化和指令生成。汇编代码结构清晰反映了C语言逻辑(如循环、函数调用、参数传递)。寄存器与栈管理是汇编完成的核心(如%rbp管理栈帧,%rdi传递参数)。函数调用约定(如参数通过寄存器传递)遵循x86-64体系规范。通过分析hello.s,能够深入理解编译器如何将高级语言映射为机器指令,为后续汇编和链接阶段奠定基础。

第4章 汇编
4.1 汇编的概念与作用
4.1.1概念
汇编(Assembly) 是将汇编代码文件(.s)转换为可重定位目标文件(.o)的过程,由汇编器(如as)完成。其核心任务包括:
1.指令翻译:将汇编指令(如mov、call)转换为机器码(二进制形式)。
符号解析:标记代码中的符号(如函数名、全局变量)并记录其位置。
2.生成目标文件:输出符合ELF(Executable and Linkable Format)标准的可重定位文件,供链接器使用。

4.1.2作用
1.生成与硬件直接交互的机器指令。
2.为链接阶段提供未完全解析的目标文件(地址未最终确定)。

注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。
4.2 在Ubuntu下汇编的命令
在hello.s的目录下打开终端,输入gcc -c hello.s -o hello.o并按下回车键,这样便可以生成hello.o目标文件。
在这里插入图片描述

图 14 汇编指令
4.3 可重定位目标elf格式
4.3.1 ELF头
在这里插入图片描述

图 15 ELF头

输入读取ELF头指令:readelf -h hello.o,按下回车键,得到elf头,elf头以16字节的序列开始,描述了生成该文件的平台的字的大小和字节顺序,ELF头剩下的部分涵盖帮助链接器语法分析和解释目标文件的信息,包括ELF头的大小、目标文件的类型(如可执行、可重定位或者共享的)、机器类型、section头部表的文件偏移以及节头部表中条目的大小和数量。

4.3.2 Section头

在这里插入图片描述

图 16 Section头
输入命令readelf -S hello.o,按下回车键。得到section头部分,记录了各section的名称、类型、地址、偏移量、大小、全体大小、旗标、链接、信息、对齐等信息。利用section头表中的字节偏移信息许可得到各section在文件中的起始位置,以及各section所占空间的大小,这样方便重定位。
4.3.3 重定位节
通过输入命令readelf -r hello.o,按下回车键。重定位节中涵盖了.text 节中需要进行重定位的信息,大家能够发现需要重定位的函数有: .rodata, puts, exits, printf, atoi, sleep, getchar。

在这里插入图片描述

图 17 .text节

4.3.4符号表
输入命令readelf -s hello.o,按下回车键,得到符号表。符号表存放了程序中定义和引用的函数和全局变量的信息。
在这里插入图片描述

图 18 符号表

4.4 Hello.o的结果解析
输入反汇编指令objdump -d -r hello.o,按下回车键,得到反汇编代码。反汇编代码中,除了.s文件中已经出现过的代码,还包含了它们对应的机器语言的代码,比如说分支转移结构中,hello.s表示为:
在这里插入图片描述

图 19 hello.o的反汇编
而在hello.o中表示为:

在这里插入图片描述

图 20 hello.o原码
这是因为.s资料中可能用段名称L3来进行助记,而在.o档案中则需要它的真实地址以便于下一步管理。而在函数调用方面,.s档案在call后可直接跟上函数名称,如 call printf@PLT,但是.o记录call后跟的是一条重定位条目指引的信息,如 call 73 <main+0x73>。
完整反汇编代码如下:
在这里插入图片描述

图 21 hello.o的完整反汇编代码
4.5 本章小结
1.汇编阶段将汇编代码转换为机器码,生成可重定位目标文件(.o)。
2.ELF格式定义了目标文件的结构,包含代码、数据、符号表和重定位信息。
3.重定位表是链接阶段的关键输入,用于解决外部符号的地址困难。
4.反汇编分析揭示了机器指令与汇编代码的映射关系,以及链接前地址的占位特性。
通过本章分析,可深入理解程序从汇编代码到机器指令的转换过程,以及目标文件在链接前的未完成状态。

第5章 链接
5.1 链接的概念与作用
5.1.1概念
链接(Linking) 是将多个可重定位目标文件(如hello.o)和库文件(如libc.so)合并为一个可执行目标文件(如hello)的过程。其核心任务包括:
1.符号解析:确定所有符号(函数、变量)的引用关系,确保每个符号均有唯一定义。
2.地址重定位:将目标文件中的逻辑地址映射到进程的虚拟地址空间。
3.合并代码与数据:整合不同目标文件的代码段(.text)、数据段(.data、.rodata)等。
5.1.2作用
1.处理跨文件的函数调用(如printf、sleep)。
2.构建完整的可执行程序,使其能够加载到内存运行
注意:这儿的链接是指从 hello.o 到hello生成过程。
5.2 在Ubuntu下链接的命令
输入命令ld -o hello
/usr/lib/x86_64-linux-gnu/crt1.o
/usr/lib/x86_64-linux-gnu/crti.o
hello.o
/usr/lib/x86_64-linux-gnu/crtn.o
-lc -dynamic-linker /lib64/ld-linux-x86-64.so.2
按下回车键。

在这里插入图片描述

图 22 链接指令
5.3 可执行目标文件hello的格式
5.3.1 ELF头
在终端中输入readelf -h hello并回车,查看hello文件的ELF头,如下图所示:
在这里插入图片描述

图 23 ELF头
EXEC,表示时可执行目标文件,这与hello.o不同。hello中的节的数量为30个。就是hello的ELF头中Type处显示的

5.3.2 Section头
在终端中输入readelf -S hello并回车,查看hello文件的Section头,如下图所示:
可能看出,Section表对hello中所有信息进行了声明,包括了大小、偏移量、起始地址以及数据对齐方式等信息。根据始地址和大小,我们就可以计算节头部表中的每个节所在的区域。
在这里插入图片描述

图 24 Section头

5.4 hello的虚拟地址空间
我们在终端中输入edb并回车,打开edb界面,在File-Open中选择hello文件,点击open,界面显示如下:
在这里插入图片描述

图 25 edb调试查看(1)

可以看到,hello的可执行部分(代码段)起始地址为0x400000。
在这里插入图片描述

图 26 edb调试查看(2)

由5.3节大家又可以得知,.interp段的起始地址为4002e0。使用edb查询可得到如下结果。
在这里插入图片描述

图 27 edb调试查看(3)

.init的起始地址为0x401000,在edb中查询地址可以得到如下图的结果:

在这里插入图片描述

图 28 edb调试查看(4)

.text的起始地址为0x4010f0,在edb中查询地址可以得到如下图的结果:

在这里插入图片描述

图 29 edb调试查看(5)

.rodata的起始地址为0x402000,在edb中查询地址许可得到如下图的结果:

在这里插入图片描述

图 30 edb调试查看(6)

.eh_frame的起始地址为0x4006a0,在edb中查询地址可以得到如下图的结果:
在这里插入图片描述

图 31 edb调试查看(7)

5.5 链接的重定位过程分析
在终端输入命令objdump -d -r hello并回车,查看hello可执行文件的反汇编条目,结果如下:
在这里插入图片描述

图 32 可执行文件的反汇编代码
hello的反汇编代码与hello.o的返汇编代码在结构和语法上是基本相同的,只不过hello的反汇编代码多了相当多的内容,我们经过比较不同来看一下区别:
1.虚拟地址不同,hello.o的反汇编代码虚拟地址从0开始,而hello的反汇编代码虚拟地址从0x400000开始。这是因为hello.o在链接之前只能给出相对地址,而hello在链接之后得到的是绝对地址。
2.反汇编节数不同,hello.o只有.text节,里面只有main函数的反汇编代码。而hello在main函数之前加上了链接过程中重定位而加入的各种在hello中被调用的函数、信息,增加了.init,.plt,.plt.sec等节的反汇编代码。
具体的地址,但相对地址没有发生变化。就是3.跳转指令不同,hello.o中的跳转指令后加的重要是汇编代码块前的标号,而hello中的跳转指令后加的则
5.6 hello的执行流程
1.使用edb执行hello,起初,最初的程序地址会在hello使用的动态链接库ld-2.2.27.so的入口点_dl_start:
2.然后,程序跳转到_dl_init,在经过了一系列初始化后,跳到hello的程 序入口点_start;
3.然后程序通过call指令跳到动态链接库ld-2.27.so的_libc_start_main 处,这个函数会进行一些必要的初始化,并负责调用main函数;
4. 下一步,软件调用动态链接库中的_cxa_atexit函数,它会设置在程序结束时需要调用的函数表;
5. 然后返回到_libc_start_main继续,然后调用hello可执行文件中的_libc_csu_init函数,这函数是由静态库引入的,也是做一些初始化的工作;
6. 然后脚本返回到_libc_start_main继续,紧接着工具调用动态链接库里的_setjmp函数,设置一些非本地跳转;
7.然后返回到_libc_start_main继续,正式开始调用main函数;
8. 由于我们在edb运行hello的时候并未给出额外的命令行参数,因此它会在第一个if处通过exit(1)直接结束程序;
9. 通过hello本身携带的exit函数,程序会跳转;
10. 之后,在进行了若干操作后,程序退出。
5.7 Hello的动态链接分析
通过程序调用一个有共享库定义的函数时,编译器无法预测函数在运行时的具体地址,因为定义这个函数的共享模块可能能够被加载到任何位置。因此,编译系统采用延迟绑定,将过程地址的绑定推迟到第一次调用该过程的时候。
延迟绑定需用到两个数据结构:GOT(Global Offset Table,全局偏移表)和PLT(Procedure Linkage Table,过程链接表)。
.plt:PLT是一个数组,其中每个条目是16字节代码。PLT[0]是一个特殊条目,它跳转到动态链接器中。每个被可执行程序调用的库函数都有它自己的PLT条目。每个条目都负责调用一个具体的函数。
.got:GOT是一个数组,其中每个条目是8字节地址。和PLT联合使用时,GOT[O]和GOT[1]包含动态链接器在解析函数地址时会使用的信息。GOT[2]是动态链接器在1d-linux.so模块中的入口点。其余的每个条目对应于一个被调用的函数,其地址应该在运行时被解析。每个条目都有一个相匹配的PLT条目。
一次调用某个函数时,程序不是直接调用,而是调用进入函数所在的PLT条目,第一条PLT指令通过GOT进行间接跳转,每个GOT条目初始时都指向其对应的PLT条目的第二条指令,这个间接跳转只是简单将控制传送回函数所在的PLT条目的下一条指令。之后将函数的ID压入栈中之后,函数所在的PLT条目跳转到PLT[0],最终PLT[0]通过GOT[1]间接地把动态链接器的一个参数压入栈中,然后通过GOT[2]简介跳转进入动态链接器。动态链接器通过使用两个栈条目来确定函数的运行时位置,再将控制传递给函数。
后续调用时,则可以不用通过GOT[4]的跳转将控制给到函数。
hello在动态连接器加载前后的重定位是不一样的,在加载之后才进行重定位。
在这里插入图片描述

图 33 .plt代码段
5.8 本章小结
1.链接阶段将目标文件和库合并为可执行文件,解决符号引用与地址分配疑问。
2.ELF格式定义了可执行文件的结构,包括代码段、数据段和动态链接信息。
3.虚拟地址空间通过页表映射实现进程隔离,代码段与数据段权限分离保障安全性。
4.动态链接延迟绑定外部函数,提升应用启动效率并支持库的共享。
5.重定位与PLT/GOT机制是动态链接的核心,实现函数地址的运行时解析。
通过本章分析,可深入理解程序如何从静态的二进制文件转变为内存中运行的进程,以及操作系统与链接器在其中的协同作用。

第6章 hello进程管理
6.1 进程的概念与作用、
6.1.1概念
进程(Process) 是操作系统中程序执行的实例,是资源分配和调度的基本单位。其核心特征包括:
独立性:每个进程拥有独立的地址空间、文件描述符、寄存器上下文等。
并发性:多个进程可通过时间片轮转或优先级调度并发执行。
动态性:进程具有生命周期(创建、运行、终止)。

6.1.2作用
1.实现多任务并行执行,提高系统资源利用率。
2.隔离程序运行环境,避免进程间相互干扰。
6.2 简述壳Shell-bash的作用与处理流程
6.2.1作用
Shell(如Bash) 是用户与操作系统内核之间的命令行接口,其核心作用包括:
1.命令解析:将用户输入的命令(如./hello 学号 姓名 手机号 秒数)拆分为可执行程序名和参数。
2.进程创建:通过fork()和execve()启动新进程。
3.环境管理:维护环境变量(如PATH),传递到子进程。
4.作业控制:支持前台/后台进程切换(如Ctrl-Z挂起,fg恢复)。

6.2.2处理流程
1.读取输入命令并解析。
否为内置命令(如cd)。就是2.检查命令
3.若为外部命令(如./hello),则:
a.Fork()创建子进程。
b.子进程调用execve()加载目标程序。
c.父进程等待子进程终止(前台模式),或继续接收输入(后台模式)
6.3 Hello的fork进程创建过程
Shell调用fork创建子进程。新创建的子进程几乎但不完全与父进程相同。子进程得到与父进程用户级虚拟地址空间相同的(但是独立的)一份副本,包括代码和数据段、堆、共享库以及用户栈。子进程还获得与父进程任何打开文件描述符相同的副本,因此fork后子进程允许读写父进程中打开的任意记录。父进程和创建的子进程最大的区别在于其PID不同。
fork会被父进程调用一次,返回两次,父进程与创建的子进程并发执行。执行hello时,fork后的进程在前台执行,因此创建它的父进程shell暂时挂起等待hello进程执行完毕。

6.4 Hello的execve过程

shell通过fork创建一个子进程后,execve函数在当前进程的上下文中加载并运行一个新程序即hello。
Execve需要三个参数:可执行目标文件名filename、参数列表argv、环境变量列表envp。这些都由shell构造并传递。除非找不到filename,否则execve不会返回。(调用一次,(正常情况下)从不返回)
调用execve会将这个进程执行的原本的应用完全替换,它会删除已存在的用户区域,包括信息和代码;然后,映射私有区:为Hello的代码、数据、.bss和栈区域创建新的区域结构,所有这些区域都是私有的、写时才复制的;之后映射共享区;最终把控制传递给当前的进程的代码入口。

6.5 Hello的进程执行
6.5.1逻辑控制流
逻辑控制流是一个PC值的序列,PC值就是程序计数器的值,这些值与可执行目标记录的指令或者包含在运行时动态链接到程序的共享对象中的指令一一对应。

6.5.2 时间分片
在现代计算机体系中,进程是轮流应用处理器的,每个进程都执行它的流的一部分,然后被抢占(暂时挂起),再轮到其它进程。一个逻辑流的执行在时间上与另一个流重叠被称为并发流,这两个流并发运行。
时间分片。就是多个流并发执行的概念被称为并发。一个进程与其他进程轮流运行的概念称为多任务。一个进程执行其控制流一部分的每一个时间段叫做时间片,多任务也就被称作

6.5.3 用户模式与内核模式
为了保护操作系统内核,处理器在某一个控制寄存器中的一个模式位,设置模式位时,进程就运行在内核模式中,否则运行在用户模式。内核模式的代码允许无限制地访问所有处理器指令集以及全部内存和 I/O 空间。如果用户模式的进程要享有此特权,它必须通过系统调用向设备驱动程序或其他内核模式的代码发出请求。另外,用户模式的代码允许发生缺页,而内核模式的代码则不允许。
运行程序代码初始时都是在用户模式中的,当发生中断故障或系统调用的异常时,进程从用户模式转变为内核模式。当异常发生时,控制传递到异常处理程序,处理器将模式转变为内核模式。内核处理程序运行在内核模式中,当它返回到应用程序代码时,处理器把模式从内核模式改回到用户模式。

6.5.4 进程上下文切换
通过内核重新启动一个被抢占的进程所得的状态,它由通用目的寄存器、浮点寄存器、软件计数器、用户栈、状态寄存器、内核栈和各种内核数据结构等对象的值构成。进程执行的某些时刻,内核能够决定抢占当前进程,并重新开始一个先前被抢占了的进程,这样的决策叫做调度,由内核中的调度器的代码处理。在这个抢占过程中需要用到上下文切换,上下文切换保存当前进程的上下文,恢复先前某个被抢占的上下文,并将控制传递给新恢复的进程。就是上下文就
在这里插入图片描述

图 34 进程上下文切换
6.6 hello的异常与信号处理
6.6.1异常类型
1.运行时异常:如除以零、空指针引用等,这类异常会导致程序崩溃。
2.资源异常:如资料未找到、内存不足等,这类异常通常需软件进行适当的错误处理。
3.输入异常:用户输入了不符合程序要求的数据。

6.6.2. 产生的信号
SIGINT:当用户按下Ctrl+C时发送,通常用于中断程序。
SIGTSTP:当用户按下Ctrl+Z时发送,用于暂停程序。
SIGTERM:请求应用终止的正常信号。

6.6.3具体信号处理与命令

1.乱按字
在这里插入图片描述

图 35 运行时乱按字

可能看到,在键盘中乱打字并没有改变printf的输出,不影响代码的正常运行。
2.按Ctrl+Z
在这里插入图片描述

图 36 运行时按Ctrl+Z

Ctrl+Z的功能是向进程发送SIGSTP信号,进程接收到该信号之后会将该作业挂起,但不会回收。下图表明了,PID为198364的hello进程仍然在运行中。
在这里插入图片描述

图 37 hello仍在运行

通过运行jobs指令,大家能够得知hello的后台job id=1
在这里插入图片描述

图 38 hello的后台job id

我们再调用fg命令。fg命令用于将后台作业(在后台运行的或者在后台挂起的作业)放到前台终端运行。运行结果如下。我们发现,挂起前后总共的输出次数仍为10次。

在这里插入图片描述

图 39 hello再次挂起运行

3.按Ctrl-C
我们在键盘中输入Ctrl+C,Ctrl-C命令内核向前台发送SIGINT信号,终止了前台作业。
在这里插入图片描述

图 40 Ctrl+C使得hello终止
4.不停按回车
在这里插入图片描述

图 41 运行时按回车键
通过我们发现,在hello执行过程中不停按回车,不仅在printf输出时会显示出回车,在hello进程执行完毕后,我们能够看出回车的信息也同样发送到了shell中,使shell进行了若干次的刷新换行。

6.7本章小结
1.进程管理是操作系统的核心功能,涉及进程创建、调度、终止全生命周期。
2.Shell通过fork-exec机制启动程序,支持前后台作业控制。
3.信号处理使脚本能够响应外部事件,保障健壮性。
4.上下文切换与模式切换是进程并发执行的基础。
通过本章分析,可深入理解操作系统如何管理进程资源,以及用户程序与内核的交互机制。

第7章 hello的存储管理
7.1 hello的存储器地址空间
存储器地址空间是程序运行时的内存访问逻辑,分为以下层次:
1.逻辑地址:程序代码中使用的地址(如变量地址),由编译器和链接器生成,体现为代码中的偏移量。
2.线性地址:通过段式管理将逻辑地址转换为连续的线性地址空间(x86-64架构中段式管理被弱化,逻辑地址直接映射为线性地址)。
3.虚拟地址:进程视角的地址空间(如0x400000),由操作系统通过页表映射到物理内存。
4.物理地址:实际内存芯片中的地址,由MMU(内存管理单元)通过页表转换得到。

Hello的地址空间:
代码段(.text):存放main函数的机器指令。
数据段(.data/.rodata):存放全局变量和字符串常量(如"用法: Hello…"),
堆(Heap):动态分配的内存(如malloc),向高地址增长。
栈(Stack):存放局部变量和函数调用信息(如i、argv),向低地址增长。
7.2 Intel逻辑地址到线性地址的变换-段式管理
在x86-64架构中,段式管理主要用于兼容历史设计,现代操作系统通常将段基址设为0,逻辑地址直接等于线性地址:
1.段选择符:从CS(代码段寄存器)或DS(数据段寄存器)中获取段描述符索引。
2.段描述符:描述段的基址、界限和权限,存储在全局描述符表(GDT)中。
3.线性地址计算:
逻辑地址 = 段基址(通常为0) + 偏移量 → 线性地址 = 偏移量。

Hello的段式管理:
由于段基址为0,hello程序的逻辑地址(如%rip指向的指令地址)直接作为线性地址。

在这里插入图片描述

图 42 Intel处理器的存储器寻址
7.3 Hello的线性地址到物理地址的变换-页式管理
线性地址(VA)到物理地址(PA)之间的转换通过分页机制完成。分页机制类似主存和Cache之间的分块机制,分页机制对虚拟地址和物理内存进行分页,页的大小通常是4KB到2M(因时而异,时过境迁,页的大小有所不同)。在x86-64机器上,虚拟地址空间的N是2的48次方,有256TB,比正常的硬盘大得多。
单射,则物理地址中某个地址所在页与虚拟空间的页的对应关系,也就知道了物理地址中某个地址所在页与硬盘中某个页的对应关系。就是在分页机制中,硬盘空间的每个字节到虚拟地址空间的每个字节存在映射关系,且这个映射是单射。虚拟地址空间和硬盘空间都以字节为单位,从0开始编地址号。设硬盘空间为H,虚拟地址空间为V,设他们之间的映射关系f
物理地址中某个地址所在页与虚拟空间的页的对应关系要通过什么来记录呢?分页机制中使用一个叫做页表的数据结构来记录这些关系,页表也是存储在内存中的,是由操作系统维护的。其实DRAM到Cache中也是类似机制,只不过DRAM到Cache的高速缓存机制是用硬件实现的。
每个进程都有一个页表,页表中的每一项,即PTE(页表条目),记录着该对应的虚拟地址空间的那一页是否有效(即是否有对应的物理内存上的页),物理页的起始位置或磁盘地址,访问权限等信息。PTE根据不同的映射状态也被划分为三种状态:未分配、未缓存、已缓存。
未分配:虚拟内存中未分配的页
还没有被缓存到物理内存中的页就是未缓存:已经分配但
已缓存:分配后缓存到物理页块中的页
在这里插入图片描述

图 43 虚拟内存和物理内存的映射关系

7.4 TLB与四级页表拥护下的VA到PA的变换
TLB(Translation Lookaside Buffer)是缓存页表项的硬件单元,加速地址转换:
1.TLB查找:根据虚拟页号查询TLB,若命中则直接获取物理页帧号。
2.TLB未命中:触发页表遍历,加载页表项到TLB。

Hello的TLB访问:
首次执行main函数时,代码页的虚拟地址触发TLB未命中,需遍历四级页表填充TLB条目。后续执行时TLB命中,加速地址转换。
在这里插入图片描述

图 44 TLB与四级页表支持下的VA到PA的变换
7.5 三级Cache拥护下的物理内存访问
Cache层次结构:
L1 Cache:分指令Cache(L1I)和数据Cache(L1D),访问延迟1~3周期。
L2 Cache:统一缓存,延迟约10周期。
L3 Cache:共享缓存,延迟约30~40周期。

Hello的Cache访问:
循环执行printf和sleep时,代码段(.text)和字符串常量(.rodata)被缓存在L1I和L1D中,减少对主存的访问。

在这里插入图片描述

图 45 Cache层次结构
7.6 hello进程fork时的内存映射
fork()的内存管理:

1.写时复制(Copy-on-Write):
子进程共享父进程的物理页,仅当修改页面时触发缺页中断,复制新物理页。
2.页表复制:子进程继承父进程的页表结构,但标记为只读。

Hello的fork行为:

Shell调用fork()创建子进程时,hello的代码段(只读)共享物理页,信息段(如堆、栈)初始为写时复制。
7.7 hello进程execve时的内存映射
execve()的内存管理:
1.释放旧地址空间:销毁当前进程的代码、数据、堆栈段。
2.加载新工具:
映射hello的代码段(.text)到虚拟地址0x4010f0。
初始化数据段(.data、.bss)和堆栈。
3.动态链接库映射:加载libc.so到进程地址空间的高地址区。
7.8 缺页故障与缺页中断处理
缺页中断流程:
1.触发条件:访问的虚拟地址未映射到物理内存或权限错误。
2.处理步骤:
内核检查缺页原因(如页面未加载、写只读页)。
若页面在磁盘(Swap或档案),调入物理内存并更新页表。
若权限错误(如写只读页),触发SIGSEGV终止进程。

Hello的缺页场景:
首次访问.text外的代码页或动态链接库时触发缺页中断,从磁盘加载页面。
在这里插入图片描述

图 46 缺页故障与缺页中断处理

7.9本章小结
1.地址空间管理是操作系统核心机制,逻辑地址利用页表映射为物理地址,TLB和Cache加速访问。
2.进程内存映射在fork和execve时动态调整,写时复制与动态加载优化资源使用。
3.缺页中断保障按需加载,动态内存分配策略影响脚本性能。
4.存储管理凭借软硬件协同(MMU、TLB、Cache)实现高效内存利用,支撑多进程并发执行。
凭借分析hello的存储管理,可深入理解操作系统如何抽象内存资源,平衡性能与隔离性,为程序提供透明的运行环境。

结论

  1. Hello的P2P与O2O全生命周期总结
    Hello程序从Program(程序)到Process(进程)的完整生命周期(P2P),以及从Zero(静态代码)到Zero(终止消亡)的全过程(O2O),体现了计算机系统各层抽象与核心技术的协同工作:
    A.从代码到进程的转换(P2P)
    预处理:展开头文件、宏替换、删除注释,生成纯净的C代码(.i)。
    编译:将C代码转换为机器相关的汇编指令(.s),暴露语法与逻辑错误。
    汇编:生成可重定位目标记录(.o),包含未解析的符号与重定位信息。
    链接:合并目标记录与库,解析符号地址,生成可执行文件(hello)。
    加载与执行:利用fork()和execve()创建进程,虚拟内存映射与动态链接库加载完成程序运行环境初始化。

B.从创建到终止的完整周期(O2O)
静态存储:hello.c以文本形式存在于磁盘,占用存储空间但未运行。
动态执行:进程借助时间片调度、上下文切换和信号处理实现并发与健壮性。
资源回收:进程终止时释放内存、关闭文件描述符,内核清除进程控制块(PCB)。

  1. 计算机系统的核心技术与设计思想
    通过Hello的案例,深入理解了计算机系统的核心机制:
    A.编译与链接的协同
    编译器、汇编器和链接器借助多阶段协作,将高级语言映射为机器指令。
    动态链接(PLT/GOT)与静态链接的权衡,搭建了代码共享与灵活更新。

B.操作系统的资源管理
进程管理:fork-exec机制、信号处理与作业控制,支撑多任务并发执行。
存储管理:虚拟内存、页表与TLB的协同,平衡性能与安全性;写时复制(Copy-on-Write)优化资源利用率。
异常处理:缺页中断与信号传递机制,保障程序健壮性。

C.硬件加速与抽象
Cache层次结构:L1/L2/L3 Cache减少内存访问延迟,提升程序执行效率。
MMU与地址转换:将逻辑地址透明映射为物理地址,隔离进程内存空间。

  1. 实践感悟与创新思考
    A.理论与实践的结合
    凭借分析预处理、反汇编和ELF文件结构,深化了对编译过程与目标文件格式的理解。

调试信号处理与缺页中断,直观体会了操作系统内核与硬件的交互机制。

B.系统设计的精妙性
软硬件协同:从编译器优化到TLB缓存,每一层抽象都隐藏了复杂性,同时提供高效执行环境。
权衡与折中:动态链接牺牲部分启动速度换取灵活性与共享性;写时复制以空间换时间,减少进程创建开销。

C.创新方向展望
编译优化:探索基于LLVM的定制化编译链,针对特定场景优化代码生成。
内存管理改进:设计更高效的页表结构(如五级页表)以支持更大地址空间;研究新型Cache替换策略(如AI驱动的预取算法)。
安全增强:结合硬件安全扩展(如Intel SGX),构建进程内存的强隔离与加密保护。

  1. 总结
    Hello软件虽小,却贯穿了计算机系统的核心脉络——从代码的文本表示到进程的动态执行,每一环节都凝聚了编译技能、操作系统与硬件设计的智慧。依据本次大作业,不仅掌握了工具链的使用与底层原理的分析方法,更深刻认识到计算机系统作为一门“抽象的艺术”,如何在复杂性与效率之间找到平衡。未来,这种系统级思维将助力于开发更高效、安全、可扩展的软件与硬件系统。

附件
hello.i
作用:预处理后的C代码文件,包括所有头文件展开和宏替换结果。
生成命令:gcc -E hello.c -o hello.i

hello.s
作用:编译生成的x86-64汇编代码文件,保留符号和标签信息。
生成命令:gcc -S hello.i -o hello.s

hello.o
作用:可重定位目标文件(ELF格式),包含未解析符号的机器码。
生成命令:gcc -c hello.s -o hello.o

hello
作用:可执行目标文件,通过链接器合并hello.o与C标准库生成。
生成命令:gcc hello.o -o hello

反汇编文件
hello_o_disasm.txt:hello.o的反汇编结果(命令:objdump -d -r hello.o)
hello_disasm.txt:hello的反汇编结果(命令:objdump -d hello)

参考文献
[1] 林来兴. 空间控制技术[M]. 北京:中国宇航出版社,1992:25-42.
[2] 辛希孟. 信息技术与信息服务国际研讨会论文集:A集[C]. 北京:中国科学出版社,1999.
[3] 赵耀东. 新时代的工业工程师[M/OL]. 台北:天下文化出版社,1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).
[4] 谌颖. 空间交会控制理论与方式研究[D]. 哈尔滨:哈尔滨工业大学,1992:8-13.
[5] KANAMORI H. Shaking Without Quaking[J]. Science,1998,279(5359):2063-2064.
[6] CHRISTINE M. Plant Physiology: Plant Biology in the Genome Era[J/OL]. Science,1998,281:331-332[1998-09-23]. http://www.sciencemag.org/cgi/ collection/anatmorp.

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

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

相关文章

2025 年 11 月断桥铝门窗实力厂家推荐榜:节能静音系统窗/阳台窗/定制门窗,匠心工艺与高性价比之选

2025 年 11 月断桥铝门窗实力厂家推荐榜:节能静音系统窗/阳台窗/定制门窗,匠心工艺与高性价比之选 随着建筑节能标准的不断提升和消费者对居住舒适度要求的日益增长,断桥铝门窗行业迎来了新一轮技术革新与市场洗牌。…

105_尚硅谷_continue执行流程分析

105_尚硅谷_continue执行流程分析1.continue执行流程案例1 2.continue执行流程案例2 3.continue执行流程案例3

2025年宁波GEO优化服务商综合推荐排行榜单:十大权威机构深度解析

摘要 随着人工智能技术的快速发展,宁波GEO优化行业在2025年迎来了爆发式增长。根据艾瑞咨询最新数据显示,宁波地区AI优化服务市场规模同比增长达67%,越来越多的企业开始重视在DeepSeek、文心一言等主流AI平台的品牌…

质量管理数字化,中小企业如何少走弯路?

这几年,越来越多的公司开始搞数字化改造,质量管理这块也不例外。大家各显神通:有的用电子屏代替纸质文件,有的把检验记录扫描存档,有的用Excel录入数据,还有的上MES、ERP或者自己开发小程序。 但这些做法大多只解…

2025年颗粒分析仪直销厂家权威推荐榜单:激光粒度检测仪/在线粒度仪/电位仪源头厂家精选

颗粒分析仪作为现代工业检测与科学研究的重要工具,其市场规模持续增长。根据行业数据显示,2024年全球颗粒分析仪器市场规模已达58.7亿元,预计2025年将突破65亿元。在材料、制药、化工、环保等领域,精准的颗粒分析可…

2025 年 11 月氮氧化物检测仪工厂实力推荐榜:专业制造与精准监测口碑之选,覆盖便携式/在线式/固定式检测仪优质厂家深度解析

2025 年 11 月氮氧化物检测仪工厂实力推荐榜:专业制造与精准监测口碑之选,覆盖便携式/在线式/固定式检测仪优质厂家深度解析 行业背景与发展现状 随着我国环保政策的持续深化和工业安全标准的不断提高,氮氧化物检测…

SELECT 1001020; date_diff

ocean base OBSELECT date_diff(second, 2010-11-30 23:59:59, 2010-11-30 23:58:59),50<date_diff(second, 2010-11-30 23:59:59, 2010-11-30 23:58:59),date_diff(second, 2010-11-30 23:59:59, 2010-11-30 23:58…

2025 年 11 月靶材厂家权威推荐榜:溅射/磁控溅射/镀膜/旋转靶材,ITO/半导体/光学镀膜/陶瓷/金属/钛/铝/铜/钨/钼/钽/硅/合金/稀土靶材精选品牌

2025 年 11 月靶材厂家权威推荐榜:溅射/磁控溅射/镀膜/旋转靶材,ITO/半导体/光学镀膜/陶瓷/金属/钛/铝/铜/钨/钼/钽/硅/合金/稀土靶材精选品牌 行业背景与发展趋势 随着全球半导体、光伏、显示面板等高科技产业的快速…

[2022 东北赛] F - Tree Path

tag: 二分,树,ST 表,线段树牛客 tag: 二分,树,ST 表,线段树一棵 \(n\) 节点的树,树上的 \(k\) 条路径 \(p_i\leadsto q_i\) 有权值 \(v_i\)。执行 \(m\) 次操作,包含下面两种:操作 0:删除权值最小的路径。 操…

2025 年 11 月 geo 优化服务商测评:核心能力与适配场景

2025年,数字化浪潮与AI技术正全方位重塑全球搜索生态,搜索引擎的流量格局正经历颠覆性重构——Gartner最新预测指出,到2026年,全球传统搜索引擎的访问量将大幅下滑25%,而AI聊天机器人已成功分流近四分之一的搜索流…

质量管理系统(QMS)的功能有哪些?

别再头疼质量问题了!一套QMS系统到底能帮你做什么? 你是不是也遇到过这些情况?每天早会,老板问起质量问题,你手忙脚乱找数据?客户突然来审厂,要查三个月前的某批货的质量记录?供应商总在同一个问题上反复…

2025 年 11 月残疾人税收优惠政策权威推荐指南:精准筹划与合规减免,助力企业高效享受国家扶持红利

2025 年 11 月残疾人税收优惠政策权威推荐指南:精准筹划与合规减免,助力企业高效享受国家扶持红利 一、残疾人就业税收优惠政策背景与价值 残疾人就业税收优惠政策作为国家促进残疾人就业的重要举措,已形成涵盖增值…

【UR #5】怎样跑得更快

给定整数 \(c\) 和 \(d\) 和质数 \(p=998244353\)。有 \(q\) 次询问,每次询问给定长度为 \(n\) 的序列 \(b\),解方程组: \(\forall i \in [1,n],\sum\limits_{j=1}^{n} \gcd(i,j)^c \times \operatorname{lcm}(i,j)…

2025年JA型弹簧减震器实力厂家权威榜单:YDS型阻尼弹簧减震器/ZGT型阻尼弹簧减震器/封闭式阻尼弹簧减震器源头厂家精选

在工业减震领域,JA型弹簧减震器凭借其高承载性和抗疲劳性,已成为众多工业设备的首选减震方案。 在工业设备运行中,振动控制直接关系到设备稳定性、生产安全和使用寿命。据国际振动控制协会2024年度测评数据显示,优…

再也不用为质量报表犯愁了

每天开质量会议,你是不是也经常遇到这些头疼事? 痛点一:开会时一问三不知早会时老板问:昨天都出了哪些质量问题?周会时领导问:这周外观问题多还是尺寸问题多?月会时经理问:这个月哪个供应商问题最多?哪…

千企实证:2025 年 11 月靠谱 GEO 公司优选指南

2025年生成式AI与地理信息技术的深度融合,让GEO优化成为企业破局流量困局的关键。但市场上GEO服务商数量激增,技术路径、行业适配度与服务模式差异悬殊,企业在选择服务商时往往面临技术实力难甄别、服务效果难保障、…

2025 年 11 月甲醛检测仪厂家权威推荐榜:精准检测与长效稳定,专业甄选家用/工业甲醛检测仪、便携式检测仪优质品牌!

2025 年 11 月甲醛检测仪厂家权威推荐榜:精准检测与长效稳定,专业甄选家用/工业甲醛检测仪、便携式检测仪优质品牌! 随着室内空气质量问题日益受到重视,甲醛检测仪作为环境监测领域的重要工具,在工业安全、家居健…

银河麒麟服务器安装KVM虚拟机

银河麒麟服务器安装KVM虚拟机yum install -y qemu-kvm libvirt libguestfs-tools virt-install virt-manager systemctl start libvirtdsystemctl enable libvirtd 在终端运行 virt-manager 启动kvm虚拟机管理器

IDEA高效快捷键清单(实习生专供)

提示:本文列出的快捷键为Windows/Linux系统默认配置,Mac用户可将Ctrl键替换为Command键,Alt键替换为Option键。 还记得我刚入职实习那会,每天最挫败的时刻不是解不出业务逻辑,而是看着导师的手指在键盘上翻飞,短…

2025最新SmartKnob开发指南:从固件烧录到Web Serial交互全流程

你还在为复杂的旋钮控制器开发而烦恼吗?SmartKnob作为一款开源的触觉输入旋钮(Haptic input knob),通过软件定义的终端止动(endstops)和动态定位点(dynamic detents),为开发者提供了高度可定制的交互体验。本…