本文以"程序人生-Hello's P2P"为题,通过一个简单的Hello World程序的生命周期,全面剖析了计算机系统从源代码到进程执行的完整过程。研究采用理论分析与实践验证相结合的方法,在Ubuntu系统环境下跟踪了hello程序的预处理、编译、汇编、链接、加载和执行全过程,深入探讨了计算机系统各核心组件的工作原理和交互机制1。
研究重点包括:
- 程序转换过程:详细解析了从hello.c源代码到可执行文件的转换链,包括预处理阶段的宏展开和头文件包含、编译阶段的语法分析和代码生成、汇编阶段的机器指令转换以及链接阶段的符号解析和地址重定位1。
- 进程管理机制:分析了hello程序作为进程在操作系统中的创建(fork)、加载(execve)、执行和终止过程,探讨了shell的作用、进程地址空间管理以及进程间通信机制1。
- 存储体系结构:研究了hello程序运行过程中涉及的存储器层次结构,包括逻辑地址到物理地址的转换机制、TLB与多级页表的工作流程、三级缓存体系对程序性能的影响,以及动态内存分配管理策略1。
- 异常与信号处理:探讨了hello程序执行过程中可能遇到的各类异常和信号,以及操作系统的相应处理机制1。
本研究的创新点在于通过一个简单的Hello World程序,系统性地揭示了现代计算机系统的完整工作流程和内部机制。研究结果不仅有助于深入理解计算机系统各组件的工作原理,也为程序性能优化、系统级调试和安全分析提供了理论基础和实践指导。论文最后总结了hello程序的生命周期全过程,并提出了关于计算机系统设计的思考与感悟1。
关键词:计算机系统;程序生命周期;进程管理;存储体系;链接加载
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
目 录
第1章 概述................................................................................... - 4 -
1.1 Hello简介............................................................................ - 4 -
1.2 环境与工具........................................................................... - 4 -
1.3 中间结果............................................................................... - 4 -
1.4 本章小结............................................................................... - 4 -
第2章 预处理............................................................................... - 5 -
2.1 预处理的概念与作用........................................................... - 5 -
2.2在Ubuntu下预处理的命令................................................ - 5 -
2.3 Hello的预处理结果解析.................................................... - 5 -
2.4 本章小结............................................................................... - 5 -
第3章 编译................................................................................... - 6 -
3.1 编译的概念与作用............................................................... - 6 -
3.2 在Ubuntu下编译的命令.................................................... - 6 -
3.3 Hello的编译结果解析........................................................ - 6 -
3.4 本章小结............................................................................... - 6 -
第4章 汇编................................................................................... - 7 -
4.1 汇编的概念与作用............................................................... - 7 -
4.2 在Ubuntu下汇编的命令.................................................... - 7 -
4.3 可重定位目标elf格式........................................................ - 7 -
4.4 Hello.o的结果解析............................................................. - 7 -
4.5 本章小结............................................................................... - 7 -
第5章 链接................................................................................... - 8 -
5.1 链接的概念与作用............................................................... - 8 -
5.2 在Ubuntu下链接的命令.................................................... - 8 -
5.3 可执行目标文件hello的格式........................................... - 8 -
5.4 hello的虚拟地址空间......................................................... - 8 -
5.5 链接的重定位过程分析....................................................... - 8 -
5.6 hello的执行流程................................................................. - 8 -
5.7 Hello的动态链接分析........................................................ - 8 -
5.8 本章小结............................................................................... - 9 -
第6章 hello进程管理.......................................................... - 10 -
6.1 进程的概念与作用............................................................. - 10 -
6.2 简述壳Shell-bash的作用与处理流程........................... - 10 -
6.3 Hello的fork进程创建过程............................................ - 10 -
6.4 Hello的execve过程........................................................ - 10 -
6.5 Hello的进程执行.............................................................. - 10 -
6.6 hello的异常与信号处理................................................... - 10 -
6.7本章小结.............................................................................. - 10 -
第7章 hello的存储管理...................................................... - 11 -
7.1 hello的存储器地址空间................................................... - 11 -
7.2 Intel逻辑地址到线性地址的变换-段式管理................... - 11 -
7.3 Hello的线性地址到物理地址的变换-页式管理............. - 11 -
7.4 TLB与四级页表支持下的VA到PA的变换.................... - 11 -
7.5 三级Cache支持下的物理内存访问................................ - 11 -
7.6 hello进程fork时的内存映射......................................... - 11 -
7.7 hello进程execve时的内存映射..................................... - 11 -
7.8 缺页故障与缺页中断处理................................................. - 11 -
7.9动态存储分配管理.............................................................. - 11 -
7.10本章小结............................................................................ - 12 -
第8章 hello的IO管理....................................................... - 13 -
8.1 Linux的IO设备管理方法................................................. - 13 -
8.2 简述Unix IO接口及其函数.............................................. - 13 -
8.3 printf的实现分析.............................................................. - 13 -
8.4 getchar的实现分析.......................................................... - 13 -
8.5本章小结.............................................................................. - 13 -
参考文献....................................................................................... - 16 -
第1章 概述
1.1 Hello简介
Hello程序经历了从程序(Program)到进程(Process)的完整生命周期(P2P),以及从零(0)开始运行到零(0)返回的完整执行过程(020)。
P2P过程:
- 预处理(Preprocessing):hello.c经过预处理器展开宏和头文件,生成hello.i
- 编译(Compilation):编译器将hello.i翻译为汇编代码hello.s
- 汇编(Assembly):汇编器将hello.s转换为机器指令,生成可重定位目标文件hello.o
- 链接(Linking):链接器合并hello.o与库文件,生成可执行文件hello
- 进程创建(Process Creation):shell通过fork创建子进程,execve加载hello程序
020过程:
- 零(0):程序从无到有被创建
- 执行:CPU运行程序指令,操作系统管理资源
- 零(0):程序执行完毕返回0,进程终止
1.2 环境与工具
硬件环境:
- CPU:Intel Core i7-10750H 2.60GHz
- 内存:16GB DDR4
- 存储:512GB SSD
软件环境:
- 操作系统:Ubuntu 20.04 LTS
- 编译器:gcc 9.4.0
- 调试工具:gdb 9.2
开发工具:
- 编辑器:VS Code 1.68.1
- 版本控制:Git 2.25.1
- 文档工具:Markdown
1.3 中间结果
| 作用 |
|
| hello.i |
预处理后的源代码,包含所有宏展开和头文件内容 |
| hello.s |
编译生成的汇编代码,展示高级语言到低级语言的转换 |
| hello.o |
可重定位目标文件,包含机器指令但未完成地址解析 |
| hello |
最终可执行文件,包含完整的程序代码和数据 |
| hello_elf.txt |
ELF格式分析结果,展示可执行文件结构 |
1.4 本章小结
本章概述了hello程序的生命周期(P2P)和执行过程(020),介绍了研究采用的硬件和软件环境,列出了研究过程中生成的关键中间文件。通过这些基础准备工作,为后续章节深入分析程序的编译链接过程、进程管理机制和存储体系结构奠定了基础。从下一章开始,将逐步详细剖析hello程序在计算机系统中的完整执行流程。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
预处理(Preprocessing)是指编译器在编译程序之前对源代码进行的一系列处理步骤。预处理的主要作用包括:
- 宏替换:将代码中的宏定义替换为实际的代码片段。
- 文件包含:将#include指令指定的头文件内容插入到当前文件中。
- 条件编译:根据#if、#ifdef、#ifndef等指令,选择性地编译代码。
- 删除注释:移除代码中的注释内容,简化后续编译过程。
- 处理特殊符号:例如将##运算符用于连接两个符号。
预处理的目的是将源代码转换为一个更简洁、更统一的中间形式,为编译器的后续阶段(如语法分析和代码生成)做好准备。
2.2在Ubuntu下预处理的命令
Cpp hello.c -o hello.i

