计算机系统
大作业
题 目 程序人生-hello’s P2P
专 业 计算机与电子通信类
学 号 2023111990
班 级 23L0514
学 生 袁骋
指 导 教 师 史先俊
计算机科学与技术学院
2025年5月
本文深入剖析了 hello 程序从源代码编写到运行结束的全过程,涵盖预处理、编译、汇编、链接、加载、运行、存储管理、回收等多个阶段。通过详细分析每个阶段的关键操作和机制,揭示了计算机系统底层的工作原理,进一步加深了对计算机系统设计与实现的理解,以及各组件之间的协同工作方式。这一过程不仅帮助我们更好地理解了程序的生命周期,也为后续的学习和研究提供了重要的参考。
关键词:计算机系统;CSAPP;编译;存储;操作系统;OS
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
目 录
第1章 概述................................................................................................................ - 4 -
1.1 hello简介......................................................................................................... - 4 -
1.2 环境与工具........................................................................................................ - 4 -
1.3 中间结果............................................................................................................ - 5 -
1.4 本章小结............................................................................................................ - 5 -
第2章 预处理............................................................................................................ - 6 -
2.1 预处理的概念与作用........................................................................................ - 6 -
2.2在Ubuntu下预处理的命令............................................................................. - 6 -
2.3 hello的预处理结果解析................................................................................. - 6 -
2.4 本章小结............................................................................................................ - 7 -
第3章 编译................................................................................................................ - 8 -
3.1 编译的概念与作用............................................................................................ - 8 -
3.2 在Ubuntu下编译的命令................................................................................ - 8 -
3.3 hello的编译结果解析..................................................................................... - 8 -
3.4 本章小结.......................................................................................................... - 12 -
第4章 汇编.............................................................................................................. - 13 -
4.1 汇编的概念与作用.......................................................................................... - 13 -
4.2 在Ubuntu下汇编的命令.............................................................................. - 13 -
4.3 可重定位目标elf格式.................................................................................. - 13 -
4.4 hello.o的结果解析........................................................................................ - 15 -
4.5 本章小结.......................................................................................................... - 17 -
第5章 链接.............................................................................................................. - 18 -
5.1 链接的概念与作用.......................................................................................... - 18 -
5.2 在Ubuntu下链接的命令.............................................................................. - 18 -
5.3 可执行目标文件hello的格式...................................................................... - 19 -
5.4 hello的虚拟地址空间................................................................................... - 21 -
5.5 链接的重定位过程分析.................................................................................. - 21 -
5.6 hello的执行流程........................................................................................... - 22 -
5.7 hello的动态链接分析................................................................................... - 22 -
5.8 本章小结.......................................................................................................... - 23 -
第6章 hello进程管理....................................................................................... - 24 -
6.1 进程的概念与作用.......................................................................................... - 24 -
6.2 简述壳Shell-bash的作用与处理流程........................................................ - 24 -
6.3 hello的fork进程创建过程......................................................................... - 24 -
6.4 hello的execve过程..................................................................................... - 25 -
6.5 hello的进程执行........................................................................................... - 25 -
6.6 hello的异常与信号处理............................................................................... - 25 -
6.7本章小结.......................................................................................................... - 29 -
第7章 hello的存储管理................................................................................... - 30 -
7.1 hello的存储器地址空间............................................................................... - 30 -
7.2 Intel逻辑地址到线性地址的变换-段式管理............................................... - 30 -
7.3 hello的线性地址到物理地址的变换-页式管理.......................................... - 30 -
7.4 TLB与四级页表支持下的VA到PA的变换................................................ - 30 -
7.5 三级Cache支持下的物理内存访问............................................................. - 31 -
7.6 hello进程fork时的内存映射..................................................................... - 31 -
7.7 hello进程execve时的内存映射................................................................. - 31 -
7.8 缺页故障与缺页中断处理.............................................................................. - 32 -
7.9动态存储分配管理.......................................................................................... - 32 -
7.10本章小结........................................................................................................ - 32 -
第8章 hello的IO管理.................................................................................... - 33 -
8.1 Linux的IO设备管理方法............................................................................. - 33 -
8.2 简述Unix IO接口及其函数.......................................................................... - 33 -
8.3 printf的实现分析........................................................................................... - 33 -
8.4 getchar的实现分析....................................................................................... - 33 -
8.5本章小结.......................................................................................................... - 33 -
结论............................................................................................................................ - 34 -
附件............................................................................................................................ - 36 -
参考文献.................................................................................................................... - 37 -
第1章 概述
1.1 hello简介
根据hello的自白,利用计算机系统的术语,简述hello的P2P,020的整个过程。
P2P:
预处理:hello.c通过预处理器展开头文件、宏定义,生成hello.i。
编译:将hello.i转换为汇编代码hello.s,完成语法分析和指令生成。
汇编:hello.s被汇编器翻译为可重定位目标文件hello.o,包含机器指令和符号表。
链接:链接器合并hello.o与库文件(如libc.so),解析符号引用,生成可执行文件hello。
运行:Shell通过fork创建子进程,execve加载hello到内存,CPU执行指令,最终进程终止。
O2O:
从0到1:程序从无到有的构建(编辑、编译、链接)。
从1到0:进程运行结束后释放资源,回归系统初始状态。
1.2 环境与工具
我使用学校提供的Dell服务器完成本实验,环境如下:
CPU:AMD EPYC 7763 64-Core Processor,16核心,16线程
内存:32GB DDR4
磁盘:200GB SATA HDD(系统盘)
操作系统:Ubuntu 22.04 LTS(Kernel 6.8.0-54-generic)
使用的软件工具:
vscode(ssh连接服务器)
gcc
gdb
readelf
objdump
1.3 中间结果
从最初的hello.c源文件开始,逐步得到了以下结果文件:
hello.i
预处理后的文件,通过GCC的-E选项生成。该文件包含了展开的头文件、宏定义以及删除的注释,为编译阶段提供了干净的输入。
hello.s
编译阶段生成的汇编代码文件,通过GCC的-S选项生成。该文件包含了程序的汇编指令,反映了C代码到汇编指令的转换过程。
hello.o
汇编阶段生成的目标文件,通过GCC的-c选项生成。该文件包含了机器指令和符号表,是链接阶段的输入。
hello
链接阶段生成的可执行文件,ld生成。
hello.dis.txt
反汇编输出文件,通过objdump工具生成。该文件包含了hello文件的反汇编代码,用于分析汇编指令和机器码之间的关系。
hello.elf.txt
ELF文件格式分析输出,通过readelf工具生成。该文件包含了可执行文件hello的ELF格式信息,用于分析程序的加载和链接过程。
hello.o.dis.txt
目标文件hello.o的反汇编输出文件,通过objdump工具生成。该文件用于分析目标文件中的汇编指令和符号信息。
1.4 本章小结
本章概述了hello的P2P/O2O流程,明确了实验环境和中间产物,为后续章节的详细分析奠定基础。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
预处理是C程序编译的第一阶段,由预处理器(cpp)执行,主要功能包括:
宏展开:替换#define定义的宏和常量。
头文件包含:将#include指定的头文件内容插入到源文件中。
条件编译:根据#if、#ifdef等指令选择性地包含或排除代码。
删除注释:移除所有/* */和//注释,减少编译负担。
添加行号和文件名标记:便于调试时定位错误。
预处理后的文件(.i)仍然是C代码,但已去除预处理指令,为后续编译阶段提供干净的输入。
2.2在Ubuntu下预处理的命令
gcc -E hello.c -o hello.i
图 1 预处理命令
2.3 hello的预处理结果解析
stdio.h, unistd.h, stdlib.h的内容被完整插入到hello.i中,导致代码行数大量增加;源代码中的注释完全消失,仅保留有效代码;添加了原始文件和行号信息标记。
图 2 预处理后的hello.i节选
2.4 本章小结
本章详细分析了C语言程序的预处理阶段。通过实际操作,我们使用gcc的-E选项生成了hello.i预处理文件,并解析了预处理过程中的关键操作,包括头文件包含、注释删除和行号标记等。预处理阶段为后续的编译阶段准备了纯净的C语言代码,是程序编译过程中不可或缺的第一步。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
编译是将预处理后的高级语言代码(.i文件)转换为汇编语言代码(.s文件)的过程。其主要作用包括:
词法分析:将源代码分解为有意义的词法单元(tokens)
语法分析:检查语法结构,生成抽象语法树(AST)
语义分析:检查类型匹配、变量声明等语义规则
中间代码生成:生成与机器无关的中间表示
代码优化:对中间代码进行优化
目标代码生成:生成特定平台的汇编代码
编译阶段是程序从高级语言向机器语言转换的关键环节,决定了程序的底层实现方式。
3.2 在Ubuntu下编译的命令
gcc -S hello.i -o hello.s -m64 -no-pie -fno-PIC
图 3 编译命令
3.3 hello的编译结果解析
3.3.1 常量处理
(1)字符串常量
存储在.rodata只读数据段,比如UTF-8编码的中文提示字符串
图 4 字符串常量存储
(2)整型常量
直接编码为指令立即数,比如if(argc!=5)中的5
图 5 整形常量存储
3.3.2 变量处理
(1)局部变量
分配栈空间存储。示例:循环变量i(初始值为0)
图 6 局部变量存储
(2)参数变量
存储在寄存器中,以argc为例
图 7 参数变量存储
3.3.3 算术操作
直接生成对应的汇编指令,比如循环变量i的递增
图 8 算术操作的处理
3.3.4数组操作
基址+偏移量,以数组argv[]访问为例
图 9 数组的处理
3.3.5 类型转换
(1)显式转换
显式调用转换函数。
图 10 显式类型转换
(2)隐式转换
使用不同长度的寄存器和指令。
图 11 隐式类型转换
3.3.6 宏处理
在预处理阶段完成。
3.3.7 函数调用
利用寄存器进行参数与返回值的传递,函数的返回值通常存储在%eax寄存器中。
图 12 返回值的存储
通过ret指令返回。
图 13 函数调用
3.3.8 控制转移
(1)条件分支
使用cmp指令比较操作数,然后根据比较结果使用je(跳转相等)、jle(跳转小于等于)等指令进行条件跳转。if(argc!=5)对应的汇编指令为:
图 14 条件分支
(2)循环
使用jmp指令实现循环跳转。对于本程序中的循环体,.L3是循环的开始标签,.L4是循环体的标签。每次循环结束后与常数9进行比较,决定是否通过jle指令跳转回.L4继续执行。
图 15 循环
3.3.9 关系操作
cmpl指令比较两个操作数,并根据比较结果设置标志位,然后其他指令根据标志位完成某个功能。本程序中有两处比较。
图 16 比较1(相等跳转)
图 17 比较2(小于等于跳转)
3.4 本章小结
本章详细分析了hello程序在编译阶段的数据、函数调用、控制转移和关系操作等的处理,明确了C代码与汇编代码的对应关系。编译器将C语言中的高级逻辑转换为具体的汇编指令,从而实现程序的功能。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
汇编是将汇编语言代码(.s文件)转换为机器语言代码(.o文件)的过程。汇编器的主要作用包括:
语法解析:将汇编语言中的指令和伪指令解析为机器语言指令。
符号处理:为代码中的符号(如标签、变量名等)生成符号表,记录它们的地址和属性。
指令编码:将汇编指令转换为对应的机器码。
生成目标文件:生成可重定位目标文件(.o文件),该文件包含机器码、符号表和重定位信息,用于后续的链接过程。
汇编是程序从高级语言向机器语言转换的重要中间步骤,它将人类可读的汇编代码转换为计算机可执行的机器码。
4.2 在Ubuntu下汇编的命令
gcc -m64 -no-pie -fno-PIC -c hello.s -o hello.o
图 18 汇编命令
4.3 可重定位目标elf格式
(1)ELF文件头信息
图 19 ELF文件头
Class: ELF64,表示这是一个64位的ELF文件。
Data: 2's complement, little endian,表示数据以小端格式存储。
Type: REL (Relocatable file),表示这是一个可重定位的目标文件,用于链接。
Machine: Advanced Micro Devices X86-64,表示目标架构是x86-64。
Entry point address: 0x0,可重定位目标文件的入口地址通常为0,因为实际入口地址在链接时确定。
Section headers: 包含14个节头,节头表的偏移为1080字节。
(2)重定位表信息
重定位表记录了需要在链接时进行地址调整的符号信息。
图 20 重定位表
hello.o包含两个重定位表:
①.rela.text
包含8个重定位条目,主要针对.text节中的符号引用。
例如:
R_X86_64_32:表示32位直接地址重定位。
R_X86_64_PLT32:表示32位PLT(过程链接表)相对地址重定位。
具体条目:
puts、exit、printf、atoi、sleep、getchar等函数的调用需要在链接时解析其地址。
.rodata节中的字符串常量地址也需要重定位。
②.rela.eh_frame
包含1个重定位条目,针对异常处理框架(EH Frame)。
类型为R_X86_64_PC32,表示32位PC相对地址重定位。
4.4 hello.o的结果解析
将hello.o用objdump -d -r hello.o > hello.o.txt反汇编,输出结果存储在txt文件中。
图 21 hello.o的反汇编输出文件hello.o.txt
(1)hello.o反汇编文件与hello.s直观对比
1.hello.o反汇编中不存在像hello.s中的.L2等分段,jmp时全部用地址来表示
图 22 hello.s中跳入循环体的语句
图 23 hello.o反汇编中跳入循环体的语句
2.相比hello.s,hello.o反汇编每行汇编代码前有地址与机器代码的十六进制表示。也如图22,图23所示。
(2)机器语言构成
机器语言由操作码和操作数组成。操作码决定了指令的操作类型,如算术运算、逻辑运算、数据传输等;操作数则指定了指令操作的对象,如寄存器、内存地址或立即数。
(3)机器语言与汇编语言的映射关系
1.指令:汇编指令直接对应机器码。例如,push %rbp对应的机器码是55,mov %rsp。这些指令在hello.s中以汇编语言的形式出现,而在hello.o的反汇编中则以机器码的形式展示,但它们的逻辑和功能是一致的。
2.操作数:hello.s中的操作数是十进制表示的,而hello.o反汇编中是十六进制。
3.分支转移和函数调用:在hello.s中,分支转移用段来实现,而在反汇编中则使用相对地址或绝对地址。对于函数调用,hello.s中使用call 函数名称,而在反汇编中使用call 地址。
4.5 本章小结
本章深入探讨了汇编阶段的关键过程及其输出结果。通过分析 hello.o 的反汇编代码,清晰地展示了汇编语言与机器语言之间的映射关系,揭示了指令、操作数以及分支转移和函数调用在两种语言中的具体表现形式。同时,对比 hello.s 和 hello.o 的内容,进一步阐释了汇编阶段如何将人类可读的汇编代码转换为计算机可执行的机器码,并生成包含机器码、符号表和重定位信息的可重定位目标文件,为后续的链接过程奠定基础。
(第4章1分)
第5章 链接
5.1 链接的概念与作用
链接是将多个目标文件和库文件合并成一个可执行文件的过程。链接器(ld)的主要作用包括:
符号解析:解析目标文件和库文件中的符号引用,确保所有符号都能正确解析到其定义的位置。
地址分配:为每个目标文件分配内存地址,确保它们在可执行文件中的位置正确。
重定位:根据符号的最终地址,调整目标文件中的指令和数据,使其能够在运行时正确访问。
生成可执行文件:将所有目标文件和库文件的内容合并,生成一个可执行文件,该文件包含程序的机器码、数据段、符号表等信息。
链接是程序从可重定位目标文件到可执行文件的关键步骤,它使得多个模块能够组合成一个完整的程序。
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/libc.so \
/usr/lib/x86_64-linux-gnu/crtn.o \
-dynamic-linker /lib64/ld-linux-x86-64.so.2
图 24 链接命令
5.3 可执行目标文件hello的格式
ELF Header:显示了 ELF 文件的基本信息。
图 25 ELF Header
Section Headers:列出了所有的节(section),包括它们的名称、类型、地址、大小等。
图 26 Section Headers
5.4 hello的虚拟地址空间
图 27 gdb查看的虚拟地址空间段信息
5.5 链接的重定位过程分析
hello的反汇编比hello.o的反汇编内容多得多,新增了库函数和节。并且hello反汇编的跳转使用虚拟内存地址,而hello.o反汇编使用偏移地址。
图 28 hello反汇编一窥
图 29 hello.o反汇编回顾
重定位条目:在 hello.elf.txt 中的 .rela.dyn 和 .rela.plt 节包含了重定位条目,这些条目指示链接器在运行时或加载时需要调整的地址。
图 30 重定位条目
地址调整:在 hello.dis.txt 中,一些函数调用(如 puts、printf 等)被重定位到它们在动态链接库中的实际地址。
PLT和GOT:程序链接表(PLT)和全局偏移表(GOT)用于动态链接。在 hello.dis.txt 中,我们可以看到 PLT 条目(如 puts@plt)和 GOT 条目(如 _GLOBAL_OFFSET_TABLE_)。
5.6 hello的执行流程
首先,程序加载后,控制流首先转移到 _start 符号处,这是程序的入口点。在 _start 处,程序会进行一些初始化工作,例如设置栈,然后调用动态链接器。接下来,控制流转移到动态链接器的 __libc_start_main 函数,该函数负责调用 main 函数并传递命令行参数。在 main 函数中,程序执行其主要逻辑,调用printf\atoi等函数。当 main 函数执行完毕并返回时,控制流回到 __libc_start_main,它随后调用 exit 函数来清理资源并终止程序。exit 函数会进行必要的清理工作,比如调用全局对象的析构函数,然后程序最终终止。
子程序:_start, main, printf等
5.7 hello的动态链接分析
在分析hello程序的动态链接时,我们首先注意到该程序依赖于几个动态链接库,包括linux-vdso.so.1、libc.so.6以及ld-linux-x86-64.so.2。这些库在程序启动时由动态链接器ld-linux-x86-64.so.2加载进内存。libc.so.6提供了程序中使用的C标准库函数,如printf和atoi。当程序开始执行,特别是当main函数被调用时,这些函数的地址已经被解析,并且程序能够正常调用它们。
通过使用gdb(GDB)调试器,我们能够观察到程序在动态链接前后的变化。在程序启动时,动态链接器负责加载所有必需的动态库,并将程序中的符号引用解析到这些库中的具体实现。随着程序的执行,比如在main函数中调用printf和atoi函数时,这些函数的调用已经被正确地链接到它们在动态库中的地址上,使得程序可以正常运行。当程序执行到exit函数并准备终止时,动态链接器会负责清理程序使用过的动态库资源。
5.8 本章小结
第五章总结了hello程序的链接过程。通过分析ELF格式、虚拟地址空间和动态链接,展示了程序从目标文件到可执行文件的转换,以及程序运行时的内存布局和行为。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
进程是程序在计算机上的一次动态执行过程,是操作系统进行资源分配和调度的基本单位。它包含了程序运行所需的各种资源,如代码段、数据段、堆栈等,以及程序的执行状态信息,例如程序计数器(PC)的值、寄存器的值等。进程的作用在于使得程序能够被操作系统有效地管理和调度,从而实现多任务的并发执行。通过创建多个进程,用户可以同时运行多个程序,提高了计算机资源的利用率和系统的整体性能。每个进程在运行过程中都相对独立,拥有自己的地址空间和资源,这使得它们之间不会相互干扰,保证了程序运行的稳定性和安全性。
6.2 简述壳Shell-bash的作用与处理流程
Shell-bash作为用户与操作系统之间的交互界面,承担着命令解析、程序执行以及进程控制等多种重要功能。当用户在终端输入命令时,bash首先会解析这些命令,判断其是否为内置命令。如果是内置命令,bash会直接在当前进程中执行;若为外部命令,bash会通过fork创建一个子进程。子进程随后会调用execve函数,将外部命令对应的程序加载到内存中并开始执行。在程序执行过程中,bash会持续监控子进程的状态,当子进程执行完毕后,bash会回收该子进程,清理相关资源,然后等待用户输入下一个命令,继续上述处理流程。此外,bash还支持作业控制功能,允许用户在后台运行程序、暂停和恢复程序的执行等。
6.3 hello的fork进程创建过程
当用户在bash终端中输入hello程序的执行命令后,bash会通过调用fork系统调用创建一个新的子进程。在fork过程中,操作系统会为子进程分配一个唯一的进程标识符(PID),并复制父进程的大部分资源,包括代码段、数据段、堆栈等,但实际的物理内存页面并不会立即复制,而是采用写时复制(Copy-On-Write,COW)机制。这意味着父子进程在创建初期共享相同的物理内存页面,只有当某个进程试图修改页面内容时,操作系统才会为该进程创建一个新的内存页面副本,从而避免了不必要的内存浪费。同时,子进程会继承父进程的文件描述符、环境变量等资源,但子进程的程序计数器(PC)会被设置为fork系统调用的返回点,以便子进程能够继续执行后续的指令。在hello程序的场景中,子进程创建完成后,会继续执行execve系统调用,加载hello程序到内存中并开始执行。
6.4 hello的execve过程
在hello程序的子进程通过fork创建完成后,紧接着会调用execve系统调用。execve的主要作用是将hello程序的可执行文件加载到子进程的内存空间中,并替换掉子进程原有的代码段、数据段、堆栈等资源,从而使得子进程能够开始执行hello程序。当execve被调用时,操作系统会首先检查可执行文件的合法性,包括文件格式是否正确、是否有执行权限等。如果检查通过,操作系统会根据可执行文件中的信息,将程序的代码段、数据段等加载到子进程的内存中,并初始化堆栈,设置程序的入口点等。随后,操作系统会将子进程的程序计数器(PC)设置为程序的入口地址,子进程便开始执行hello程序。在hello程序执行过程中,操作系统会按照进程调度算法为hello进程分配CPU时间片,使得hello程序能够在多任务环境中与其他进程并发运行。
6.5 hello的进程执行
hello程序的执行过程是一个典型的用户态与内核态不断切换的过程。在程序运行期间,操作系统会按照一定的调度算法为hello进程分配CPU时间片。当hello进程获得CPU时间片后,它会在用户态开始执行程序代码。在用户态下,hello进程主要负责执行程序的逻辑运算、数据处理等操作。然而,在程序执行过程中,hello进程可能会遇到一些需要操作系统介入的情况,例如需要进行文件读写操作、请求更多的内存资源等。此时,hello进程会通过系统调用陷入内核态。在内核态下,操作系统会处理hello进程的请求,例如为进程分配内存、执行文件读写操作等。当操作系统完成请求处理后,hello进程会从内核态返回到用户态,继续执行程序代码。此外,在hello进程的执行过程中,还可能会因为时间片用完、更高优先级的进程抢占CPU等原因而暂停执行,等待下一次被调度。当hello程序执行完毕后,操作系统会回收hello进程占用的资源,包括内存、文件描述符等,完成hello进程的生命周期。
6.6 hello的异常与信号处理
运行中可能产生中断、陷阱、故障、中止。
可能产生SIGINT, SIGKILL, SIGTSTP等信号。具体处理见下。
(1)正常运行./hello 2023111990 袁骋 18567806985 3
正常每隔3秒重复10次内容,然后等待一个字符输入后程序终止。
图 31 hello正常运行
(2)乱按键盘(有回车)
可以发现程序运行中乱按键盘并不会造成程序停止,但是在程序结束后,第二个回车前打的字符被输入到了Shell中,这说明键盘的输入被暂存在缓冲区。(第一个回车前的字符都给hello里的getchar()了,不然这个程序也不会中止)
图 32 hello运行中乱按键盘
(3)Ctrl+Z(SIGTSTP)
进程被挂起,通过ps可以看到进程Stopped,通过fg恢复运行。
图 33 hello运行中按Ctrl+Z
图 34 hello挂起后输入ps命令
图 35 hello挂起后输入jobs命令
图 36 hello挂起后输入pstree命令(截图不全)
图 37 hello挂起后输入fg命令
(4)kill命令
kill -9 3014359向hello进程发送SIGKILL,所以进程被杀死。
图 38 hello挂起后输入kill命令
(5)Ctrl+C(SIGINT)
按下Ctrl+C,hello收到SIGINT信号,默认是终止程序,因此hello被终止。使用ps验证,看不到hello进程存在。
图 39 hello运行中按Ctrl+C
6.7本章小结
本章深入探讨hello程序的进程管理机制,包括进程的创建、执行、调度以及异常与信号处理。通过分析系统调用,揭示了进程从启动到终止的全过程,展现了操作系统对进程的精细管控与高效调度。
(第6章2分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址是程序中直接使用的地址,由段选择符和段内偏移组成。
线性地址是逻辑地址经过段式管理转换后得到的地址,它在现代操作系统中通常与虚拟地址相同。
虚拟地址是线性地址经过页式管理转换后得到的地址,它允许程序访问比物理内存更大的地址空间。
物理地址则是虚拟地址最终映射到的内存地址,由硬件管理单元(MMU)负责完成从虚拟地址到物理地址的转换。
hello程序在运行过程中,操作系统通过这些地址空间的转换机制,确保程序能够高效地访问内存资源,同时提供内存保护和隔离功能。
7.2 Intel逻辑地址到线性地址的变换-段式管理
段式管理将内存划分为多个段,每个段由一个段描述符定义,包含段的起始地址、长度和访问权限等信息。段描述符存储在全局描述符表(GDT)或局部描述符表(LDT)中。当hello程序运行时,处理器根据段选择符从GDT或LDT中获取段描述符,将逻辑地址中的段内偏移与段描述符中的段基址相加,得到线性地址。
7.3 hello的线性地址到物理地址的变换-页式管理
页式管理将线性地址空间划分为固定大小的页面,每个页面映射到物理内存中的一个页面帧。页表是实现这种映射的关键数据结构,它记录了页面到页面帧的映射关系。hello程序运行时,操作系统负责维护页表,并通过硬件的内存管理单元(MMU)完成从线性地址到物理地址的转换。当访问一个线性地址时,MMU会查找页表,找到对应的页面帧,从而得到物理地址。如果页表中没有找到对应的映射,就会触发缺页中断,操作系统会处理缺页中断,将所需的页面加载到物理内存中,并更新页表。
7.4 TLB与四级页表支持下的VA到PA的变换
TLB是一个高速缓存,用于存储最近访问的页表项,从而减少对页表的访问次数。在多级页表结构中,TLB可以显著提高地址转换的速度。hello程序运行时,处理器首先在TLB中查找线性地址到物理地址的映射。如果TLB中没有命中,才会访问页表。
在现代操作系统中,通常使用四级页表结构,这种结构可以支持更大的地址空间,同时减少页表的大小。
hello程序的虚拟地址通过四级页表的逐级查找,最终映射到物理地址。TLB的存在使得这个过程更加高效,减少了地址转换的延迟。
7.5 三级Cache支持下的物理内存访问
现代处理器通过三级缓存(L1、L2、L3)来提高内存访问速度。L1缓存是最快的,但容量较小,通常集成在处理器核心内部。L2和L3缓存的容量更大,速度相对较慢,但仍然比主内存快得多。当hello程序访问物理内存时,处理器首先在L1缓存中查找所需的数据。如果L1缓存中没有命中,就会依次访问L2和L3缓存。如果三级缓存中都没有找到所需的数据,才会访问主内存。这种缓存层次结构可以显著减少内存访问的延迟,提高程序的运行效率。
7.6 hello进程fork时的内存映射
当hello程序通过fork创建子进程时,操作系统会为子进程创建一个新的地址空间。这个地址空间是父进程地址空间的副本,但实际的物理内存页面并不会立即复制,而是采用写时复制机制。
这意味着父子进程在创建初期共享相同的物理内存页面,只有当某个进程试图修改页面内容时,操作系统才会为该进程创建一个新的内存页面副本。这种机制可以显著减少内存的使用量,提高进程创建的效率。在hello程序的场景中,fork操作后,子进程会继承父进程的代码段、数据段、堆栈等资源,但每个进程都有自己的独立地址空间。
7.7 hello进程execve时的内存映射
当hello程序的子进程调用execve加载新的可执行文件时,操作系统会替换掉子进程的代码段、数据段和堆栈等资源。这个过程涉及多个步骤:首先,操作系统会释放子进程当前的内存映射;然后,根据新的可执行文件的ELF格式信息,重新分配内存,并将代码段、数据段等加载到新的内存区域;最后,初始化堆栈,并设置程序的入口点。
7.8 缺页故障与缺页中断处理
缺页故障:访问的页面不在物理内存中,需要从磁盘或其他存储设备中加载。
当发生缺页故障时,处理器会触发缺页中断,操作系统会处理这个中断,将所需的页面加载到物理内存中,并更新页表。缺页中断的处理过程包括查找页面的存储位置、分配物理内存页面、将页面内容从磁盘加载到物理内存、更新页表以及恢复程序的执行。hello程序在运行过程中,如果访问了未加载到内存中的页面,就会触发缺页中断,操作系统会通过上述步骤处理缺页故障,确保程序能够继续运行。
7.9动态存储分配管理
动态内存分配允许程序在运行时根据需要分配和释放内存。
操作系统通过堆管理器来实现动态内存分配,堆管理器负责维护一个堆区,程序可以从堆区中分配内存块。当程序调用malloc时,堆管理器会从堆区中分配一个合适大小的内存块,并返回其地址。当程序不再需要这块内存时,可以通过free函数将其释放回堆区。
Printf会调用malloc,请简述动态内存管理的基本方法与策略。(此节课堂没有讲授,选做,不算分)
7.10本章小结
本章详细探讨了hello程序的存储管理机制的多个方面。通过这些机制,操作系统为hello程序提供了高效的存储管理功能,确保了hello的顺利运行和资源的有效利用。
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
(以下格式自行编排,编辑时删除)
设备的模型化:文件
设备管理:unix io接口
8.2 简述Unix IO接口及其函数
(以下格式自行编排,编辑时删除)
8.3 printf的实现分析
(以下格式自行编排,编辑时删除)
[转]printf 函数实现的深入剖析 - Pianistx - 博客园
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall等.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
(以下格式自行编排,编辑时删除)
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
(以下格式自行编排,编辑时删除)
(第8章 选做 0分)
结论
通过以上各个阶段,hello程序从源代码到可执行文件,再到运行中的进程,最后被回收,经历了一系列复杂的转换和管理过程。在此总结hello一生所经历的过程。
1.编辑与预处理阶段
hello的旅程始于源代码的编写,程序员将代码保存为hello.c文件。预处理器对源代码进行处理,展开头文件、宏定义,删除注释,并添加行号和文件名标记,生成预处理文件hello.i。这一阶段为后续编译工作准备了纯净的C语言代码。
2.编译阶段
编译器将预处理后的hello.i文件转换为汇编语言代码hello.s。编译过程包括词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成。编译器将高级语言逻辑转换为具体的汇编指令,为后续的汇编和链接工作奠定了基础。
3.汇编阶段
汇编器将hello.s文件中的汇编代码翻译为机器语言代码,生成可重定位目标文件hello.o。汇编器解析汇编指令,生成机器码,并为代码中的符号生成符号表和重定位信息。这一阶段将人类可读的汇编代码转换为计算机可执行的机器码。
4.链接阶段
链接器将hello.o与标准库文件(如libc.so)合并,解析符号引用,分配地址,完成重定位,最终生成可执行文件hello。链接过程是程序从目标文件到可执行文件的关键转换,使得多个模块能够组合成一个完整的程序。
5.加载与运行阶段
当用户在Shell中运行hello时,操作系统通过fork创建子进程,子进程调用execve将hello加载到内存中。程序加载后,控制流转移到入口点_start,经过初始化后调用main函数。程序在运行过程中,操作系统管理其内存、调度CPU时间片,并处理异常和信号。
6.存储管理与内存映射
hello程序运行时,操作系统通过段式和页式管理机制管理其地址空间,从逻辑地址到线性地址,再到虚拟地址和物理地址的转换,确保程序高效访问内存。同时,TLB和多级页表加速了地址转换过程,三级缓存提高了内存访问效率。
7.I/O
hello与外界输入输出交互。
8.回收阶段
当hello程序执行完毕并退出时,操作系统负责回收其占用的资源。这包括关闭程序打开的文件描述符,释放分配的内存(包括堆和栈空间),清理进程的地址空间,以及回收进程的PID等资源。至此,hello的一生(暂时)结束了,直到下一次加载。
通过本课程学习,以及对hello从编写到运行的全过程的深入分析,我对计算机系统的设计与实现有了更深刻的理解。计算机系统是一个复杂而精妙的协同工作体系,从硬件到软件、从编译器到操作系统,每一部分都扮演着不可或缺的角色。编译器将高级语言转化为机器可执行的代码,优化程序性能;操作系统负责资源分配与管理,确保程序稳定运行。
随着硬件技术的不断进步和软件需求的日益复杂,计算机系统的设计与实现将面临更多挑战。例如,多核处理器的普及要求操作系统和编译器更好地支持并行计算;云计算和大数据技术的发展则对存储管理和数据交换提出了更高的要求。面对这些挑战,我们需要不断探索新的设计与实现方法,比如引入人工智能技术优化系统资源分配。
(结论0分,缺失-1分)
附件
hello.i
预处理后的文件,通过GCC的-E选项生成。该文件包含了展开的头文件、宏定义以及删除的注释,为编译阶段提供了干净的输入。
hello.s
编译阶段生成的汇编代码文件,通过GCC的-S选项生成。该文件包含了程序的汇编指令,反映了C代码到汇编指令的转换过程。
hello.o
汇编阶段生成的目标文件,通过GCC的-c选项生成。该文件包含了机器指令和符号表,是链接阶段的输入。
hello
链接阶段生成的可执行文件,ld生成。
hello.dis.txt
反汇编输出文件,通过objdump工具生成。该文件包含了hello文件的反汇编代码,用于分析汇编指令和机器码之间的关系。
hello.elf.txt
ELF文件格式分析输出,通过readelf工具生成。该文件包含了可执行文件hello的ELF格式信息,用于分析程序的加载和链接过程。
hello.o.dis.txt
目标文件hello.o的反汇编输出文件,通过objdump工具生成。该文件用于分析目标文件中的汇编指令和符号信息。
(附件0分,缺失 -1分)
参考文献
[1] Bryant, Randal E., & O'Hallaron, D. (2016). Computer Systems: A Programmer's Perspective (3rd ed.). Pearson.
[2] 奇小葩. (2021). 进程管理(一)--进程管理的基本概念-CSDN博客. https://blog.csdn.net/u012489236/article/details/115423024
[3] 闫晟. (2020)逻辑地址、物理地址、虚拟地址_虚拟地址 逻辑地址-CSDN博客[EB/OL]. https://blog.csdn.net/TYUTyansheng/article/details/108148566
(参考文献0分,缺失 -1分)