哈工大计统大作业-程序人生

摘 要

本项目以"程序人生-Hello's P2P"为核心,通过编写、预处理、编译、汇编、链接及运行一个简单的Hello程序,系统探讨了计算机系统中程序从代码到进程的全生命周期。实验基于Ubuntu环境,使用GCC工具链完成代码转换,分析了预处理展开头文件、编译生成汇编指令、汇编生成可重定位目标文件、链接生成可执行文件等关键步骤的底层机制。结合进程管理、存储管理和IO管理,深入揭示了操作系统对程序执行的资源分配、地址转换、异常处理及动态链接的支持。项目通过理论与实践结合,完整呈现了程序在编译系统、操作系统和硬件协同下的运行流程,验证了计算机系统分层抽象与模块化设计的高效性,为深入理解系统级编程与优化提供了重要参考。

**关键词:**预处理;编译;链接;进程管理;存储管理;动态链接;IO管理

目 录

[第1章 概述............................................................................................................. - 4 -](#第1章 概述............................................................................................................. - 4 -)

[1.1 Hello简介...................................................................................................... - 4 -](#1.1 Hello简介...................................................................................................... - 4 -)

[1.2 环境与工具..................................................................................................... - 4 -](#1.2 环境与工具..................................................................................................... - 4 -)

[1.3 中间结果......................................................................................................... - 4 -](#1.3 中间结果......................................................................................................... - 4 -)

[1.4 本章小结......................................................................................................... - 4 -](#1.4 本章小结......................................................................................................... - 4 -)

[第2章 预处理......................................................................................................... - 5 -](#第2章 预处理......................................................................................................... - 5 -)

[2.1 预处理的概念与作用..................................................................................... - 5 -](#2.1 预处理的概念与作用..................................................................................... - 5 -)

[2.2在Ubuntu下预处理的命令.......................................................................... - 5 -](#2.2在Ubuntu下预处理的命令.......................................................................... - 5 -)

[2.3 Hello的预处理结果解析.............................................................................. - 5 -](#2.3 Hello的预处理结果解析.............................................................................. - 5 -)

[2.4 本章小结......................................................................................................... - 5 -](#2.4 本章小结......................................................................................................... - 5 -)

[第3章 编译............................................................................................................. - 6 -](#第3章 编译............................................................................................................. - 6 -)

[3.1 编译的概念与作用......................................................................................... - 6 -](#3.1 编译的概念与作用......................................................................................... - 6 -)

[3.2 在Ubuntu下编译的命令............................................................................. - 6 -](#3.2 在Ubuntu下编译的命令............................................................................. - 6 -)

[3.3 Hello的编译结果解析.................................................................................. - 6 -](#3.3 Hello的编译结果解析.................................................................................. - 6 -)

[3.4 本章小结......................................................................................................... - 6 -](#3.4 本章小结......................................................................................................... - 6 -)

[第4章 汇编............................................................................................................. - 7 -](#第4章 汇编............................................................................................................. - 7 -)

[4.1 汇编的概念与作用......................................................................................... - 7 -](#4.1 汇编的概念与作用......................................................................................... - 7 -)

[4.2 在Ubuntu下汇编的命令............................................................................. - 7 -](#4.2 在Ubuntu下汇编的命令............................................................................. - 7 -)

[4.3 可重定位目标elf格式................................................................................. - 7 -](#4.3 可重定位目标elf格式................................................................................. - 7 -)

[4.4 Hello.o的结果解析...................................................................................... - 7 -](#4.4 Hello.o的结果解析...................................................................................... - 7 -)

[4.5 本章小结......................................................................................................... - 7 -](#4.5 本章小结......................................................................................................... - 7 -)

[第5章 链接............................................................................................................. - 8 -](#第5章 链接............................................................................................................. - 8 -)

[5.1 链接的概念与作用......................................................................................... - 8 -](#5.1 链接的概念与作用......................................................................................... - 8 -)

[5.2 在Ubuntu下链接的命令............................................................................. - 8 -](#5.2 在Ubuntu下链接的命令............................................................................. - 8 -)

[5.3 可执行目标文件hello的格式.................................................................... - 8 -](#5.3 可执行目标文件hello的格式.................................................................... - 8 -)

[5.4 hello的虚拟地址空间.................................................................................. - 8 -](#5.4 hello的虚拟地址空间.................................................................................. - 8 -)

[5.5 链接的重定位过程分析................................................................................. - 8 -](#5.5 链接的重定位过程分析................................................................................. - 8 -)

[5.6 hello的执行流程.......................................................................................... - 8 -](#5.6 hello的执行流程.......................................................................................... - 8 -)

[5.7 Hello的动态链接分析.................................................................................. - 8 -](#5.7 Hello的动态链接分析.................................................................................. - 8 -)

[5.8 本章小结......................................................................................................... - 9 -](#5.8 本章小结......................................................................................................... - 9 -)

[第6章 hello进程管理................................................................................... - 10 -](#第6章 hello进程管理................................................................................... - 10 -)

[6.1 进程的概念与作用....................................................................................... - 10 -](#6.1 进程的概念与作用....................................................................................... - 10 -)

[6.2 简述壳Shell-bash的作用与处理流程..................................................... - 10 -](#6.2 简述壳Shell-bash的作用与处理流程..................................................... - 10 -)

[6.3 Hello的fork进程创建过程..................................................................... - 10 -](#6.3 Hello的fork进程创建过程..................................................................... - 10 -)

[6.4 Hello的execve过程................................................................................. - 10 -](#6.4 Hello的execve过程................................................................................. - 10 -)

[6.5 Hello的进程执行........................................................................................ - 10 -](#6.5 Hello的进程执行........................................................................................ - 10 -)

[6.6 hello的异常与信号处理............................................................................ - 10 -](#6.6 hello的异常与信号处理............................................................................ - 10 -)

[6.7本章小结....................................................................................................... - 10 -](#6.7本章小结....................................................................................................... - 10 -)

[第7章 hello的存储管理................................................................................ - 11 -](#第7章 hello的存储管理................................................................................ - 11 -)

[7.1 hello的存储器地址空间............................................................................ - 11 -](#7.1 hello的存储器地址空间............................................................................ - 11 -)

[7.2 Intel逻辑地址到线性地址的变换-段式管理............................................ - 11 -](#7.2 Intel逻辑地址到线性地址的变换-段式管理............................................ - 11 -)

[7.3 Hello的线性地址到物理地址的变换-页式管理....................................... - 11 -](#7.3 Hello的线性地址到物理地址的变换-页式管理....................................... - 11 -)

[7.4 TLB与四级页表支持下的VA到PA的变换............................................. - 11 -](#7.4 TLB与四级页表支持下的VA到PA的变换............................................. - 11 -)

[7.5 三级Cache支持下的物理内存访问.......................................................... - 11 -](#7.5 三级Cache支持下的物理内存访问.......................................................... - 11 -)

[7.6 hello进程fork时的内存映射.................................................................. - 11 -](#7.6 hello进程fork时的内存映射.................................................................. - 11 -)

[7.7 hello进程execve时的内存映射.............................................................. - 11 -](#7.7 hello进程execve时的内存映射.............................................................. - 11 -)

[7.8 缺页故障与缺页中断处理........................................................................... - 11 -](#7.8 缺页故障与缺页中断处理........................................................................... - 11 -)

[7.9动态存储分配管理....................................................................................... - 11 -](#7.9动态存储分配管理....................................................................................... - 11 -)

[7.10本章小结..................................................................................................... - 12 -](#7.10本章小结..................................................................................................... - 12 -)

[第8章 hello的IO管理................................................................................. - 13 -](#第8章 hello的IO管理................................................................................. - 13 -)

[8.1 Linux的IO设备管理方法.......................................................................... - 13 -](#8.1 Linux的IO设备管理方法.......................................................................... - 13 -)

[8.2 简述Unix IO接口及其函数....................................................................... - 13 -](#8.2 简述Unix IO接口及其函数....................................................................... - 13 -)

[8.3 printf的实现分析........................................................................................ - 13 -](#8.3 printf的实现分析........................................................................................ - 13 -)

[8.4 getchar的实现分析.................................................................................... - 13 -](#8.4 getchar的实现分析.................................................................................... - 13 -)

[8.5本章小结....................................................................................................... - 13 -](#8.5本章小结....................................................................................................... - 13 -)

[结论......................................................................................................................... - 14 -](#结论......................................................................................................................... - 14 -)

[附件......................................................................................................................... - 15 -](#附件......................................................................................................................... - 15 -)

[参考文献................................................................................................................. - 16 -](#参考文献................................................................................................................. - 16 -)

第1章 概述

1.1 Hello简介

Program:在VSCode中编写hello.c

预处理:gcc -E hello.c -o hello.i → 展开头文件

编译:gcc -S hello.i -o hello.s → 生成x86_64汇编

汇编:gcc -c hello.s -o hello.o → 生成ELF可重定位文件

链接:gcc hello.o -o hello → 动态链接glibc 2.38

Process:Shell通过fork()创建子进程,execve()加载hello。

OS分配虚拟内存,CPU执行指令后调用exit_group(0)终止。

020(From Zero to Zero):0→1:文本文件→可执行文件

1→0:进程终止后,OS回收页表、文件描述符等资源。

1.2 环境与工具

硬件 x86_64 CPU, DELL服务器(Ubuntu)

内核 Linux 6.8.0-54-generic

工具链 GCC 13.3.0(-Og)、GNU Binutils 2.41、glibc 2.38

调试 GDB 14.2、strace 6.8、objdump 2.41

编辑器 VSCode

1.3 中间结果

hello.i # 预处理后代码

hello.s # 汇编代码

hello.o # ELF文件

hello # 动态链接可执行文件

1.4 本章小结

本章简述了Hello程序的P2P(从代码到进程)和020(从无到无)生命周期,列出了实验环境和生成的中间文件。后续章节将深入分析预处理、编译、进程运行等细节。

第2章 预处理

2.1 预处理的概念与作用

预处理(Preprocessing)是C程序编译的第一步,由预处理器(cpp)执行,主要完成以下任务:

宏展开:替换#define定义的宏

头文件包含:递归展开#include指令,插入头文件内容

条件编译:处理#if、#ifdef等指令,决定是否包含代码块

删除注释:移除所有/* ... */和//注释

添加行标记:插入#line指令,便于调试时定位源码位置。

作用:

使代码可移植(如通过条件编译适配不同平台)

减少重复代码(通过头文件和宏)

生成"纯净"的C代码(不含预处理指令),供编译器处理

2.2在Ubuntu下预处理的命令

-m64:64位 -E:仅预处理 -o hello.i:输出到hello.i文件。

2.3 Hello的预处理结果解析

头文件展开:

原始代码中的#include被替换为头文件内容:

stdio.h:插入printf、exit等函数声明

unistd.h:插入sleep、getchar等系统调用声明

stdlib.h:插入atoi的函数声明

注释删除:

源码开头的注释被完全移除

行标记:

插入#line指令,标识原始代码位置

2.4 本章小结

本章通过gcc -E命令对hello.c进行预处理,生成hello.i文件,并分析其内容,进行预处理核心任务:头文件展开(插入stdio.h等)、删除注释、添加行标记。原始代码从24行扩展为3180行(主要因头文件插入),保留了所有函数调用(printf、sleep)的声明。

第3章 编译

3.1 编译的概念与作用

概念:

编译(Compilation)指将预处理后的C代码(.i)转换为汇编代码(.s)的过程,由编译器(GCC的cc1组件)完成

作用:

语法分析:检查代码是否符合C语言规范

语义优化:在-Og优化级别下,平衡可读性与性能

生成汇编:将高级C代码转换为低级机器指令的助记符

3.2 在Ubuntu下编译的命令

-m64:64位 -S:生成汇编代码 -Og:启用调试优化

3.3 Hello的编译结果解析

3.3.1 字符串处理

.LC0:

.string "\347\224\250\346\263\225..." # UTF-8编码的中文字符串

.LC1:

.string "Hello %s %s %s\n" # printf格式字符串

3.3.2 函数框架

函数入口:

main:

endbr64 # 对抗ROP攻击的指令

pushq %rbp # 保存基址指针

pushq %rbx # 保存被调用者保存寄存器

subq $8, %rsp # 分配栈空间

3.3.3 参数检查

cmpl $5, %edi # 比较argc和5

jne .L6 # 不等于则跳转错误处理

movq %rsi, %rbx # 保存argv指针

3.3.4 错误处理

.L6:

leaq .LC0(%rip), %rdi # 加载错误字符串地址

call puts@PLT # 调用puts输出

movl $1, %edi

call exit@PLT # 退出程序

3.3.5 主循环结构

循环初始化:movl $0, %ebp # i = 0

循环条件:

.L2:

cmpl $9, %ebp # 比较i和9

jle .L3 # i <= 9时继续循环

循环增量:addl $1, %ebp # i++

3.3.6 函数调用

printf调用:

movq 24(%rbx), %rcx # argv[3]

movq 16(%rbx), %rdx # argv[2]

movq 8(%rbx), %rsi # argv[1]

leaq .LC1(%rip), %rdi # 格式字符串

movl $0, %eax # 清空浮点寄存器

call printf@PLT

其他调用:

call atoi@PLT # 字符串转整数

call sleep@PLT # 延时

call getchar@PLT # 等待输入

3.3.7 函数返回

movl $0, %eax # 返回值0

addq $8, %rsp # 释放栈空间

popq %rbx # 恢复寄存器

popq %rbp

ret

3.4 本章小结

编译器将C代码准确转换为x86_64汇编指令,优化选项-Og保持了代码可读性,完整实现了:参数检查、循环控制、多参数函数调用、系统调用。符合x86_64调用约定。

使用RIP相对寻址访问字符串常量,参数通过寄存器传递(rdi, rsi, rdx, rcx),循环转换为条件跳转结构,完整保留了程序的控制流逻辑。

第4章 汇编

4.1 汇编的概念与作用

概念:

汇编是将汇编代码(.s)转换为机器语言二进制目标文件(.o)的过程,由汇编器完成

作用:

指令编码:将汇编助记符转换为机器码

生成可重定位目标文件:包含代码、数据、符号表和重定位信息,供链接器使用

4.2 在Ubuntu下汇编的命令

-c:仅汇编不链接 -m64:生成64位目标文件

4.3 可重定位目标elf格式

4.3.1 节头基本信息

通过 readelf -S hello.o 分析目标文件结构:

|----------------|----------|-------|-----|---------------------------|
| 节名 | 类型 | 大小 | 标志 | 说明 |
| .text | PROGBITS | 0x75 | AX | 主函数机器指令(可执行代码) |
| .rela.text | RELA | 0xc0 | I | .text节的重定位条目(需链接器修正地址) |
| .rodata.str1.8 | PROGBITS | 0x30 | AMS | 8字节对齐的只读字符串(中文错误提示.LC0) |
| .rodata.str1.1 | PROGBITS | 0x10 | AMS | 1字节对齐的只读字符串(Hello格式串.LC1) |
| .eh_frame | PROGBITS | 0x40 | A | 异常处理帧信息(用于栈展开) |
| .symtab | SYMTAB | 0x120 | - | 符号表(函数和全局变量名) |

4.3.2 重定位项目分析

通过 readelf -r hello.o 查看重定位条目:

R_X86_64_PC32(本地符号相对地址修正)

作用:加载字符串地址(.LC0)

指令:lea .LC0(%rip), %rdi

公式:最终地址=Symbol地址−Offset−4

R_X86_64_PLT32(外部函数PLT跳转修正)

作用:调用动态库函数(printf、sleep)

指令:call printf@PLT

公式:PLT偏移=PLT表基址+Symbol在PLT中的索引×16−Offset−4

4.4 Hello.o的结果解析

通过 objdump -d -r hello.o 输出的反汇编结果,结合第3章的 hello.s 汇编代码,对 hello.o 的机器语言构成、重定位条目及与汇编的映射关系进行详细分析。

4.4.1 汇编与反汇编的对比分析

4.4.1.1 函数入口与栈帧构建

汇编代码(hello.s ):

反汇编(hello.o ):

分析:

机器码:f3 0f 1e fa:endbr64(Intel CET指令,防御ROP攻击)。

55 53 48 83 ec 08:保存 %rbp、%rbx,分配8字节栈空间。

操作数编码:无重定位需求,偏移量直接编码(sub $0x8,%rsp 对应 48 83 ec 08)。

4.4.1.2 参数检查与错误处理

汇编代码(hello.s ):

反汇编(hello.o ):

分析:

机器码:83 ff 05:cmpl $5, %edi(比较argc与5)。

75 0a:jne(条件不满足时跳转至偏移量0x19)。

跳转目标:目标地址 0x19 在汇编阶段已计算为绝对偏移,无需重定位。

4.4.1.3 错误处理分支(.L6

汇编代码(hello.s ):

反汇编(hello.o ):

分析:

字符串加载(.LC0):

机器码:48 8d 3d 00 00 00 00

lea指令的PC相对寻址,占位符 00 00 00 00 需重定位

重定位条目:R_X86_64_PC32

修正值 = .LC0地址 - 下一条指令地址(0x20) - 4

函数调用(puts@PLT):

机器码:e8 00 00 00 00

call指令的PLT表跳转,占位符需链接器填充

重定位条目:R_X86_64_PLT32

修正值 = puts@PLT入口地址 - 下一条指令地址(0x25) - 4

4.4.1.4 主循环逻辑(.L2-.L3

汇编代码(hello.s ):

反汇编(hello.o ):

分析:

(1) 参数传递机制

argv 访问: %rbx 存储 argv 数组基址,偏移量计算

mov 0x8(%rbx), %rsi # argv[1] = *(argv + 8) 机器码 48 8b 73 08

48:64位操作数前缀 8b 73 08:mov指令编码,源地址为 %rbx + 8

printf 参数传递:

寄存器顺序:%rdi(格式串)、%rsi(argv[1])、%rdx(argv[2])、%rcx(argv[3])。

浮点处理:mov $0x0, %eax 表示无向量寄存器参数。

(2) 控制流与重定位

循环跳转(jle .L3) 机器码:7e cb

7e:jle操作码 cb:8位偏移量(补码 -53),目标地址为 0x62 - 53 + 2 = 0x2f

无需重定位,偏移量在汇编阶段确定。

外部函数调用(sleep@PLT) 机器码:e8 00 00 00 00

占位符 00 00 00 00 需链接器填充

R_X86_64_PLT32,通过PLT表实现动态链接。

4.4.1.5 程序退出与栈帧回收

汇编代码(hello.s ):

反汇编(hello.o ):

分析:

栈平衡 addq $8, %rsp 回收栈空间,popq恢复保存的寄存器

返回值 mov $0x0, %eax 设置返回值0

4.4.2 机器语言构成

机器语言由操作码和操作数组成,以二进制形式编码,具有以下特征:

定长操作码 call 对应 e8,movq 对应 48 8b

变长操作数 地址偏移、立即数等长度可变(1-8字节)

指令前缀 48 表示64位操作数,f3 0f 1e fa 表示安全指令 endbr64

4.4.3 机器语言与汇编语言的映射关系

|--------|----------------------|----------------------|------------------------------------------|
| | 汇编指令 | 机器码 | 操作数编码解析 |
| 数据操作指令 | movq 8(%rbx), %rsi | 48 8b 73 08 | 48(64位前缀)+ 8b(mov)+ 73 08(%rbx+8 → %rsi) |
| 数据操作指令 | lea .LC0(%rip), %rdi | 48 8d 3d 00 00 00 00 | 48 8d(lea)+ 3d(%rdi目标)+ 00 00 00 00(重定位) |
| 控制流 | call printf@PLT | e8 00 00 00 00 | e8(call)+ 00 00 00 00(重定位) |
| 控制流 | jle .L3 | 7e cb | 7e(jle)+ cb(8位偏移量 -53) |

4.4.4 机器语言与汇编语言不一致性分析

分支转移指令 如jle .L3 → 7e cb

汇编语言使用符号标签(.L3)机器语言编码为相对偏移量,与当前指令地址无关。

偏移计算:目标地址 0x2f = 当前地址 0x62 + 偏移 0xcb(-53) + 指令长度 2。

函数调用指令 如call puts@PLT → e8 00 00 00 00

汇编语言显式标注符号(puts@PLT)机器语言使用占位符,依赖重定位条目修正。

重定位修正:根据 R_X86_64_PLT32 类型,链接时填充PLT表偏移。

地址加载指令 如lea .LC0(%rip), %rdi → 48 8d 3d 00 00 00 00

汇编语言使用符号(.LC0)和相对寻址(%rip)

机器语言编码为 PC相对偏移占位符,需重定位修正。

重定位修正:根据 R_X86_64_PC32 类型,计算 .LC0 与 %rip 的相对偏移。

4.5 本章小结

本章分析了可重定位目标文件 hello.o 的生成与结构,揭示了汇编到机器语言的转换机制。目标文件通过 .text 节存储机器指令,.rela.text 记录需重定位的符号地址。机器语言由操作码和操作数构成,分支指令直接编码相对偏移,函数调用通过占位符预留重定位空间。安全设计上,endbr64 指令与ELF属性节增强了控制流完整性。本章为理解链接过程与指令集架构协作提供了底层视角。

第5章 链接

5.1 链接的概念与作用

链接是将多个目标文件和库文件合并为可执行文件的过程,包括:

符号解析:将未定义的符号(如 printf)绑定到动态库中的实际地址

地址重定位:修正代码和数据段的绝对地址,生成可执行内存布局

节合并:整合不同目标文件的 .text、.data 等节,生成最终ELF结构

5.2 在Ubuntu下链接的命令

-dynamic-linker:指定动态链接器路径 crt1.o 提供程序入口 _start

crti.o 和 crtn.o 包含初始化代码 -lc 链接 libc.so,提供标准库函数

5.3 可执行目标文件hello的格式

|----------|----------|-------|----|------------------------------------------------|
| 段名 | 起始地址 | 大小 | 权限 | 说明 |
| .interp | 0x4002e0 | 0x1c | R | 存储动态链接器路径 |
| .plt | 0x401020 | 0x70 | RX | 过程链接表(PLT),用于跳转到动态库函数 |
| .text | 0x4010f0 | 0xaa | RX | 主程序代码,包含 _start、main 和 _dl_relocate_static_pie |
| .rodata | 0x402000 | 0x48 | R | 只读数据段,存储字符串常量 |
| .got.plt | 0x403fe8 | 0x48 | RW | 全局偏移表(GOT),存储动态库函数的实际地址 |
| .data | 0x404030 | 0x4 | RW | 已初始化的全局数据 |
| .dynamic | 0x403e38 | 0x1a0 | RW | 动态链接信息,包含依赖库列表和重定位条目 |

5.4 hello的虚拟地址空间

使用edb加载hello,查看虚拟地址空间各段信息,并与5.3对照分析

通过对比edb加载的虚拟地址空间信息与5.3中的ELF段信息,分析如下:

1. 只读代码段(R--/R-X

内存区域:

对应ELF段:

0x400000-0x401000(R--):包含.interp(0x4002e0)、.note、.hash等只读段,与ELF权限一致

0x401000-0x402000(R-X):包含可执行段.plt(0x401020)、.text(0x4010f0),权限与ELF中标记的RX完全匹配

0x402000-0x403000(R--):对应只读数据段.rodata(0x402000),权限一致

2. 可读写数据段(RW

内存区域:

对应ELF段:

.dynamic(0x403e38):存储动态链接信息,权限为RW。

.got.plt(0x403fe8):全局偏移表,需运行时修改,权限为RW。

.data(0x404030):已初始化全局数据,权限为RW。

3. 动态链接库与系统区域

内存区域:

stack\]、\[vvar\]、\[vdso\]、\[vsyscall\]为系统保留区域 动态链接库的段不属于hello的ELF文件,但在进程虚拟地址空间中加载 \[stack\]为运行时栈,\[vdso\]为内核提供的虚拟动态共享对象,与5.3中的段无关 **4.** **权限合并与对齐** ELF文件中的多个段(如.interp、.note)被合并到同一内存页(0x400000-0x401000),权限取并集(R--)。 可执行段(.text、.plt)合并到R-X区域,符合代码段的典型权限。 ### 5.5 链接的重定位过程分析 对比 hello.o 和 hello 的反汇编代码,解析重定位过程 1. 函数调用重定位(以 printf 为例) hello.o 中的未解析调用: ![](https://i-blog.csdnimg.cn/direct/2a81cfe74fb747159314d6bb0cdbe1ec.png) hello 中的重定位结果: ![](https://i-blog.csdnimg.cn/direct/020692c631b540618eff5e5978ff22f9.png) PLT条目(0x4010a0): ![](https://i-blog.csdnimg.cn/direct/a3f6bae08db248d09b8d25885ab4c784.png) GOT条目初始值:首次调用时,GOT\[printf\] 指向PLT中的解析代码,触发动态链接器加载 printf 的实际地址。 2. 数据地址重定位(以字符串常量为例) hello.o 中的未解析地址: ![](https://i-blog.csdnimg.cn/direct/e63ce6f81490459da3c30e81c6b85a85.png) hello 中的重定位结果: ![](https://i-blog.csdnimg.cn/direct/24b54fe7d9d34ffb86916d64772ad1e6.png) 字符串常量位置:.rodata 段的 0x402038 存储 "Hello %s %s %s\\n"。 ### 5.6 hello的执行流程 ![](https://i-blog.csdnimg.cn/direct/a97f5854d7b645b2a2c40fab95fc5a7e.png) 1. 动态加载阶段 入口函数:_start(动态链接器) 地址:0x00007ffff7fe4540(位于 ld-linux-x86-64.so.2) 作用:加载程序依赖的共享库(如 libc.so.6),初始化运行环境。 2. 程序启动阶段 入口函数:_start(hello程序) 地址:0x4010f0(.text段) 操作:调用 __libc_start_main,传递 main 函数地址。 调用:call \*0x403fd8 ; 调用 __libc_start_main@GLIBC_2.34 3.主函数执行阶段 入口函数:main 地址:0x401125 执行流程: (1)参数检查: cmpl $0x5, %edi ; 检查 argc 是否为5 jne 0x40113e ; 跳转到错误处理分支 (2)循环逻辑: 循环计数器:%ebp 从0递增至9。 函数调用链: |------------|----------|-------------| | 函数调用 | 地址 | 作用 | | printf@plt | 0x4010a0 | 输出格式化字符串 | | atoi@plt | 0x4010c0 | 将字符串参数转换为整数 | | sleep@plt | 0x4010e0 | 休眠指定秒数 | 循环终止条件:cmpl $0x9, %ebp jle 循环体 4. 中断处理阶段 触发条件:用户按下 Ctrl-C(发送 SIGINT 信号)。 调用链: |------------------------|--------------------|--------------------| | 函数名 | 地址 | 作用 | | __GI___clock_nanosleep | 0x00007ffff7ceca7a | 内核级休眠实现 | | __GI___nanosleep | 0x00007ffff7cf9a27 | 调用 clock_nanosleep | | __sleep | 0x00007ffff7d0ec63 | 封装 nanosleep 系统调用 | | main | 0x401181 | 主函数中的 sleep 调用点 | 5. 程序终止阶段 正常终止: main 返回后,__libc_start_main 调用 exit 终止进程。 中断终止: 内核直接终止进程,释放资源。 ### 5.7 Hello的动态链接分析 **1.** **动态链接前的GOT** **状态** 在程序首次调用外部函数前,GOT条目指向PLT中的解析代码。 调试命令与输出: ![](https://i-blog.csdnimg.cn/direct/7b37a84a78634c97a7c1c589292058b3.png) [email protected](0x404008)初始值为 0x00401040,指向 printf@plt+0x10 [email protected](0x404018)初始值为 0x00401060,指向 atoi@plt+0x10。 **2.** **动态链接后的GOT** **状态** 首次调用函数后,动态链接器(ld.so)解析实际函数地址并更新GOT。 调试命令与输出: ![](https://i-blog.csdnimg.cn/direct/b853f2aadac94b89acc807d7474bc96a.png) [email protected](0x404008)更新为0x7ffff7c60100,指向libc.so中的printf实际地址。 [email protected](0x404018)更新为 0x7ffff7c46660,指向libc.so中的atoi实际地址。 ### 5.8 本章小结 本章分析了hello程序从目标文件到可执行文件的链接过程及其运行机制。链接通过符号解析、地址重定位和节合并生成ELF结构,关键段包括.text(代码)、.rodata(只读数据)和.got.plt(动态函数地址)。虚拟地址空间中,ELF段权限与内存映射一致,系统区域由内核独立管理。重定位修正函数调用至PLT,动态链接通过GOT实现延迟绑定:首次调用触发地址解析并更新GOT为实际地址。程序执行流程从动态加载器ld.so初始化开始,经_start调用main,循环输出信息后通过exit或信号终止。 ## 第6章 hello进程管理 ### 6.1 进程的概念与作用 进程是程序的执行实例,拥有独立的地址空间、代码、数据和系统资源 **作用:** 资源分配:CPU时间片、内存、文件句柄等由操作系统统一管理 隔离保护:进程间相互隔离,避免数据冲突或非法访问 并发执行:通过进程调度实现多任务并行 ### 6.2 简述壳Shell-bash的作用与处理流程 作用: 命令解析:解析用户输入的命令(如 ./hello)。 进程管理:创建子进程执行程序(fork + execve),支持后台运行(\&)。 环境管理:维护环境变量(如 PATH)和输入输出重定向(\>、\<)。 处理流程: 1.读取输入:从终端或脚本获取命令。 2.解析命令:分割参数,处理管道(\|)、重定向等。 3.执行命令:若为内置命令(如 cd),直接执行;否则创建子进程运行程序。 ### 6.3 Hello的fork进程创建过程 调用fork():Shell创建子进程,复制父进程的地址空间、文件描述符等 返回值区分: 父进程:fork() 返回子进程PID 子进程:fork() 返回0 子进程任务:调用 execve() 加载 hello 程序,替换当前进程映像 ### 6.4 Hello的execve过程 功能:加载并执行 hello 程序,替换当前进程的代码段、数据段和堆栈 参数传递: argv:命令行参数数组(\["学号", "姓名", ...\]) envp:环境变量数组(如 PATH=/usr/bin) 执行后: 进程的入口点变为 hello 的 _start 函数 ### 6.5 Hello的进程执行 进程上下文:包括寄存器值、程序计数器(PC)、栈指针(SP)等。 时间片调度:操作系统分配CPU时间片(如10ms),时间耗尽后触发时钟中断,切换进程。 用户态与核心态转换: 系统调用(如 sleep()):从用户态切换到核心态,执行内核代码。 中断处理(如 Ctrl-C):内核接管控制权,处理信号后返回用户态。 ### 6.6 hello的异常与信号处理 |------------|---------|-------------| | 异常类型 | 触发信号 | 处理方式 | | 中断(Ctrl-C) | SIGINT | 终止进程 | | 停止(Ctrl-Z) | SIGTSTP | 挂起进程至后台 | | 非法操作 | SIGSEGV | 终止进程并生成核心转储 | 1.Ctrl-Z 挂起 ![](https://i-blog.csdnimg.cn/direct/463feabe30fa48359be4758805ef08b5.png) 2.ps ![](https://i-blog.csdnimg.cn/direct/e0550f6e14414c9e9de8bb38ef3123e2.png) 3.jobs ![](https://i-blog.csdnimg.cn/direct/f77b12416b13493eaccf0d3dc8422416.png) 4.pstree ![](https://i-blog.csdnimg.cn/direct/ec28bea959864d3e849d0e4b55c1db92.png) 5.kill ![](https://i-blog.csdnimg.cn/direct/faf0f4f1a28746ce873e9bf187b5aae8.png) 6.fg \& Ctrl+C ![](https://i-blog.csdnimg.cn/direct/1a6ea9e7acaa431bab41c74fae7b61e5.png) ### 6.7本章小结 本章探讨了hello程序的进程管理机制。进程作为程序的执行实例,为hello提供了资源分配、隔离保护和并发执行的能力。Shell(bash)通过解析用户命令、创建子进程(fork)并加载程序(execve)来管理hello的执行。fork复制父进程环境,而execve将hello的代码和数据载入内存,开启新的执行流程。进程运行时,操作系统通过时间片调度和上下文切换实现多任务并行,并在系统调用或中断时切换用户态与核心态。此外,hello需处理异常和信号,如SIGINT终止进程、SIGTSTP挂起进程,并通过ps、jobs等工具进行监控和管理。本章揭示了进程从创建到终止的全生命周期,体现了操作系统对程序执行的核心控制作用。 ## 第7章 hello的存储管理 ### 7.1 hello的存储器地址空间 在hello程序的执行过程中,存储器地址通过多级抽象实现高效管理: 逻辑地址:程序段内的偏移地址,由编译器和链接器生成,对应代码段或数据段的局部位置。 hello的main函数在代码段中的逻辑地址为0x4004d6,表示相对于代码段基址的偏移。 线性地址:Intel处理器通过段式管理将逻辑地址转换为全局线性地址(段基址 + 偏移)。若hello的代码段基址为0x08048000,逻辑地址0x100转换为线性地址0x08048100。 虚拟地址:hello进程看到的独立地址空间(0x8048000),通过页表映射到物理地址,实现进程间隔离。 物理地址:实际内存芯片上的硬件地址,由MMU通过页表转换得到。 当hello执行printf时,指令的逻辑地址(如0x4010a0)经段式管理转换为线性地址,再通过页式管理映射到物理地址,最终访问内存中的字符串常量(.LC1位于.rodata段)。 ### 7.2 Intel逻辑地址到线性地址的变换-段式管理 Intel使用段式管理完成逻辑地址到线性地址的转换: 段选择符:代码段寄存器(CS)存储段选择符,指向全局描述符表(GDT)中的段描述符。 段描述符:包含段基址、界限和权限。hello的代码段基址为0x08048000。 线性地址计算:线性地址 = 段基址 + 逻辑偏移量。 hello的main函数逻辑地址0x4004d6,在段基址0x08048000下转换为线性地址0x0804d4d6(假设段基址为0x08048000)。 ### 7.3 Hello的线性地址到物理地址的变换-页式管理 操作系统通过页式管理将虚拟地址(线性地址)映射到物理地址: 分页机制:虚拟地址划分为页号和页内偏移 页表查询:通过页表基址寄存器找到页表,逐级查询四级页表项(PML4→PDPT→PD→PT),获取页框。 物理地址生成:物理地址 = PFN \<\< 12 \| Offset 假设hello访问虚拟地址0x402038(存储"Hello %s %s %s\\n"),页号为0x402,页内偏移0x038,通过页表查询PFN0x2000,物理地址为0x2000038。 ### 7.4 TLB与四级页表支持下的VA到PA的变换 现代系统通过TLB和四级页表加速地址转换: 四级页表:虚拟地址划分为4级索引(如9+9+9+9+12位),逐级查询页表项 TLB缓存:转换后备缓冲器(TLB)缓存近期页表项。若命中,直接获取PFN;否则遍历四级页表 当hello首次访问0x4010f0(_start函数入口)时,TLB未命中,需4次内存访问完成页表遍历。后续访问同一页时,TLB命中,转换延迟降至1\~2周期 ### 7.5 三级Cache支持下的物理内存访问 物理内存访问通过三级Cache优化性能: 缓存层级:L1(指令/数据分离)、L2(统一)、L3(共享)缓存,逐级容量增大、速度降低。 缓存行匹配:物理地址被划分为标记(Tag)、组索引(Set Index)和块偏移(Offset),通过组相联映射定位缓存行。 替换策略:LRU(最近最少使用)算法淘汰旧数据。 hello访问物理地址0x2000038时,若L1 Cache命中(Tag匹配),1\~2周期完成读取;否则触发L2/L3访问(约10\~40周期)或内存加载(约100周期)。 ### 7.6 hello进程fork时的内存映射 fork创建子进程时采用写时复制(Copy-on-Write)机制: 共享父进程页表:子进程初始共享父进程的物理页,页表项标记为只读。 写时复制:当任一进程尝试修改共享页时,触发页错误,内核分配新物理页并复制内容。 若父进程修改hello的全局变量,子进程将获得独立的物理页副本,保证进程隔离性。 ### 7.7 hello进程execve时的内存映射 execve加载hello程序时重建内存映射: 释放旧地址空间:清除原进程的代码、数据段和堆栈。 加载新程序:将hello的代码段映射到0x8048000,数据段到0x8050000,并初始化堆栈和堆。 动态链接:若使用共享库(如libc.so),通过动态链接器映射到进程空间。 execve后,hello的.text段映射到0x4010f0(可执行代码),.rodata段到0x402000(只读字符串),权限为R-X和R--。 ### 7.8 缺页故障与缺页中断处理 触发条件:访问未加载的虚拟页(页表项无效)或写只读页(如COW场景)。 中断处理:检查虚拟地址合法性。 分配物理页(或从磁盘换入)。 更新页表项并重新执行指令。 hello首次访问堆内存时触发缺页,内核分配零页(全零填充的物理页),避免未初始化数据错误。 ### 7.9动态存储分配管理 printf调用malloc时,动态内存管理策略如下: 隐式空闲链表:通过块头部的大小字段遍历空闲块,合并相邻空闲块减少碎片。 显式空闲链表:维护空闲块链表,加速分配。 伙伴系统:按2的幂划分块,减少外部碎片但可能产生内部碎片。 glibc的malloc为hello分配小内存时,优先从线程本地缓存(tcache)获取,减少锁竞争;大内存则通过mmap系统调用直接映射。 ### 7.10本章小结 本章系统解析了hello程序的存储管理机制。从逻辑地址到物理地址的转换依赖段式与页式管理,TLB和四级页表优化了映射效率。三级Cache通过层级缓存降低内存访问延迟。进程fork时的写时复制与execve的内存重建机制,保障了进程的隔离与灵活性。缺页中断实现按需加载,动态内存管理策略(如malloc)平衡了效率与碎片问题。存储管理是操作系统资源调度的核心,支撑了hello程序从地址抽象到物理执行的全流程。 ## 第8章 hello的IO管理 ### 8.1 Linux的IO设备管理方法 Linux采用统一的"一切皆文件"模型管理IO设备: **设备抽象:**所有硬件设备(键盘、显示器、磁盘等)被抽象为文件,位于/dev目录下,如/dev/tty表示终端设备。 **设备分类:** 字符设备:以字节流形式访问(如键盘、串口),通过字符设备文件(如/dev/input/event0)操作。 块设备:以固定大小数据块访问(如硬盘、SSD),通过块设备文件(如/dev/sda)操作。 **设备驱动管理:**内核通过设备驱动程序与硬件交互,用户程序通过标准Unix IO接口(如read、write)访问设备,无需关心底层实现。 hello通过printf输出到终端时,实际是向字符设备文件/dev/tty写入数据,由显示驱动处理;getchar则从同一设备读取键盘输入。 ### 8.2 简述Unix IO接口及其函数 **Unix IO** **接口**提供以下核心系统调用: open:打开设备文件(如/dev/tty) 返回文件描述符(fd)。 read:从fd读取数据(如键盘输入) 函数原型:ssize_t read(int fd, void \*buf, size_t count) write:向fd写入数据(如屏幕输出) 函数原型:ssize_t write(int fd, const void \*buf, size_t count) close:关闭fd,释放资源 ioctl:控制设备参数(如设置终端模式) **IO** **模式**: 阻塞IO:默认模式,若设备无数据,进程挂起等待(如getchar等待键盘输入)。 非阻塞IO:通过O_NONBLOCK标志设置,立即返回错误码EAGAIN。 异步IO:通过信号或回调通知数据就绪(如aio_read)。 ### 8.3 printf的实现分析 1.格式化字符串处理:调用vsprintf将格式字符串和参数转换为格式化后的字符串(如"Hello 学号 姓名 手机号 1"),存入用户态缓冲区。 2.系统调用写入:调用write系统调用(函数号SYS_write),触发软中断(如syscall指令或int 0x80),切换到内核态。内核通过终端设备驱动将字符串写入显示缓冲区(vram)。 3.显示驱动处理:字模库映射将ASCII字符转换为像素点阵(16×16点阵),vram更新将像素RGB值写入显存对应位置(每个像素占4字节,格式为ARGB)。 屏幕刷新,显示控制器按刷新率(如60Hz)逐行读取vram,通过信号线输出到显示器。 ### 8.4 getchar的实现分析 1.键盘中断触发:用户按下按键时,键盘控制器发送中断请求(IRQ1),CPU调用键盘中断处理程序(ISR)。 2.扫描码转换:ISR读取键盘扫描码(如回车键为0x1C),转换为ASCII码(如'\\n'),存入内核缓冲区tty_read_buf。 3.系统调用读取:getchar调用read系统调用(fd=0),从缓冲区读取ASCII码,若缓冲区为空则阻塞等待。遇到换行符('\\n')时,read返回缓冲区内容,用户程序继续执行。 ### 8.5本章小结 本章探讨了hello程序的IO管理机制。Linux通过"一切皆文件"模型将硬件设备抽象为字符设备(如终端/dev/tty)或块设备,用户程序通过标准Unix接口(如read、write)实现设备访问。printf的执行分为三步:vsprintf格式化字符串生成输出内容,write系统调用将数据写入显存(vram),显示驱动将ASCII字符转换为像素矩阵并刷新屏幕。getchar依赖键盘中断机制,中断处理程序将扫描码转为ASCII码存入内核缓冲区,read系统调用阻塞读取直至回车触发返回。 ## 结论 Hello程序的生命周期完整体现了计算机系统的核心设计理念。从代码(Program)到进程(Process)的P2P流程中,编译系统通过预处理、编译、汇编、链接将高级语言转换为可执行文件;操作系统通过进程管理(fork、execve)和存储管理(段页式转换、TLB、Cache)为程序提供执行环境;硬件与内核协作完成指令执行、地址转换和IO操作。 通过本项目,我深切感悟到: 分层抽象:计算机系统通过编译链、操作系统和硬件的分层抽象,实现了从高级语言到物理硬件的无缝衔接,极大提升了开发效率与系统可靠性。 动态性与灵活性:动态链接、写时复制和按需分页等机制,平衡了资源利用率与性能,展现了系统设计的精巧。 优化空间:在工具链配置(如编译器优化选项)、内存管理(如减少缺页中断)和IO效率(如缓冲策略)等方面仍有优化潜力。 本项目不仅巩固了计算机系统核心知识,更启发了对系统设计与实现的创新思考。 ## 附件 |------------------|-----------------------------------------------| | 文件名 | 作用 | | hello.c | | | hello.i | 预处理后的代码文件,展开所有宏、头文件,并删除注释。 | | hello.s | 汇编代码文件,包含x86-64汇编指令。 | | hello.o | 可重定位目标文件(ELF格式),尚未链接。 | | hello | 最终的可执行文件,包含动态链接库(如libc)。 | | hello_o_dump.txt | hello.o的反汇编输出(objdump -d), 用于分析机器码和汇编指令的对应关系。 | | hello_o_elf.txt | hello.o的ELF结构分析(readelf), 查看节头、符号表、重定位信息等。 | | hello_dump.txt | hello的反汇编输出(objdump -d), 分析可执行文件的机器码和运行时行为。 | | hello_elf.txt | hello的ELF结构分析(readelf), 查看程序入口、动态链接信息等。 | |----------------------------| | 原始的C语言源代码文件 | | 预处理后的代码文件,展开所有宏、头文件,并删除注释。 | ## 参考文献 \[1\] [Randal E. Bryant](https://book.douban.com/author/263391 "Randal E. Bryant") ,[David O'Hallaron](https://book.douban.com/search/David%20O%27Hallaron "David O'Hallaron"). [深入理解计算机系统](https://book.douban.com/subject/26912767/ "深入理解计算机系统")(第3版), [机械工业出版社](https://book.douban.com/press/2793 "机械工业出版社"),2016.11 \[2\] [Alfred V. Aho](https://book.douban.com/search/Alfred%20V.%20Aho "Alfred V. Aho"),[Monica S.Lam](https://book.douban.com/search/Monica%20S.Lam "Monica S.Lam"),[Ravi Sethi](https://book.douban.com/search/Ravi%20Sethi "Ravi Sethi"),[Jeffrey D. Ullman](https://book.douban.com/search/Jeffrey%20D.%20Ullman "Jeffrey D. Ullman"). 编译原理, [机械工业出版社](https://book.douban.com/press/2793 "机械工业出版社"),2008.12 \[3\] [Andrew S. Tanenbaum](https://book.douban.com/author/285094 "Andrew S. Tanenbaum"). 现代操作系统(第3版),[机械工业出版社](https://book.douban.com/press/2793 "机械工业出版社"),2009.7

相关推荐
Felven3 小时前
C. Basketball Exercise
c语言·开发语言
可乐鸡翅好好吃3 小时前
通过BUG(prvIdleTask、pxTasksWaitingTerminatio不断跳转问题)了解空闲函数(prvIdleTask)和TCB
c语言·stm32·单片机·嵌入式硬件·bug·keil
才鲸嵌入式5 小时前
01 Ubuntu20.04下编译QEMU8.2.4,交叉编译32位ARM程序,运行ARM程序的方法
linux·c语言·单片机·嵌入式·arm·qemu·虚拟机
int型码农11 小时前
数据结构第八章(二)-交换排序
c语言·数据结构·算法·排序算法
jz_ddk16 小时前
[zynq] Zynq Linux 环境下 AXI BRAM 控制器驱动方法详解(代码示例)
linux·运维·c语言·网络·嵌入式硬件
Magnum Lehar16 小时前
vulkan游戏引擎启动环境配置1
c语言
待什么青丝17 小时前
【TMS570LC4357】之相关驱动开发学习记录1
c语言·arm开发·驱动开发·学习
C_Liu_17 小时前
C语言:数据在内存中的存储
c语言·开发语言