2.3 Hello的预处理结果解析
1. 文件结构
- 头文件插入:.i 文件中包含了多个头文件的内容,例如 <stdio.h>、<unistd.h>、<stdlib.h> 等。这些头文件提供了标准库函数的声明和定义。
- 宏展开:所有宏定义已经被展开,例如 NULL、size_t 等。
- 原始代码:文件的最后部分是原始的 hello.c 源代码,经过预处理后插入到文件中。
2. 头文件内容
- <stdio.h>:提供了标准输入输出函数的声明,例如 printf、scanf、fprintf 等。
- <unistd.h>:提供了与系统调用相关的函数声明,例如 sleep、getpid 等。
- <stdlib.h>:提供了标准库函数的声明,例如 exit、malloc、free 等。
3. 预处理指令
- # 行号指示符:例如 # 1 "hello.c",这些指示符用于标记源代码的行号和文件名,帮助编译器在后续阶段定位错误。
- #include 指令:所有 #include 指令已经被展开,头文件的内容直接插入到 .i 文件中。
2.4 本章小结
本章通过理论与实践相结合的方式,深入探讨了C语言预处理的各个方面。预处理是编译过程中的一个重要阶段,它为后续的编译步骤奠定了基础。通过预处理,源代码变得更加简洁和统一,同时去除了不必要的注释和冗余信息。在Ubuntu环境下,我们展示了如何使用cpp命令或gcc -E选项进行预处理,并详细解析了一个简单的C程序(hello.c)的预处理结果。这些内容不仅帮助读者理解预处理的作用和机制,还展示了如何通过预处理文件(.i文件)来分析和调试代码。预处理是C语言编程中不可或缺的一部分,掌握其原理和应用对于提高编程效率和代码质量具有重要意义。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
编译过程
从 .i 文件到 .s 文件的编译过程主要涉及编译器的 编译阶段,具体步骤如下:
a. 语法分析(Syntax Analysis)
- 编译器会解析 .i 文件中的代码,检查语法是否正确。
- 它会将代码分解成一系列的语法单元(如表达式、语句、函数定义等)。
- 语法分析的目的是确保代码符合语言的语法规则,并构建语法树(Syntax Tree)。
b. 语义分析(Semantic Analysis)
- 在语法分析的基础上,编译器会进一步检查代码的语义是否正确。
- 例如,检查变量是否声明、类型是否匹配、函数调用是否正确等。
- 语义分析阶段会将语法树转换为中间表示(Intermediate Representation,IR),例如三地址代码(Three-Address Code)。
c. 代码生成(Code Generation)
- 编译器将中间表示转换为汇编语言代码。
- 这一步会根据目标硬件平台的指令集,将高级语言的逻辑转换为低级的汇编指令。
- 生成的汇编代码保存在 .s 文件中。
作用
a. 语言转换
- 将高级语言(如 C/C++)转换为低级的汇编语言。
- 高级语言更接近人类的思维方式,而汇编语言更接近硬件的指令集。这种转换是程序能够运行在硬件上的必要步骤。
b. 错误检测
- 在编译过程中,编译器会检测语法错误和语义错误。
- 例如,未声明的变量、类型不匹配、语法错误等。
- 及时发现并修复这些错误可以提高代码的质量和可维护性。
c. 优化
- 编译器在代码生成阶段会对代码进行优化。
- 例如,消除冗余代码、调整代码顺序以提高性能等。
- 优化后的代码可以提高程序的运行效率。
d. 硬件适配
- 不同的硬件平台有不同的指令集。
- 编译器会根据目标硬件生成对应的汇编代码,确保程序能够在特定的硬件上运行。
3.2 在Ubuntu下编译的命令
gcc -S hello.i -o hello.s

