
计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 电子与信息工程学院
学 号 2023111573
班 级 23L0502
学 生 刘沛霖
指 导 教 师 刘宏伟
计算机科学与技术学院
2024年5月
摘 要
本研究以典型 C 语言程序hello.c为分析载体,系统阐释程序从源代码生成到进程执行的完整生命周期。通过对预处理、编译、汇编、链接、进程激活及存储寻址等关键环节的深度剖析,揭示计算机系统底层协同机制。实验基于 Ubuntu 22.04 LTS 操作系统平台,综合运用 GCC 工具链、二进制分析工具及动态调试器(GDB),详尽记录程序形态转换的技术细节。重点探讨 ELF 文件格式规范、虚拟地址空间组织策略、动态符号解析机制、进程调度算法及内存分层管理技术,验证现代计算机系统通过分层抽象与资源优化实现高效执行的设计范式,为系统级软件开发与性能调优提供理论支撑与实践路径。
关键词:计算机系统;程序生命周期;ELF格式;进程管理;存储管理;动态链接
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
目录
第1章 概述
1.1 Hello简介
1.2 环境与工具
1.3 中间结果
1.4 本章小结
第2章 预处理
2.1 预处理的概念与作用
2.2在Ubuntu下预处理的命令
2.3 Hello的预处理结果解析
2.4 本章小结
第3章 编译
3.1 编译的概念与作用
3.2 在Ubuntu下编译的命令
3.3 Hello的编译结果解析
3.4 本章小结
第4章 汇编
4.1 汇编的概念与作用
4.2 在Ubuntu下汇编的命令
4.3 可重定位目标elf格式
4.4 Hello.o的结果解析
4.5 本章小结
第5章 链接
5.1 链接的概念与作用
5.2 在Ubuntu下链接的命令
5.3 可执行目标文件hello的格式
5.4 hello的虚拟地址空间
5.5 链接的重定位过程分析
5.6 hello的执行流程
5.7 Hello的动态链接分析
5.8 本章小结
第6章 hello进程管理
6.1 进程的概念与作用
6.2 简述壳Shell-bash的作用与处理流程
6.3 Hello的fork进程创建过程
6.4 Hello的execve过程
6.5 Hello的进程执行
6.6 hello的异常与信号处理
6.7本章小结
第7章 hello的存储管理
7.1 hello的存储器地址空间
7.2 Intel逻辑地址到线性地址的变换-段式管理
7.3 Hello的线性地址到物理地址的变换-页式管理
7.4 TLB与四级页表支持下的VA到PA的变换
7.5 三级Cache支持下的物理内存访问
7.7 hello进程execve时的内存映射
7.8 缺页故障与缺页中断处理
7.9动态存储分配管理
7.10本章小结
结论
附件
参考文献
第1章 概述
1.1 Hello简介
根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。
Hello 的 P2P 过程(From Program to Process)
P2P 即程序到进程的转换过程,体现了计算机系统从静态代码到动态执行的核心机制,涉及编译系统、进程管理、内存映射与 CPU 调度等关键环节:
程序(Program)的诞生
用户通过编辑器将代码写入hello.c,形成磁盘上的静态源文件(程序)。该文件需经过编译流程四阶段:
预处理(cpp):展开头文件、宏定义,生成.i文件(如处理#include指令);
编译(gcc):将 C 代码翻译为汇编语言,生成.s文件;
汇编(as):汇编器将汇编代码转换为机器指令,生成.o目标文件;
链接(ld):连接器合并目标文件与标准库,生成可执行程序(如hello),此时程序以二进制形式存储于磁盘,尚未占用内存资源。
进程(Process)的激活
在 Shell 中执行./hello时,操作系统通过以下步骤将程序转换为进程:
进程创建:父进程(Shell)调用fork()系统调用创建子进程,复制自身地址空间;
程序加载:子进程通过execve()系统调用加载hello可执行文件,清空原有地址空间并建立新的虚拟地址空间映射(如代码段、数据段、堆、栈);
内存管理:操作系统通过mmap()将可执行文件内容映射到虚拟内存,同时借助MMU(内存管理单元)完成虚拟地址(VA)到物理地址(PA)的转换,依赖TLB(转换检测缓冲区)、4 级页表和3 级 Cache加速地址翻译,确保程序指令与数据的快速访问;
CPU 调度:进程进入就绪队列,操作系统通过时间片轮转算法为其分配 CPU 资源,使其进入运行态,执行 “取指 – 译码 – 执行” 流水线操作,完成代码逻辑(如输出 “Hello”)。
进程的生命周期
程序执行完毕后,进程进入终止态:操作系统回收其占用的内存(如通过munmap()释放mmap区域)、文件句柄等资源,进程状态变为 “僵尸进程” 直至父进程调用wait()读取其退出状态,最终彻底销毁进程控制块(PCB)。
二、Hello 的 O2O 过程(From Zero-0 to Zero-0)
O2O 体现程序从 “零” 状态启动到资源归零的完整生命周期,涵盖存储初始化、执行期资源占用与最终释放的闭环:
初始零状态(Zero-0):程序未运行时的 “无”
存储层面:磁盘上的hello可执行文件中,未初始化的全局变量(如int x;)对应二进制值为 0 或未定义,称为 “BSS 段”;代码段(.text)和已初始化数据段(.data)则存储明确的指令与数据。
地址空间:程序未加载时,虚拟地址空间尚未创建,VA 与 PA 无映射关系,操作系统通过页表机制为后续加载预留地址空间结构。
执行期非零状态:资源占用的 “有”
程序运行时,操作系统为其分配非零资源:
内存资源:通过mmap分配代码段、数据段、堆、栈的虚拟地址区域,其中堆用于动态内存分配(如malloc),栈用于存储函数调用栈帧;物理内存通过页调度机制(如缺页中断)按需加载到 Cache,提升访问效率。
CPU 资源:进程通过时间片获得 CPU 使用权,执行算术操作(如+、*)、逻辑操作(如&&、|)、控制转移(如if、for)等指令,完成计算任务。
I/O 资源:通过系统调用(如write)操作键盘、显示器等设备,实现输入输出,期间涉及中断处理与 DMA(直接内存访问)技术,减少 CPU 干预。
终止归零状态(Zero-0):资源回收的 “无”
程序执行结束后,操作系统执行零化清理:
内存归零:通过free或自动回收机制释放动态分配的内存(如堆空间),页表条目被删除,VA 与 PA 映射解除,物理内存页可能被写回磁盘(如Pagefile)或标记为空闲;
进程状态归零:进程退出状态(通常为 0)存入 PCB,父进程读取后,内核销毁 PCB,进程标识符(PID)回归可用池,实现 “从进程到无进程” 的状态归零;
I/O 资源归零:关闭打开的文件描述符,释放设备控制权,中断处理程序恢复初始状态,确保设备就绪等待下一次操作。
1.2 环境与工具
列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开发与调试工具。
硬件环境:X64 CPU;2.5GHz;16GRAM
软件环境:Windows11;VMware Workstation Pro;Ubuntu 20.24
开发工具:Codeblocks 64位;vi/vim/gedit+gcc;GDB;OBJDUMP;EDB
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
hello.i:预处理文件,展开头文件与宏定义,指令:gcc -E hello.c -o hello.i
hello.s:汇编代码文件,指令:gcc -S hello.i -o hello.s
hello.o:可重定位目标文件,指令:gcc -c hello.s -o hello.o
hello:可执行文件,指令:gcc hello.o -o hello
1.4 本章小结
本章概述 Hello 程序的生命周期、实验环境及中间文件,构建从程序到进程的整体认知,为后续章节的深入分析奠定基础。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
预处理的概念:
程序设计领域中,预处理一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。
预处理器的作用:
一般由预处理器(preprocessor)对程序源代码文本进行处理,这个过程并不对程序的源代码进行解析,但它把源代码分割或处理成为特定的单位。
2.2在Ubuntu下预处理的命令
命令:gcc -E hello.c -o hello.i

2.3 Hello的预处理结果解析

预处理器将头文件替换为了对本地文件的引用,增加了大段头文件本身的内容,不会对头文件中的内容做任何计算或转换,只是简单地复制和替换。
2.4 本章小结
本章讲述了编译过程的第一步预处理,介绍了预处理的概念和作用,并通过实际操作生成了hello.i文件,通过对比分析清楚了预处理的作用与原理。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
编译阶段将预处理后的 C 代码(hello.i)转换为汇编代码(hello.s),核心步骤包括:
词法分析:分解为 token(如关键字、操作符);
语法分析:构建抽象语法树(AST),检查语法正确性;
语义分析:类型检查、作用域解析;
代码生成:生成目标架构的汇编指令;
代码优化:常量传播、死代码消除等。
注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序
3.2 在Ubuntu下编译的命令
命令:gcc -S hello.i -o hello.s

3.3 Hello的编译结果解析



3.3.1 程序元信息和字符串常量
.file “hello.c”:标识汇编代码由 hello.c 源文件生成,供编译器追踪原始文件。
.section .rodata:定义只读数据段,存放字符串常量。
.LC0:存储参数错误提示信息(中文),以 UTF – 8 编码的八进制转义序列表示(如 347224250 等),供 puts 输出。
.LC1:定义 printf 的格式字符串 “Hello %s %s %s
“,用于格式化输出。
3.3.2 函数框架
endbr64:安全特性指令,防止控制流劫持攻击。
pushq %rbp + movq %rsp, %rbp:建立函数栈帧,保存旧基址指针并更新当前基址指针。
subq $32, %rsp:在栈中分配 32 字节空间,用于存储局部变量。
3.3.3 参数检查
用 cmpl 指令比较参数数量(如 cmpl $5, -20(%rbp)),若不相等(参数错误),则通过 je .L2 跳转至错误处理(如调用 puts 输出 .LC0 后 exit)。
借助过程链接表(PLT)调用库函数(如 call puts@PLT、call exit@PLT),实现动态链接。
3.3.4 主循环结构
利用 jmp 和条件跳转指令(如 jle)实现 for 循环逻辑。
通过指针算术运算(如 addq $8, %rax,64 位系统中指针占 8 字节)访问 argv 数组元素。
循环内调用 printf@PLT 输出信息,调用 sleep@PLT 实现延迟(如 call sleep@PLT)。
3.3.5 函数返回
call getchar@PLT:等待用户输入,暂停程序。
movl $0, %eax:设置函数返回值为 0,表示正常退出。
leave:恢复栈帧(等价于 movq %rbp, %rsp; popq %rbp)。
ret:返回调用者(通常为操作系统)。
3.3.6 数据与类型处理
常量处理:
字符串常量通过 .string 指令定义,存于 .rodata 段。中文字符转换为 UTF – 8 编码的八进制转义序列(如 .LC0),供汇编器解析。
变量处理:
movl 指令处理 32 位变量(如 movl $0, -20(%rbp) 存储 int 类型)。
movq 指令处理 64 位指针(如 movq %rax, -32(%rbp) 存储 argv 指针数组首址)。
类型转换:
atoi@PLT 显式将字符串转为整数(如 call atoi@PLT)。
movl 指令隐式扩展 32 位(如 movl %eax, -24(%rbp),%eax 为 32 位寄存器)。
3.3.7 表达式与操作符
算术运算:
addq 实现指针运算(如 addq $8, %rax,计算 argv 数组元素偏移)。
addl 实现整数运算(如 addl $1, -4(%rbp),整数自增)。
关系运算:
cmpl $5, -20(%rbp) 比较参数数量,结合 je 实现条件跳转(如参数错误跳转)。
位运算:
寄存器清零(如 movl $0, %eax)隐含位操作,将所有位设为 0。
3.3.8 控制流实现
if 条件判断:
cmpl + je 组合(如 cmpl $5, -20(%rbp); je .L2),根据比较结果(零标志位)决定是否跳转,划分基本块。
for 循环:
循环入口(.L3):cmp $9, -4(%rbp) 比较循环计数器。
满足条件(jle .L4)则跳转执行循环体,否则退出。
3.3.9 函数操作
参数传递:
遵循 System V AMD64 ABI 约定,前 6 个参数用寄存器传递(如 leaq .LC1(%rip), %rax; movq %rax, %rdi 传递格式字符串地址至 %rdi)。
函数调用:
call 指令实现调用(如 call printf@PLT),通过 PLT 实现延迟绑定,动态链接库函数。
返回值处理:
返回值通过 %eax 传递(如 movl $0, %eax),leave 恢复栈帧后 ret 返回。
3.3.10 内存访问
数组访问:
基址 + 偏移量方式(如 movq -32(%rbp), %rax 取 argv 首址,addq $8, %rax 计算 argv[1] 地址,movq (%rax), %rcx 间接寻址取内容)。64 位系统中,指针占 8 字节。
3.3.11 其他特性
sizeof 处理:
编译器静态计算 sizeof 结果,直接使用常量(如定义数组或变量时隐含空间分配)。
复合赋值:
虽无显式复合赋值语句,但类似 addl $1, -4(%rbp)(等价于 cnt++)实现了隐含的复合赋值操作。
此部分是重点,说明编译器是怎么处理C语言的各个数据类型以及各类操作的。应分3.3.1~ 3.3.x等按照类型和操作进行分析,只要hello.s中出现的属于大作业PPT中P4给出的参考C数据与操作,都应解析。
3.4 本章小结
这一章介绍了C编译器如何把hello.i文件转换成hello.s文件的过程,简要说明了编译的含义和功能,演示了编译的指令,并分析生成的hello.s文件中的汇编代码,明确了汇编代码是如何通过一些更底层的操作实现我们编写的高级语言程序中的功能的。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
(以下格式自行编排,编辑时删除)
汇编的概念
汇编语言是一种与硬件强相关的低级语言,通过助记符(如add表示加法、mov表示数据移动)和符号化地址(如标签loop:)替代机器语言的二进制编码,便于程序员编写和阅读。但计算机硬件只能识别二进制形式的机器指令,因此需要通过汇编器将.s文件转换为.o目标文件。
输入:汇编语言源文件(.s),由汇编指令(如mov eax, 1)、伪指令(如.section .data定义数据段)、符号(如标签main:)组成。
输出:目标文件(.o),包含机器语言二进制代码、数据、符号表、重定位信息等结构。
汇编的作用
汇编语言是一种与硬件强相关的低级语言,通过助记符(如add表示加法、mov表示数据移动)和符号化地址(如标签loop:)替代机器语言的二进制编码,便于程序员编写和阅读。但计算机硬件只能识别二进制形式的机器指令,因此需要通过汇编器将.s文件转换为.o目标文件。
输入:汇编语言源文件(.s),由汇编指令(如mov eax, 1)、伪指令(如.section .data定义数据段)、符号(如标签main:)组成。
输出:目标文件(.o),包含机器语言二进制代码、数据、符号表、重定位信息等结构。
注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。
4.2 在Ubuntu下汇编的命令
(以下格式自行编排,编辑时删除)
命令:gcc -c -o hello.o hello.s


应截图,展示汇编过程!
4.3 可重定位目标elf格式
首先使用命令:readelf -a hello.o > hello_o.elf,得到hello_o.elf文件
4.3.1 ELF头
ELF(Executable and Linkable Format,可执行与可链接格式)是 Linux、Unix 等系统中广泛使用的目标文件格式(包括可重定位文件.o、可执行文件、共享库.so等)。ELF 头是目标文件的 “元数据入口”,位于文件起始位置(偏移 0 处),其核心作用是向链接器、加载器等工具提供解析文件所需的全局信息,确保后续处理(如链接、加载)能正确识别文件结构和内容。

4.3.2程序头
程序头是 ELF 文件中描述文件内容如何映射到内存的关键结构,主要用于可执行文件(.out)和共享库(.so)。它由一组固定格式的条目(Entry)组成,每个条目描述一个内存段(Segment的属性(如代码段、数据段、动态链接信息段等)。操作系统的加载器(Loader)通过解析程序头,将 ELF 文件的各个部分正确映射到内存地址空间,并设置对应的访问权限,为程序执行做好准备。

4.3.3关键节区
代码节区
.text:存储程序的机器指令(CPU 直接执行的操作序列),是程序运行的核心逻辑。
.plt:动态链接的 “桥梁”,用于运行时解析外部共享库函数的地址(如调用printf时触发地址查找)。
数据节区
.rodata:存储只读常量(如字符串”Hello”、全局const变量),程序运行中不可修改,保障数据安全。
.data:存储可读写的全局 / 静态变量(如int global = 10),程序运行中可修改其值。

4.3.4 重定位节
偏移量(Offset):需修正的指令在.text节中的位置。
类型(Type):
R_X86_64_PC32:PC 相对寻址修正(用于数据引用)。计算方式:重定位值 = 符号地址 + 加数 – (PC值 + 4)。
R_X86_64_PLT32:过程链接表(PLT)修正(用于函数调用)。计算方式:重定位值 = 符号在PLT中的地址 + 加数 – (PC值 + 4)。
符号名称(Symbol Name):外部符号(如puts、printf):需链接到标准库。节符号:指向当前模块内的节,用于数据访问。
加数(Addend):修正公式中的偏移量,用于调整目标地址。

4.3.5 符号表
符号表记录了程序中所有函数和变量的名称、地址、类型等信息。它为调试器、链接器等工具提供了对程序内部结构的详细描述,使得这些工具能够正确地解析和操作程序中的各个符号。例如,调试器可以通过符号表获取函数的名称和地址,从而在调试过程中方便地设置断点和查看变量值。
动态

静态


分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。
4.4 Hello.o的结果解析
(以下格式自行编排,编辑时删除)


通过对比hello.c源程序经汇编生成的hello.s文件与hello.o目标文件的反汇编代码,可总结以下关键差异:
1. 格式差异:是否包含机器码
汇编代码(hello.s):纯文本格式,仅包含人类可读的汇编指令(如subq $32, %rsp),无机器码(二进制或十六进制指令编码)。
反汇编代码:每行前附加对应指令的机器码(如48 83 ec 20),直观展示指令的二进制形式。
2. 数据表示:立即数的进制形式
汇编代码(hello.s):立即数(如栈空间申请大小)以十进制表示(如subq $32, %rsp中的32)。
反汇编代码:立即数转换为十六进制(如48 83 ec 20对应subq $0x20, %rsp中的0x20,即十进制 32)。
3. 跳转逻辑:符号化标识与地址计算
汇编代码(hello.s):通过 ** 助记符(如.LC0)** 标记数据或代码位置,直接声明其所属段(如.rodata),跳转目标为符号名(如call .LC0)。
反汇编代码:跳转基于地址偏移计算,通过当前 PC 值(程序计数器)与机器码中的偏移量相加,确定目标地址(如PC=0x15时,0x15+0x14=0x29为跳转目标)。
4. 重定位处理:符号声明与地址预留
汇编代码(hello.s):重定位(如外部函数调用)通过助记符直接声明(如call printf),依赖汇编器记录符号引用。
反汇编代码:重定位表现为预留地址占位符(如机器码中未填充的地址字段),供链接器在最终生成可执行文件时填充实际地址。
总的来说,汇编代码是 “人读” 的符号化指令,侧重可读性与逻辑表达;反汇编代码是 “机读” 的二进制指令的文本映射,侧重展示机器码与地址细节,二者差异本质反映了程序从 “符号→二进制” 的转换过程。
objdump -d -r hello.o 分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。
说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。
4.5 本章小结
本章聚焦于从汇编代码(hello.s)到可重定位目标文件(hello.o)的转换过程,借助readelf和objdump工具解析hello.o的 ELF 格式,深入分析节区划分、重定位信息与符号表等结构。通过对比汇编代码与反汇编结果,明确汇编指令到机器码的映射关系,揭示链接阶段需解决的符号重定位问题,为后续多目标文件合并成可执行程序提供底层逻辑支撑。
(第4章1分)
第5章 链接
5.1 链接的概念与作用
从hello.o到hello的链接是指通过链接器将可重定位目标文件(hello.o)与库文件整合,生成可执行程序(hello)的过程。其核心作用包括:
1.符号解析:解决hello.o中未定义的外部符号(如printf)引用问题,从动态库(如libc.so)匹配符号地址,确保程序能调用外部功能。
2.地址重定位:将hello.o中基于文件的相对地址(如.text段内偏移)转换为虚拟内存绝对地址(如0x400000),修正指令和数据的引用地址,确保内存正确寻址。
3.段合并与布局:合并.text(代码)、.data(数据)等节区,按 ELF 规范分配权限(如代码段只读可执行),优化内存访问效率与安全性。
4.生成可执行实体:输出符合 ELF 规范的hello,包含程序头表(指导内存加载)和入口地址,供操作系统加载运行。
注意:这儿的链接是指从 hello.o 到hello生成过程。
5.2 在Ubuntu下链接的命令
命令:

实际链接过程隐含调用了 ld,并链接了默认的启动文件(crt1.o)和库(libc.so)。

使用ld的链接命令,应截图,展示汇编过程! 注意不只连接hello.o文件
5.3 可执行目标文件hello的格式
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。
使用readelf解析hello的ELF格式,得到hello的节信息和段信息
指令:

5.3.1 ELF头
除节头数量由14个变成27个,其他的保持不变。

5.3.2程序头
用于描述文件在内存中的加载和运行属性

5.3.3关键节区:
关键节区主要包含有代码节区(.text 和 .plt)和数据节区(.rodata 和 .data

5.3.4重定位信息
重定位信息是 ELF 文件中用于修正外部符号引用的关键部分。在程序编译时,某些符号(如函数名、变量名)可能尚未确定其最终地址,重定位信息记录了这些符号的位置和需要修正的偏移量。

5.3.5符号表
符号表记录了程序中所有函数和变量的名称、地址、类型等信息。它为调试器、链接器等工具提供了对程序内部结构的详细描述,使得这些工具能够正确地解析和操作程序中的各个符号



5.4 hello的虚拟地址空间
使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。


程序从地址0x400000开始到0x401000被载入,虚拟地址从0x4000000x400f0结束,根据5.3中的节头部表,可以通过edb找到各段的信息。
如.interp节,在hello.elf文件中能看到开始的虚拟地址,同样的,我们可以找到如.text节的信息。
5.5 链接的重定位过程分析
objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。







对比如下
|
对比维度 |
hello.o(可重定位目标文件) |
hello(可执行文件) |
|
地址空间 |
采用相对地址,代码和数据位置未固定(如.text段起始为0x0)。 |
采用绝对虚拟地址,由链接器统一分配(如.text段起始为0x401000)。 |
|
符号状态 |
外部符号(如puts)标记为UND(未定义),需链接时从库文件中解析地址。 |
外部符号已绑定到共享库(如puts@GLIBC_2.2.5),通过GOT(全局偏移表)和PLT(过程链接表)实现运行时调用。 |
|
重定位表 |
包含.rela.text(代码段重定位表)和.rela.data(数据段重定位表),记录需要修正的地址(如函数调用、全局变量引用)。 |
所有地址引用已修正(如call puts@plt指令指向0x401090)。 |
|
入口点 |
无_start函数,无法独立运行(仅为中间文件,需链接后生成完整程序)。 |
设置_start函数(如入口地址0x4010f0)作为程序起点。 |
结合hello.o的重定位项目,分析hello中对其怎么重定位的。
5.6 hello的执行流程
使用gdb/edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程(主要函数)。请列出其调用与跳转的各个子程序名或程序地址。

1. 加载与入口启动
操作系统加载:通过动态链接器ld-linux-x86-64.so.2将hello映射到虚拟内存,解析.dynamic段加载libc.so,填充GOT/PLT解析外部符号(如puts)。
入口点_start(地址如0x4010f0):
初始化栈帧,读取参数argc/argv。
调用__libc_start_main@plt(通过 PLT 跳转到libc.so的0x7ffff7a220b0)。
2. libc启动与main调用
__libc_start_main(libc.so函数):
初始化libc环境(堆、I/O 缓冲区),调用用户初始化函数(如有)。
调用用户main函数(地址如0x401125),传递argc、argv、环境变量envp。
3. main函数执行
用户逻辑:执行printf等操作,内部通过printf@plt跳转至libc.so的printf实际地址(如0x7ffff7a3d380),最终调用系统调用write输出内容。
返回值:main返回0(正常退出)。
4. 程序终止
exit函数(libc.so函数,地址如0x7ffff7a16560):
执行atexit注册的清理函数,刷新 I/O 缓冲区。
调用_exit系统调用(地址如0x7ffff7a16590),内核回收进程资源,结束程序。
关键调用链:
_start → __libc_start_main → main → exit → _exit(系统调用终止)。
5.7 Hello的动态链接分析
分析hello程序的动态链接项目,通过edb/gdb调试,分析在动态链接前后,这些项目的内容变化。要截图标识说明。
动态链接采用延迟加载(Lazy Binding)策略,将程序按功能模块划分为独立组件,仅在运行时根据实际需要将这些模块动态组装为完整程序。这一机制依赖两个关键结构实现函数的动态链接:全局偏移表(GOT,Global Offset Table)和过程链接表(PLT,Procedure Linkage Table)。
在动态链接初始化(dl_init)阶段前后,全局偏移表(.got段)的内容会发生显著变化。尤其是其中的.got.plt子段(专门用于过程链接的偏移表),会在动态链接过程中被动态链接器更新 —— 当程序首次调用外部函数(如printf)时,动态链接器通过.got.plt定位到该函数在共享库中的实际地址,并将其写入.got.plt对应条目,后续调用即可直接通过该地址快速跳转,实现 “延迟绑定” 的高效性。
前:

后:
|
|
对于变量,程序借助代码段与数据段的相对位置固定特性,通过相对寻址计算其正确地址;对于库函数的动态链接,则依赖过程链接表与全局偏移表的协同机制:
PLT初始存储一组通用代码,首次调用库函数时,PLT 通过 GOT 中预设的地址(指向 PLT 自身的下一条指令)触发动态链接器介入。
GOT初始记录的是 PLT 内部地址,动态链接器在首次调用时解析库函数的实际内存地址,并将其写入 GOT 对应条目。
后续调用中,PLT 直接通过更新后的 GOT 地址跳转至库函数实际内存位置,实现无需程序重启的动态地址绑定,确保程序运行时按需解析外部函数引用。
5.8 本章小结
本章首先介绍链接的核心概念与作用,通过具体命令演示如何将目标文件链接生成hello可执行文件。随后解析hello的 ELF 格式内容,借助edb工具观察其虚拟地址空间布局。最后以hello为例,深入分析重定位过程、程序执行流程及动态链接机制的实现细节。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
进程是计算机系统中程序运行的基本单元,作为操作系统进行资源分配和调度的独立实体,具备三大核心特性:
1.独立性:每个进程拥有专属的虚拟地址空间、数据栈及系统资源(如文件句柄、信号量),确保进程间数据互不干扰。
2.动态性:进程是程序的一次动态执行流程,历经创建、运行、阻塞、终止等生命周期阶段,而非静态的程序文件。
3.并发性:多个进程可通过时间片轮转、优先级调度等机制共享 CPU,在宏观上呈现 “同时运行” 的效果。
以hello程序为例,进程的核心作用体现在:
1.构建独立执行环境:为hello分配专属虚拟地址空间,隔离其代码(.text)、数据(.data)与其他进程。
2.资源管理与分配:管理hello使用的内存(通过 MMU 映射物理页)、I/O 资源(如标准输出设备)及 CPU 时间片。
3.隔离与安全保护:通过地址空间隔离和权限控制(如代码段只读),防止hello非法访问其他进程数据或系统关键资源。
4.支持并发执行:操作系统通过进程调度器(如 CFS 调度算法)将hello与其他进程交替运行,实现系统资源的高效利用。
6.2 简述壳Shell-bash的作用与处理流程
Shell(壳)是用户与操作系统内核交互的桥梁,通过命令行界面接收用户输入,解析并调度系统资源执行指令。以 Linux 系统中主流的 bash(Bourne Again Shell)为例,其核心作用与处理流程如下:
一、bash 的核心作用
1.命令解析与执行:识别用户输入的命令类型(内置命令 / 外部程序),并调用相应机制执行。
2.进程管理:通过fork/execve系统调用创建子进程,实现程序的独立运行与资源隔离。
3.环境配置:为程序设置运行环境(如环境变量、文件描述符),建立标准输入 / 输出 / 错误流(stdin/stdout/stderr)。
4.作业控制与信号处理:支持前台 / 后台任务调度(如&符号指定后台运行),响应键盘信号(如Ctrl+C终止进程、Ctrl+Z暂停进程)。
二、处理流程(以执行 hello 程序为例)
1. 命令解析与路径查找
输入读取:用户在终端输入./hello命令,bash 读取完整输入行。
语法解析:拆分命令名(./hello)与参数(如有),识别为外部程序执行请求。
路径解析:
若命令以./开头,直接查找当前目录下的hello可执行文件;
否则遍历PATH环境变量指定的目录(如/usr/bin:/bin),匹配文件名。
若未找到:输出bash: hello: 未找到命令错误。
2. 进程创建与程序加载
fork 子进程:
bash 调用fork()系统调用创建一个与自身完全相同的子进程。
execve 执行程序:
在子进程中调用execve(“./hello”, argv, envp),完成:
卸载子进程原有程序数据;
加载hello的 ELF 格式内容到虚拟内存(映射.text、.data等段);
初始化程序执行环境(传递参数argv、环境变量envp)。
3. 前台 / 后台任务调度
前台执行(默认模式):
bash 暂停自身执行,等待子进程hello运行结束(通过wait()系统调用),期间可接收键盘信号(如Ctrl+C直接终止hello)。
后台执行(命令末尾加&):
hello在后台运行,bash 无需等待其结束,立即返回新行提示符。用户可通过jobs命令查看后台任务,通过kill命令终止任务。
4. 环境设置与资源管理
环境变量传递:
将当前 bash 进程的环境变量(如PATH、USER)复制给hello进程,可通过printenv命令在hello中查看。
文件描述符继承:
hello默认继承 bash 的标准输入(键盘)、标准输出(终端)和标准错误流,实现printf输出到屏幕、scanf读取键盘输入。
5. 信号处理与程序终止
键盘信号响应:
Ctrl+C(SIGINT):bash 将信号传递给hello进程,默认终止程序。
Ctrl+Z(SIGTSTP):暂停hello进程,可通过fg命令恢复前台运行。
进程回收:
hello执行完毕后(正常返回或异常终止),bash 通过waitpid()系统调用回收子进程资源(释放 PID、内存页等),避免僵尸进程残留。
三、bash 与 hello 程序的交互逻辑
bash 通过上述流程实现了用户意图到系统内核调用的转换,为hello等程序提供了从启动到终止的完整生命周期管理,体现了操作系统对多任务、资源隔离与用户交互的底层支撑。
6.3 Hello的fork进程创建过程
fork系统调用是 Linux 创建进程的核心机制,用于从当前进程(父进程,如 bash)创建一个完全独立的子进程。以执行hello程序为例:
调用时机:
当用户在 bash 中输入./hello后,bash 调用fork()创建子进程,该子进程是父进程的精确副本(包含相同的虚拟地址空间、文件描述符等)。
父子进程分化:
父进程(bash):fork()返回子进程的 PID(如12345),继续执行后续逻辑(如等待子进程结束)。
子进程:fork()返回0,通过execve系统调用加载hello程序,替换自身原有内存空间。
写时复制(Copy-on-Write, COW):
父子进程初始共享物理内存页,仅在任意一方尝试修改数据时才复制页面,提升内存效率。
6.4 Hello的execve过程
execve系统调用用于在子进程中加载并执行hello的 ELF 可执行文件,彻底替换子进程的运行内容:
ELF 文件解析:
内核读取hello的 ELF 头,确认文件类型(可执行文件)、机器架构(X86-64)、入口地址(如0x4010f0)等信息。
解析程序头表(Program Header Table),将.text(代码段)、.data(数据段)等段映射到子进程的虚拟内存空间(如.text加载到0x400000,.data加载到0x601000)。
环境初始化:
传递命令行参数(如argv数组)和环境变量(如envp,包含PATH、USER等)到hello进程。
建立标准输入 / 输出 / 错误流(文件描述符0、1、2),默认指向终端设备。
程序执行切换:
内核清除子进程原有地址空间数据,建立新的页表映射,将程序计数器(PC)设置为hello的入口地址(_start函数),触发程序执行。
关键作用:
将静态的 ELF 文件转换为动态的运行进程,完成 “从文件到进程” 的质变。
确保hello进程与 bash 进程在内存空间上完全隔离,保障系统稳定性。
6.5 Hello的进程执行
(以下格式自行编排,编辑时删除)
结合进程上下文信息、进程时间片,阐述进程调度的过程,用户态与核心态转换等等。
6.6 hello的异常与信号处理
1.正常运行:在程序正常运行时,打印10次提示信息,以输入回车为标志结束程序,并回收进程。

2.运行时按下Ctrl + C:按下Ctrl + C,Shell进程收到SIGINT信号,Shell结束并回收hello进程。

3.运行时按下Ctrl + Z:按下Ctrl + Z,Shell进程收到SIGSTP信号,Shell显示屏幕提示信息并挂起hello进程。

4.回车及乱按:在程序执行过程中乱按所造成的输入均缓存到stdin,当getchar的时候读出一个’
’结尾的字串(作为一次输入),hello结束后,stdin中的其他字串会当做Shell的命令行输入。会发现,不停乱按并没有阻碍进程的执行,最终还是完整的输出了预期中的10次提示信息。

5.Ps:对hello进程的挂起可由ps和jobs命令查看,可以发现hello进程确实被挂起而非被回收,且其job代号为1。

Jobs↓

6.Pstree:输入pstree命令,可以将所有进程以树状图显示




7.Fg:使hello进程继续在前台执行

8.Kill:输入kill命令,则可以杀死指定(进程组的)进程:

hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。
6.7本章小结
本章以 hello 程序为例,探讨计算机系统中进程与 Shell(bash)的核心机制。首先介绍进程的概念与作用,包括独立性、动态性、并发性,以及为程序提供执行环境和资源管理。接着阐述 bash 的处理流程,如解析命令、fork 子进程、execve 加载程序、处理前台 / 后台任务及信号响应。然后分析 hello 的进程创建(fork)、启动(execve)和执行调度过程,涉及用户态 / 核心态切换与时间片分配。最后说明 hello 可能的异常(如非法访问)和信号处理(如 Ctrl+C/Z),通过ps,jobs、kill,pstree等命令演示进程控制,并探究指令的作用。
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
7.1.1逻辑地址(Logical Address)
在 hello 程序的编译阶段,源代码中的指令和数据使用逻辑地址,由段选择子(Segment Selector)和段内偏移量组成。例如,hello 的.text段中main函数的起始地址在逻辑地址空间中表示为段内偏移量(如0x1125),段选择子默认指向代码段描述符(段基址为0)。逻辑地址不直接对应物理内存,需通过段式管理转换为线性地址。
7.1.2线性地址(Linear Address)
在 x86-64 架构的平坦模式下,逻辑地址直接等于线性地址。例如,hello 的_start函数逻辑地址0x4010f0即线性地址,无需段基址调整。线性地址是虚拟地址空间中的连续地址,范围为0x0000000000000000~0x00007fffffffffff(64 位系统),但实际使用中由页式管理进一步映射到物理地址。
7.1.3虚拟地址(Virtual Address, VA)
操作系统为 hello 进程分配的虚拟地址空间中,各段加载到固定虚拟地址:
.text段:0x401000-0x402000(代码段,只读可执行);
.rodata段:0x402000-0x403000(只读数据段,存储字符串常量);
.data段:0x404048-0x404060(已初始化数据段,如全局变量stdin)。
虚拟地址通过页表映射到物理内存,实现进程间地址空间隔离。
7.1.4物理地址(Physical Address, PA)
虚拟地址通过页表转换为物理地址。例如,若0x401000对应的页表条目(PTE)中物理页帧号为0x1234,页大小为 4KB,则物理地址为0x1234000 + 0x1000 = 0x1235000。物理地址直接对应 DRAM 中的存储单元,由 MMU(内存管理单元)完成转换。
地址空间关系示意图:

7.2 Intel逻辑地址到线性地址的变换-段式管理
段式管理的核心机制
x86-64 架构采用平坦模式段式管理,简化地址转换:
段描述符表:全局描述符表(GDT)和局部描述符表(LDT)存储段的元信息(段基址、界限、权限)。hello 程序使用 GDT,其中代码段、数据段的段基址均为0,段界限为0xFFFFFFFFFFFFFFFF(覆盖整个虚拟地址空间)。
地址转换规则:逻辑地址的段内偏移量直接作为线性地址(因段基址 = 0)。
hello 程序的段式转换示例
hello 的printf@plt函数在目标文件中的逻辑地址为段选择子(0x08,指向代码段)+ 段内偏移(0x4010a0)。通过 GDT 查询代码段的段基址为0,因此线性地址为0x4010a0。
示意图:

7.3 Hello的线性地址到物理地址的变换-页式管理
页式管理的核心步骤
虚拟地址(VA)通过页表转换为物理地址(PA),关键步骤:
1.虚拟地址拆分:64 位 VA 分为虚拟页号(VPN)和页内偏移(VPO)(4KB 页大小时,低 12 位为 VPO,高位为 VPN)。
2.页表查询:
页表基址寄存器(CR3)指向一级页表(PML4)。
逐级查询页表(PML4→PDPT→PD→PT),获取物理页帧号(PPN)。
物理地址生成:PPN 左移 12 位(页大小 4KB)后与 VPO 拼接,得到 PA。
示意图:

7.4 TLB与四级页表支持下的VA到PA的变换
TLB 加速机制
TLB(转换后援缓冲器)缓存最近使用的页表条目,减少页表访问次数:
TLB 结构:存储VPN→PPN的映射,按组相联方式组织(如 4 路组相联)。
转换流程:
TLB 命中:直接用缓存的 PPN 生成 PA;
TLB 未命中:访问四级页表查询 PPN,更新 TLB 后生成 PA。
hello 程序的 TLB 应用场景
hello 首次调用printf时,VA0x4010a0的 TLB 未命中,需访问四级页表;后续调用时 TLB 命中,直接通过缓存的 PPN 生成 PA,加速地址转换。
示意图:

7.5 三级Cache支持下的物理内存访问
三级 Cache 的分层结构
现代 CPU 通过三级 Cache(L1/L2/L3)加速物理内存访问,层级关系:
L1 Cache:片内集成,速度最快(~1ns),容量小(32KB~64KB),分为指令 Cache(L1I)和数据 Cache(L1D)。
L2 Cache:片内共享,速度稍慢(~10ns),容量较大(256KB~4MB)。
L3 Cache:片内共享,速度更慢(~30ns),容量最大(8MB~32MB)。
Cache 映射方式(以组相联为例)
物理地址分为标记(Tag)、组索引(Index)、块偏移(Offset):
组索引确定 Cache 组(如 16 组);
标记匹配确定缓存行;
块偏移定位具体数据。
hello 程序的 Cache 访问示例
hello 执行printf(“Hello
“)时,访问.rodata段的字符串常量(物理地址0x567a000):
L1D Cache 查询:组索引匹配,标记匹配→命中,直接读取数据;
若 L1 未命中,查询 L2 Cache→若命中则加载到 L1;
若 L2 未命中,查询 L3 Cache→若命中则加载到 L2/L1;
若 L3 未命中,访问主存→加载数据到 L3/L2/L1。
示意图:

7.6 hello进程fork时的内存映射
COW 的核心逻辑
fork创建子进程时,父子共享物理页面,仅在写操作时复制:
1.初始共享:子进程复制父进程的页表,物理页面标记为只读。
2.写操作触发复制:
父 / 子进程尝试修改页面→MMU 检测到只读标记→触发页错误异常;
内核分配新物理页,复制原页面内容,更新子进程页表为可写,原父进程页表保持只读(或恢复可写)。
hello 程序的 fork 场景
hello 执行fork()后:
子进程与父进程共享.text段(代码段,只读)和未修改的.data段(数据段,初始只读);
若子进程修改全局变量count(位于.data段),触发 COW,内核为count所在页面分配新物理页,子进程页表指向新页,父进程仍指向旧页。
示意图:

7.7 hello进程execve时的内存映射
execve 的内存重映射流程
execve加载 hello 程序时,内核重新构建虚拟地址空间:
1.释放旧区域:删除原进程的用户空间区域(如旧代码段、堆、栈)。
2.映射新区域:
代码段(.text):映射 hello 的 ELF 代码段到0x401000-0x402000,标记为只读可执行;
数据段(.data/.bss):映射初始化数据(.data)到0x404048-0x404060,未初始化数据(.bss)通过匿名映射分配零页;
共享库(如libc.so):映射到0x7ffff7a00000-0x7ffff7c00000,供 hello 和其他进程共享;
堆(Heap):从0x605000开始向上扩展,用于动态内存分配;
栈(Stack):从0x7fffffffe000向下扩展,用于函数调用和局部变量。
示意图:

7.8 缺页故障与缺页中断处理
缺页中断的触发与处理流程
当访问无效或未加载的虚拟页面时,触发缺页中断,内核处理步骤:
1.异常检测:MMU 发现页表条目(PTE)的有效位(Present Bit)为0,触发#PF异常(页错误)。
2.合法性检查:
虚拟地址是否在进程的虚拟地址空间内?若否,终止进程(段错误SIGSEGV);
访问权限是否匹配?如写只读页面,触发保护异常(SIGBUS)。
3.页面加载:
若页面来自文件(如.text段),从 ELF 文件加载到物理内存;
若为匿名页面(如堆、栈),分配物理页并清零;
更新页表的有效位(置1)和权限位。
示意图:

7.9动态存储分配管理
7.9.1 分配与释放的基本模式
显式分配:
通过malloc/calloc/realloc主动申请堆内存(如char *buf = malloc(1024);)。
隐式分配:
库函数内部调用malloc(如printf格式化长字符串时自动分配临时缓冲区),对用户透明。
释放机制:
需通过free手动释放,否则导致内存泄漏;隐式分配的内存由库函数内部释放(如printf的临时缓冲区随函数返回自动释放)。
7.9.2 内存管理策略与碎片优化
|
策略 |
核心逻辑 |
优缺点 |
hello 场景 |
|
首次适应(First-Fit) |
从堆起始搜索首个足够大的空闲块分配,分割剩余空间。 |
速度快,易产生外部碎片(小块分散在低地址)。 |
printf快速分配小缓冲区(如首次调用时取低地址空闲块)。 |
|
最佳适应(Best-Fit) |
选择最小的足够大空闲块,减少空间浪费。 |
搜索开销大,可能产生大量微小碎片。 |
适用于精准分配小字符串(如”Hi”)。 |
|
最差适应(Worst-Fit) |
优先分割最大空闲块,减少碎片数量。 |
消耗大块内存,可能导致后续大请求失败。 |
处理printf的大缓冲区需求(如 10KB 字符串)。 |
碎片处理方案:
1.合并与分割:释放时合并相邻空闲块,分配时切割大块为 “需求 + 剩余” 两部分;
2.内存池技术:预分配大块内存(如 4KB),切成固定大小单元(如 16/32 字节),供printf等高频小分配快速取用,避免频繁系统调用。
7.9.3 常见问题与优化方向
1.内存泄漏
风险:printf内部分配的缓冲区若未释放(如库函数异常退出),需依赖glibc内部机制回收。
2.性能瓶颈
优化:小对象通过内存池预分配(如printf的短字符串复用池内单元),减少malloc调用开销。
3.线程安全
多线程下malloc通过加锁(如glibc的arena机制)或无锁设计(如jemalloc)保证安全,避免竞态条件
7.10本章小结
本章围绕 hello 程序的存储管理,揭示计算机系统如何通过分层抽象与动态策略实现高效内存管理。首先解析四层地址体系:逻辑地址(段内偏移)经平坦模式段式管理转为线性地址(x86-64 中与逻辑地址一致),再通过四级页表和 TLB 转换为物理地址,三级 Cache 加速物理访问。进程创建(fork)时利用写时复制(COW)共享页面,仅在修改时分配新物理页;程序加载(execve)则重新映射 ELF 段与共享库,构建独立虚拟地址空间。
缺页故障处理机制确保动态加载未命中页面,通过合法性检查、物理页分配及页表更新维持程序连续运行。动态内存管理中,printf 隐式调用 malloc 采用首次适应算法分配堆内存,释放时合并相邻块减少碎片,小对象通过内存池预分配优化性能,平衡速度与空间利用率。
本章通过 hello 程序全流程,展现存储管理如何通过段页式地址变换、Cache 加速、进程内存隔离及动态分配策略,实现高效可靠的内存资源管理,体现系统设计中性能、安全与通用性的精妙平衡。
(第7章 2分)
结论
用计算机系统的语言,逐条总结hello所经历的过程。
你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。
Hello 程序生命周期
1.预处理:展开头文件、宏替换,生成纯文本hello.i,消除编译依赖。
2.编译:将hello.i转为汇编hello.s,完成语法语义分析与寄存器分配。
3.汇编:生成可重定位目标文件hello.o,标记未定义符号待链接。
4.链接:解析符号、合并段、分配虚拟地址,生成可执行文件hello,设置入口点_start。
5.进程加载:fork创建子进程,execve加载hello,映射 ELF 段到虚拟地址空间,通过 TLB / 页表加速地址转换。
内存:动态内存分配管理堆空间。
二、创新方向
定制化程序与硬件配合
针对像 “hello” 这样常打印文字的程序,设计专门的硬件指令和软件配合,让电脑更快执行这类任务,同时减少耗电。
内存智能分配
根据hello内存访问模式(如.rodata只读、.data少量写),动态调整内存策略。
人工智能优化程序
用人工智能分析 “hello” 的运行情况,自动调整程序的编译和运行方式,让它跑得更快、更少出错。
程序安全保护
给 “hello” 加一层安全保护,限制hello的代码与数据访问权限,提升安全防护能力防止被攻击,且不影响正常运行。
跨电脑快速适配。
让 “hello” 这样的程序能轻松在不同类型的电脑(如 X86、ARM 架构)上运行,不需要重新编写。
二、启示与感悟
从hello.c到进程的完整生命周期,展现了计算机系统如何通过 “编译 – 加载 – 执行 – 回收” 的精密协作,将人类可读的符号代码转化为电子信号的有序流动。每一个阶段都蕴含着对 “正确性、高效性、安全性” 的极致追求。未来的系统设计需在异构计算(CPU+GPU+NPU)、边缘计算(低延迟 + 能耗优化)等场景中,进一步融合硬件特性与软件抽象,通过跨层协同实现 “精准优化”,让计算机系统在保持兼容性的同时,持续突破性能与安全的边界。
对于我个人而言,我是第一次进行规模如此庞大的作业,这本身就是很大的挑战,同时,这门课程的难度很大,平时完成各次实验我都会有很多问题,这个大作业的完成过程更是复杂,百度,csdn,问ai,请教同学……我想尽各种办法来完成这个作业。当然,收获更是喜人:通过完成这次作业,我加深了对程序的处理、运行的理解,比如第六章部分,在尝试各个指令的作用时,有一种探索的乐趣。想起大作业要求ppt说的,这是一场旅途,一个不断学习不断增长见识的旅途。这让我想起了著名的“愚人之旅”,我作为这场旅途的“愚人”,从充满新奇的探索,到坎坷、收获、满足、失落……学习之旅伴随着心路历程,最终到达这个圆满的“终点”。当然,说是终点,不如说是一个阶段性的停歇,因为以后对于计算机知识的学习不会停止,对计算机系统的知识应用更不会停止。“会者定离,一期一祈”,计算机系统的学习暂告一段落,但想来,在以后的学习中会有更多的邂逅。
(结论0分,缺失 -1分,根据内容酌情加分)
附件
hello.c:老师提供的c语言源程序文件;
hello.i:将hello.c预处理得到的预处理文件;
hello.s:将hello.i编译后得到的汇编文件;
hello.o:将hello.s进行汇编后得到的二进制目标文件;
hello:将hello.o链接后得到的可执行文件;
hello_o.asm: 将hello.o反汇编得到的反汇编文件;
hello.asm:将hello反汇编得到的反汇编文件
(附件0分,缺失 -1分)
参考文献
为完成本次大作业你翻阅的书籍与网站等
[1] Bryant, R. E., & O'Hallaron, D. R. (2016). Computer Systems: A Programmer's
Perspective (3rd ed.). Pearson.
[2] askunix_hjh. 什么是进程? ( 进程的详细概念 )[EB/OL].CSDN,2017-12-09. https://blog.csdn.net/m0_37925202/article/details/78759408.
西邮小菜机. 操作系统的内存管理——页式、段式管理、段页式管理[EB/OL].CSDN,2022-05-08.w-CSDN
博客eixin_46199479/article/details/123438544.
[4] 不会写代码的丝丽. ELF格式解读-(1) elf头部与节头[EB/OL].CSDN, 2022-04-21. https://blog.csdn.net/qfanmingyiq/article/details/124295287.
[5] Linux 下C编程:Linux 操作系统和C语言(详解)_linux c语言-CSDN博客
[6] Ubuntu 相关: http://forum.ubuntu.org.cn/
[7] C汇编Linux手册: http://docs.huihoo.com/c/linux-c-programming/
[8] gcc 使用: https://blog.csdn.net/weixin_50697073/article/details/123759516
(参考文献0分,缺失 -1分)


















暂无评论内容