3.3 Hello的编译结果解析
3.3.1 常量和变量
- 常量:
- .LC0 和 .LC1 是字符串常量,分别用于错误提示和 printf 的格式化字符串。
- 在汇编代码中,字符串常量存储在 .rodata 段中。
- 变量:
- 局部变量存储在栈上,例如 i 存储在 -4(%rbp)。
- argc 和 argv 作为函数参数传递,存储在 -20(%rbp) 和 -32(%rbp)。
3.3.2 赋值操作
- 赋值操作:
- movl $0, -4(%rbp):将局部变量 i 初始化为 0。
- movl %eax, -4(%rbp):将寄存器中的值赋给局部变量 i。
3.3.3 类型转换
- 类型转换:
- movl %eax, %edi:将 int 类型的值从 %eax 移动到 %edi。
- movq (%rax), %rcx:将 char* 类型的值从内存加载到寄存器。
3.3.4 算术操作
- 算术操作:
- addl $1, -4(%rbp):将局部变量 i 加 1。
- cmpl $5, -20(%rbp):比较 argc 是否等于 5。
3.3.5 逻辑/位操作
- 逻辑操作:
- je .L2:如果条件满足(argc == 5),跳转到 .L2。
- jle .L4:如果条件满足(i <= 9),跳转到 .L4。
3.3.6 关系操作
- 关系操作:
- cmpl $5, -20(%rbp):比较 argc 是否等于 5。
- cmpl $9, -4(%rbp):比较 i 是否小于等于 9。
3.3.7 数组/指针操作
- 数组/指针操作:
- movq -32(%rbp), %rax:将 argv 的地址加载到寄存器。
- movq (%rax), %rcx:通过指针访问数组元素。
3.3.8 控制转移
- 控制转移:
- jmp .L3:无条件跳转到 .L3。
- je .L2:条件跳转到 .L2。
- jle .L4:条件跳转到 .L4。
3.3.9 函数操作
- 函数操作:
- call printf@PLT:调用 printf 函数。
- call atoi@PLT:调用 atoi 函数。
- call sleep@PLT:调用 sleep 函数。
3.3.10 其他操作
- 其他操作:
- leave 和 ret:恢复栈帧并返回。
- .cfi 指令:用于异常处理和栈回溯。
3.4 本章小结
本章通过理论与实践相结合的方式,详细介绍了编译的概念、作用以及在Ubuntu环境下将C语言源代码编译为汇编代码的过程。通过对 hello.c 程序的编译结果进行解析,我们深入理解了编译器如何处理各种数据类型和操作,包括常量、变量、赋值操作、类型转换、算术操作、逻辑/位操作、关系操作、数组/指针操作、控制转移和函数操作。这些内容不仅帮助读者理解编译器的工作原理,还展示了如何通过汇编代码来分析和调试程序。掌握这些知识对于提高编程效率和代码质量具有重要意义。(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
- 输入:汇编器的输入是汇编语言源文件,通常以 .s 或 .asm 为扩展名。
- 输出:汇编器的输出是目标文件(Object File),它包含机器代码、符号表、重定位信息和调试信息等。
- 主要步骤:
- 语法分析:解析汇编语言指令,检查语法是否正确。
转载自CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/2302_78905112/article/details/148195986



