
计算机系统
大作业
题 目 程序人生Hello’s P2P
专 业 电子通信
学 号 2023112330
班 级 23L0505
学 生 申天柱
指 导 教 师 刘宏伟
计算机科学与技术学院
2024年5月
本论文以hello程序运行过程为主,介绍了hello的预处理、编译、汇编、链接,以及hello在运行过程中是如何接受操作系统管理以及如何使用硬件完成数据交换,进而洞悉程序生命周期、进程管理、内存层次、状态机哲学的计算机系统美学。本论文成果在于探清了hello作为一个普遍进程是如何运行的,实际意义在于对计算机系统以及组成有了更加深入的理解,为今后更加深入的研究打好基础。
关键词:hello程序,Linux系统,程序管理,计算机系统
(摘要0分,缺失1分,根据内容精彩称都酌情加分01分)
目 录
第1章 概述
1.1 Hello简介
P2P描述静态程序(Program)如何转化为动态进程(Process),体现计算资源的动态调度。GCC编译器驱动程序读取源文件hello.c再经过cpp(预处理器)预处理(对开头语句修改,插入头文件,预处理宏定义)进行修改生成hello.i;hello.i传入ccl(编译器)生成汇编程序hello.s;hello.s经过as(汇编器)生成可重定位目标程序hello.o;hello.o最终通过ld(链接器)完成静/动态链接后,将相关库函数(printf等)写入ELF文件,生成可执行文件hello。在shell中输入./hello后调用fork以及execve函数加载运行。
020描述系统从初始空状态(Zero)经过事务处理后回归零状态,强调原子性与状态完整性。如果说P2P模式对应软件构造课程中build阶段,那么020则对应run阶段。第一个0指的是在./hello之前,内存中本没有hello的数据信息,在shell的fork以及execve相互作用下加载器把hello的代码以及数据载入内存,mmap则分配虚拟内存,在cache、流水线等硬件配合下执行代码直至结束。第二个0指的是在程序结束后,hello进程会被回收,内核删除内存中的相关代码和数据,“抹杀”了hello曾经来过的痕迹,实现归零(to zero)。当hello经过了P2P的过程后,成为了一个完整的程序,在程序执行完成后,hello进程被回收,与hello相关的所有状态信息与数据被清除,即“to Zero
1.2 环境与工具
硬件:13th Gen Intel(R) Core(TM) i913900HX 2.20 GHz ;RAM:16GB
软件:window 11 64位;MobaXterm 25.0 ;Ubuntu 24.04.1 ;Codeblock 20.03 ; cpp ; gcc; as ; ldreadelf ; gdb;edb
1.3 中间结果
hello.c源文件
hello.i预处理文件
hello.s汇编文件
hello.o可重定位目标文件
hello目标文件
hello.elf 目标文件的ELF格式文件
hello.asm目标文件的反汇编文件
1.4 本章小结
本章简述了hello 的P2P和020 过程,介绍了完成大作业的软硬件环境和开发调试工具,列举了过程中生成的中间文件和其作用
第2章 预处理
2.1 预处理的概念与作用
2.1.1预处理概念
预处理是编译过程的第一步,由预处理器执行,负责在正式编译前对源代码进行文本级处理。它不分析语法或语义,而是简单地对源代码进行文本替换、文件包含、条件编译等操作,生成一个“预处理后的源代码”(通常以 .i 或 .ii 为扩展名),供后续编译阶段使用。
2.1.2预处理的作用
1.宏定义:预处理器会根据源文件中定义的宏进行定义。通过#define定义的宏会在预处理阶段被展开,如定义常量、函数式宏,实现代码复用和简化。
2.头文件包含:预处理器会处理#include指令,将指定的头文件内容插入到源文件中。
3.条件编译:预处理器会根据条件编译指令(如#if、#ifdef、#ifndef、#elif、#else和#endif)来控制编译过程中代码的包含与排除。
4.处理特殊符号:预处理器会移除源文件中的注释,包括//和/ /形式的注释,以便后续编译器处理。
5、编译器指令:向编译器传递特殊控制命令(如优化选项、警告抑制)。
2.2在Ubuntu下预处理的命令
将hello.c文件通过gcc hello.c o hello.i处理,得到hello.i

2.3 Hello的预处理结果解析
通过gcc E hello.c o hello.i来查看hello的预处理结果。以下为部分内容截图:


2.4 本章小结
2.4.1预处理操作
- 去掉注释;
- 对stdio.h、unistd.h、stdlib.h等头文件进行展开,删去头文件引用内容,将main 函数放在文件末尾。
2.4.2总结
本章介绍了预处理的概念和作用和Ubantu下的预处理指令,并将hello.c源程序内容和预处理得到的hello.i程序内容进行对比,结合实例分析了预处理阶段的过程及作用。
3.1编译的概念与作用
3.1.1编译的概念
编译是指编译器(cc1)将预处理后的源代码.i文件转换为汇编代码.s的过程,它包含一个汇编语言程序。主要完成对词法、语法、语义的分析,代码优化,以及最后的生成目标平台的汇编程序。
3.1.2编译的作用
编译可以实现跨平台适配,将高级语言转化为与硬件架构相关的汇编代码,使得程序能够在不同的CPU上运行;编译可以实现代码优化,通过编译器的优化能够有效提升代码性能;编译可以实现错误检查,在编译阶段暴露语法错误,避免运行时崩溃;编译可以为汇编器提供输入,生成的.s文件是人类可读的汇编代码,便于调试、修改,了解编译器优化的内容等。
3.2 在Ubuntu下编译的命令
用gcc S hello.i o hello.s 来将hello.i转化为hello.s

3.3 Hello的编译结果解析
Hello.s部分内容如下:

3.3.1常量

这里定义了两个字符串常量,分别为"用法: Hello 学号 姓名 手机号 秒数!\n"和"Hello %s %s %s\n"
Hello.s中还有很多立即数常量,如:9

3.3.2变量
局部变量用4(%rbp)来展示,如:

3.3.3表达式
对于原C语言中的表达式argc!=5,在.s文件中表示为:

类似的还有:

3.3.4赋值
i=0的赋值如下:

将参数的值从寄存器移到栈上:

3.3.5算数操作
程序中的加法操作体现在每次循环对i进行加一操作,.s文件中该操作如下:

3.3.6数组、指针
main函数中使用了argv数组,需要依次访问argv[1]/argv[2]/argv[3]/argv[4]

3.3.7传参
puts传参:

exit传参:

printf传参:

还有其他传参方式,在此不一一列举。
3.3.8函数调用
结合前文所述,从.s中观察,调用所有函数如下puts、exit、printf、atoi、sleep、getchar。其中getchar和sleep调用如下:

3.3.9函数返回
ret 指令用于将程序的控制权返回到调用该函数的位置,.cfi_endproc 表示函数返回:

atoi函数调用返回值在%eax中传入%edi(sleep的参数):

3.4 本章小结
本章针对汇编程序.s的概念和作用进行了系统的分析,通过与原始hello.c文件进行对比,细致地分析了C语言中数据和操作是如何在汇编语言中实现的。对汇编语言中各种指令、寄存器、内存的读写、函数调用等有了更深的认识。
第4章 汇编
4.1 汇编的概念与作用
4.4.1 汇编语言的基本概念
汇编语言是一种低级编程语言,与计算机硬件架构(如x86、ARM、MIPS等)直接相关。它使用助记符(如`mov`,`add`,`jmp`)来表示机器指令,比二进制机器码(如`0x89 0xD8`)更易读,但仍接近硬件层。
关键特点:
与硬件直接交互:直接操作CPU寄存器、内存地址、I/O端口等。
一对一映射:每条汇编指令通常对应一条机器指令(反汇编时可能不完全还原)。
平台相关:不同CPU架构(x86、ARM等)的汇编语法不同。
4.1.2 汇编语言的作用
(1)底层程序开发
操作系统内核:如Linux内核中的关键部分(上下文切换、中断处理)。
驱动程序:直接控制硬件设备(如显卡、网卡)。
嵌入式系统:资源受限的设备(单片机、传感器)常需汇编优化。
(2)性能优化
关键代码优化:对性能敏感的代码(如游戏引擎、加密算法)可用汇编手动优化。
编译器生成代码:编译器(如GCC)会将高级语言(C/C++)编译为汇编,再转为机器码。
(3)逆向工程与安全
分析恶意软件:反汇编可研究病毒/木马的行为。
漏洞挖掘:通过汇编代码查找缓冲区溢出等漏洞。
破解与调试:修改程序逻辑(如游戏外挂、软件破解)。
(4)教学与理解计算机原理
理解计算机如何工作:学习寄存器、内存管理、指令执行周期。
编译器原理:了解高级语言如何转换为底层指令。
4.2 在Ubuntu下汇编的命令
利用as hello.s o hello.o来得到hello.o:

4.3 可重定位目标elf格式
4.3.1文件头:
用readelf h hello.o来查看头文件

4.3.2 头部表
用readelf S hello.o来查看节头部表

4.3.3符号表
用readelf s hello.o 来查看符号表:

4.4 Hello.o的结果解析

上图是用objudmp d hello.o来查看反汇编表。
图像说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。代码末尾指令基本相同,.s是由汇编语言组成的,而反汇编得到的代码既有汇编代码也有机器语言代码,即多包含了二进制编码。例如当进行分支转移时,.s文件是跳转代码段,例如L1、L2等,反汇编文件中是直接跳转到当前地址加上偏移量得到目标代码地址。在函数调用时,.s文件的call指令后面跟函数名字,而反汇编文件是函数名和函数地址的信息,跳转到重定位条目所指示的目标地址。在数据访问时,汇编语言会使用[array]变量名,而反汇编文件使用二进制内存地址。
4.5 本章小结
本章通过将.s转换为.o文件,分析了汇编的概念及其作用。通过查看可重定位ELF格式,对不同的节、ELF头、节头部表等都有了更深的理解。最后将.o文件利用objdump反汇编得到反汇编文件,对比分析.s文件与反汇编文件,更能发现重定位对程序的作用和影响,了解到汇编语言和机器语言之间的映射关系。
第5章 链接
5.1 链接的概念与作用
5.1.1.链接的基本概念
链接是程序构建过程中的关键步骤,负责将多个目标文件(.o)和库文件(.a/.so)合并,生成最终的可执行文件或共享库。
链接器、的主要任务包括:
1、符号解析:确定所有符号(函数、变量)的地址。
2、重定位:修正代码和数据中的相对地址,使其指向正确的内存位置。
5.1.2.链接的作用
(1)合并多个目标文件
将分散编译的`.o`文件(如`main.o`、`utils.o`)合并为一个可执行文件。
(2)解析外部依赖
解决跨文件的符号引用(如`main.o`调用`utils.o`中的函数)。
若符号未定义,链接器会报错(如`undefined reference to'func'`)。
(3)链接静态库或动态库
静态库(.a):代码直接嵌入可执行文件。
动态库(.so):运行时加载,减少磁盘和内存占用。
(4)地址分配与重定位
为代码和数据分配最终的内存地址。
修正目标文件中的相对地址(如跳转指令、全局变量引用)。
(5)优化与符号处理
移除未使用的代码(GCC的`gcsections`选项)。
控制符号的可见性(如隐藏内部符号)。
5.2 在Ubuntu下链接的命令
在Ubuntu系统下,链接的命令为:
ldo hellodynamiclinker/lib64/ldlinuxx8664.so.2/usr/lib/x86_64linuxgnu/crt1.o/usr/lib/x86_64linuxgnu/crti.o hello.o/usr/lib/x86_64linuxgnu/libc.so/usr/lib/x86_64linuxgnu/crtn.o

5.3 可执行目标文件hello的格式
5.3.1ELF头
用readelf a hello:

5.3.2节头表
部分内容截图:

5.4 hello的虚拟地址空间
5.4.1 edb加载hello
使用edb加载hello,得到hello进程虚拟地址空间的各段信息:

5.4.2对比
为了与5.3对比分析,我们选择.data节,从5.3可知.data段的偏移为3048,虚拟地址为00404048,我们在edb中找到对应的虚拟地址,可以找到与.data段有关的数据信息在右侧栏中。

5.5 链接的重定位过程分析
5.5.1反汇编指令

5.5.2反汇编结果
反汇编结果部分如下:

不同之处在于,本次反汇编文本中新增了_init、getchar、printf等函数,新增了.init节、.plt节、.plt.sec节。且通过call进行函数调用时直接显示函数的绝对地址和函数名,没有关于重定位类型和偏移量的说明。在反汇编文件中,每行指令都有唯一的虚拟地址,但是hello.o的反汇编只有偏移地址,原因是hello已经经过链接器链接后完成了重定位,每条指令的虚拟地址都被确定。链接首先要求链接器扫描所有的.o文件,建立全局符号表,检查未定义符号是否有定义。接着对节进行合并,合并到最终的可执行文件,修改代码中的临时地址为实际运行时的地址(利用绝对地址重定位和相对地址重定位)。
5.6 hello的执行流程
以下为hello的执行流程
hello!_init地址为0x0000000000401000
hello!start地址为0x0000000000401100
hello!main地址为0x0000000000401125
hello!_fini地址为0x0000000000401140

5.7 Hello的动态链接分析

5.7.1静态链表
在进行动态链接前首先需要进行静态链接,生成部分链接的可执行文件hello,如果要分析动态链接项目的变化,可以查看动态链接前后的GOT/PLT变化,由前文节头表可知,.got.plt位于00404000处。利用edb查看动态链接前的段内容如下:

5.7.2动态链表
在动态链接前,.got.plt中的函数地址条目初始指向PLT中对应的指令序列,而.plt条目则被设计为首次调用时触发动态链接器解析。当程序首次调用共享库函数时,控制流通过PLT跳转到GOT中的初始地址,这会触发动态链接器(通过PLT[0])解析实际函数地址,并将结果回填到对应的GOT条目中。所以动态链接前后.got.plt的内容发生了变化,这种延迟绑定保证了位置无关代码的灵活性,又最小化了运行时性能损耗。

5.8 本章小结
本章节简要介绍了链接的相关过程,首先简要阐述了链接的概念和作用,给出了链接在Ubuntu系统下的指令。之后研究了可执行目标文件hello的ELF格式,之后依据重定位条目分析了重定位的过程,并借助edb调试工具,研究了程序中各个子程序的执行流程,最后则借助edb调试工具通过对虚拟内存的查取,分析研究了动态链接的过程。
第6章 hello进程管理
6.1 进程的概念与作用
6.6.1.进程的概念
定义:进程是正在执行的程序实例,是操作系统进行资源分配和调度的基本单位。它不仅包含程序的代码(文本段),还包括运行时的状态(如寄存器值、内存、打开的文件等)。
关键特征:
动态性:进程有生命周期(创建、运行、终止)。
独立性:每个进程拥有独立的地址空间和系统资源(如文件描述符)。
并发性:多个进程可以交替执行(单核CPU)或并行执行(多核CPU)。
6.1.2.进程的作用
(1)实现多任务并发
并发执行:操作系统通过进程调度(如时间片轮转)让多个进程交替运行,用户感知为“同时”运行(如边听音乐边浏览网页)。
隔离性:一个进程崩溃通常不会影响其他进程,保障系统稳定性。
(2)资源分配与管理
资源单位:进程是操作系统分配资源(CPU时间、内存、I/O设备)的基本实体。
例如:每个进程拥有独立的虚拟地址空间,由操作系统映射到物理内存。
(3)程序执行的载体
动态执行:程序是静态的(如硬盘上的可执行文件),进程是程序被加载到内存后的动态实例。
例如:多次运行同一个程序会创建多个独立的进程。
6.2 简述壳Shellbash的作用与处理流程
6.2.1.Shell的作用
Shell是用户与操作系统内核(Kernel)之间的命令行接口,主要作用包括:
命令解释与执行:解析用户输入的命令,调用系统内核或程序执行。
脚本编程:支持编写脚本(Shell Script)自动化任务。
环境管理:管理环境变量、工作目录、进程等。
输入/输出重定向:控制命令的输入来源(如文件)和输出目标(如文件或管道)。
作业控制:管理前台/后台进程(如`&`、`jobs`、`fg`)。
6.2.2.Shell的处理流程
当用户在终端输入命令并按下回车时,Bash的处理流程如下:
(1)读取输入:从标准输入(键盘)或脚本文件读取命令字符串。
示例:输入`ls l/tmp`。
(2)解析命令:分词(Tokenize):按空格分割命令为单词(`ls`、`l`、`/tmp`)。
(3)执行命令:内置命令(如`cd`、`echo`):由Shell自身直接处理。外部程序(如`ls`、`grep`):
1.查找路径:通过`$PATH`环境变量搜索可执行文件。
2.创建子进程:通过`fork()`复制当前Shell进程。
3.加载程序:子进程调用`exec()`加载目标程序(如`/bin/ls`)。
4.等待结果:父进程(Shell)默认等待子进程结束(可通过`&`转为后台运行)。
(4)输出结果:将命令的标准输出(stdout)或错误(stderr)显示到终端或重定向到文件。
(5)返回状态码:命令执行后返回退出状态码(`$?`):
`0`表示成功,非`0`表示错误(如`1`为一般错误)。
6.3 Hello的fork进程创建过程
在命令行输入./hello命令运行hello程序,此命令不是内置命令,因此Bash通过调用fork函数创建一个新的运行的子进程。新创建的子进程几乎但不完全与父进程相同,子进程得到父进程用户级虚拟地址空间相同的(但是独立的)一份副本,包括代码和数据段、堆、共享库以及用户栈。子进程还获得与父进程任何打开文件描述符相同的副本,也就意味着父进程调用fork函数时,子进程能够读写父进程打开的任何文件。父进程和新创建的子进程之间最大的区别在于其不同的PID。
6.4 Hello的execve过程
execve函数在当前进程的上下文中加载并运行一个新程序。execve函数加载并运行可执行目标文件filename,且带参数列表argv和环境变量envp。只有当出现错误时,例如找不到filename,execve才会返回到调用程序。execve函数一次调用而从不返回。
6.5 Hello的进程执行
当初始化堆栈和程序计数器指向main入口后,进程调度器会根据时间片轮转算法,将新创建的hello进程加入到就绪队列中等待CPU调度。当hello进程获得CPU时间片开始执行。每个进程都用自己的上下文信息,包括寄存器状态、进程控制块等,是独立的副本。当进程hello获得时间片时,调度器会保存当前进程的上下文,加载下一个运行进程hello的上下文。当进程在执行用户代码时处在用户态,如果进程调用系统函数时,会从用户态转换为核心态。当调用结束后又会触发中断返回用户态。当时间片结束或者I/O发生时,中断会触发从用户态向核心态的转换,由操作系统进行内存调度。
当hello程序执行完毕后,进程调用exit()函数,通知操作系统进程已经结束。从用户态切换到核心态,操作系统清理子进程部分资源,向父进程(shell)发送子进程终止信号,父进程对僵死进程进行回收
6.6 hello的异常与信号处理
6.6.1正常运行

6.6.2乱按

6.7本章小结
本章主要分析如何管理hello进程,包括进程的概念、作用,shell(bash)的作用和执行流程,hello的fork和execve过程,以及hello进程的执行过程。详细分析了进程执行过程中可能会产生的异常和信号,通过实际操作观察不同信号的处理机制,对不同shell命令,hello的响应也大不相同
第7章 hello的存储管理
7.1 hello的存储器地址空间
7.1.1逻辑地址
定义:程序代码中直接使用的地址(如`hello`中的变量地址、函数指针)。
特点:由段选择符(Segment Selector)+偏移量(Offset)组成(x86架构)。
在Linux中,段基址通常为`0`,因此逻辑地址直接等于线性地址。
7.1.2线性地址
线性地址跟逻辑地址类似,也是一个不真实的地址,是由逻辑地址经过分段机制转换后的32/64位连续地址空间,线性地址=段基地址+段偏移地址
7.1.3物理地址
定义:实际DRAM内存芯片上的地址,由MMU(内存管理单元)通过页表转换得到。
特点:对进程透明,只有内核和MMU知道物理地址。
不同进程的虚拟地址可能映射到同一物理地址(共享库、内存共享)。
7.1.4虚拟地址
在分页机制启用时与线性地址等价,是操作系统和开发者视角的同一地址空间。当一个程序运行时,变量和函数的地址都是在虚拟地址空间中的位置,虚拟地址空间是操作系统为程序分配的独有区域。
7.2 Intel逻辑地址到线性地址的变换段式管理
在 Intel x86 架构中,逻辑地址到线性地址的变换涉及到段式管理机制。这个过程有以下步骤:
1.逻辑地址的生成:
2.逻辑地址通常由 CPU 中的段寄存器和偏移量组成。在 x86 架构中,有四个段寄存器:CS(代码段)、DS(数据段)、SS(堆栈段)和ES(附加数据段)。偏移量表示在指定段内的位置。
3.段选择符解析:
4.CPU 使用逻辑地址中的段选择符从全局描述符表(GDT)或局部描述符表(LDT)中获取相应的段描述符。段选择符包含了段描述符在描述符表中的索引。
5.段描述符解析:
6.从描述符表中获取到的段描述符包含了段的基址和长度等信息。段基址是一个32位的线性地址,用于指示段在内存中的起始位置。段长度则决定了段的大小。
7.线性地址的计算:
8.将段基址与偏移量相加,得到线性地址。这个线性地址是一个32位地址,用于访问物理内存。
9.地址转换和访问:
10.当 CPU 访问内存时,将逻辑地址转换为线性地址。这个线性地址通过内存管理单元(MMU)进行转换,最终映射到物理内存中的相应位置。
这个过程中,段式管理机制允许操作系统将程序的地址空间划分为多个段,每个段可以具有不同的访问权限和大小。这样可以更灵活地管理内存,并提供更好的安全性和隔离性
7.3 Hello的线性地址到物理地址的变换页式管理
这里的线性地址可以视作虚拟地址,hello程序的线性地址到物理地址转换是通过多级页表机制完成的,分页机制将线性地址分解为虚拟页面偏移量和虚拟页号,根据虚拟页面偏移量先查快表TLB,TLB命中则取出物理页号和虚拟页面偏移量连接成物理地址。如果快表TLB不命中,则查询四级页表,找出物理页号。如果页表不命中就需要从磁盘写入物理内存,更新页表,重复上面的动作。
7.4 TLB与四级页表支持下的VA到PA的变换
7.4.1TLB
TLB是快表,是一个小的、虚拟内存的缓存。查询快表时间极短,甚至可以忽略不计。其中每一行保存着一个由单个PTE组成的块。当处理器给出虚拟地址后,先查询快表中是否有对应目标,如果命中则和虚拟页面偏移链接成物理地址。
7.4.2四级页表
单级页表占用内存十分严重,所以采用层次结构的页表来压缩页表,一级页表中每个PTE负责映射虚拟地址空间中一个4MB的片,每个片都是由连续的页面组成。如果片i中每个页面都没分配,那么一级PTE i就为空。对于四级页表,虚拟地址被划分为4个VPN和1个VPO。第四级页表中的每个PTE包括着某个物理页面的PPN,或者一个磁盘块的地址。为了构造物理地址,在能够确定PPN之前,MMU必须访问4个PTE,PPO和VPO相同。
7.4.3转换过程
CPU首先在TLB中查找虚拟地址的映射,如果命中则直接使用缓存的物理地址,如果未命中,则需要进行页表查找。
利用VPN1在一级页表中查找,得到的结果加上VPN1*8在二级页表中查找,如此下去最终在第四级中查找到PPN,将PPN与PPO相连得到物理地址。
物理地址=物理页号+物理页面偏移量
7.5 三级Cache支持下的物理内存访问
三级缓存是一种采用多级缓存的存储体系,可以提高计算机内存访问速度和效率。在三级缓存的架构中,缓存分为L1、L2和L3三级,每一级缓存都有不同的容量和访问速度。
当CPU需要访问内存时,首先在L1缓存中查找数据,先找组索引位,然后与标志位对比。如果L1缓存中未命中,则需要从存储层次结构中的下一层(即L2缓存)查找。如若仍未命中,则会继续在L3缓存中查找。如果在L3缓存中也未命中,则会从主存中获取数据。
如果在三级缓存中找到了需要的数据,则可以直接访问缓存中的数据,从而提高访问速度。如果在三级缓存中没有找到,则需要从主存中获取数据,并将数据存入三级缓存中,以便下次访问时可以更快地获取数据
7.6 hello进程fork时的内存映射
当fork函数被当前进程hello调用时,内核为新进程hello创建各种数据结构,并分配给它一个唯一的PID。为了给这个新的hello创建虚拟内存,它创建了当前进程的mm_struct、区域结构和页表的原样副本。它将两个进程中的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。
当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当着两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面,因此,也就为每个进程保持了私有地址空间的抽象概念
7.7 hello进程execve时的内存映射
当调用execve()加载./hello程序时,Linux内核会完全重置进程的内存空间。销毁原内存布局,释放进程现有的所有内存映射(代码、数据、堆栈),但保留文件描述符、信号处理等非内存属性。
接着程序读取hello可执行文件的ELF头部,识别代码段、数据段、未初始化数据段等区域。然后建立新内存映射,将代码,数据映射到对应区域,共享对象由动态链接映射到进程共享区域,设置PC指向代码区域入口点,最终完成从旧进程到hello的切换。
通过这种方式execve可以在当前进程的上下文中运行一个新程序,而不需要创建新进程,既能彻底替换进程镜像,又能高效利用物理内存。
7.8 缺页故障与缺页中断处理
缺页故障指访问一个虚拟地址的数据时,对应的物理地址不在内存中,从而引发的异常情况。页面命中完全是由硬件完成的,而处理缺页异常是由硬件和操作系统内核协作完成的。
当发生缺页故障时,处理器会向操作系统发出缺页中断信号,缺页处理程序确定物理内存中的牺牲页(若页面被修改,则换出到磁盘),而后调入新的页面,并更新内存中的PTE。最后缺页处理程序返回到原来进程,再次执行导致缺页的指令,从而完成内存访问
7.9动态存储分配管理
动态内存管理通过malloc和free函数在堆区实现内存的按需分配与释放,主要采用空闲链表和内存池两种策略。malloc首次调用时会通过brk或mmap系统调用向操作系统申请大块内存,并基于隐式或显式空闲链表管理空闲块,分配时采用首次适配、最佳适配或分离空闲链表等算法快速查找合适内存块,同时通过边界标记和合并空闲块减少碎片;free释放内存时仅标记空闲状态,延迟归还操作系统以提升重用效率。printf等函数调用malloc时触发内存分配,高效的管理策略能显著降低频繁小内存申请的性能开销。
7.10本章小结
本章从hello存储器地址空间的四种地址出发,分别探讨了段式管理和页式管理实现地址转换的方式。接着研究了虚拟地址到物理地址转换的具体步骤,查TLB表、查四级页表。取到物理地址后进一步研究物理内存三级Cache访问的过程。在明晰了翻译和访存流程和虚拟地址的逻辑后,重新探讨了hello进程的fork和execve在虚拟内存中的作用。最后研究全过程中可能会出现的异常以及处理流程,在面对动态存储分配时程序也有一套分配管理机制。
原文链接:第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章1分)
结论
用计算机系统的语言,逐条总结hello所经历的过程。
你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。
(结论0分,缺失 1分,根据内容酌情加分)
附件
hello.c源文件
hello.i 预处理文件
hello.s 汇编文件
hello.o 可重定位目标文件
Hello 目标文件
hello.elf 目标文件的ELF格式文件
hello.asm 目标文件的反汇编文件
参考文献
[1]Randal E.Bryant,David O'Hallaron.深入理解计算机系统[M].机械工业出版社.2018.4
[2]Pianistx.printf函数实现的深入剖析[EB/OL].2013[2021-6-9].
https://www.cnblogs.com/pianist/p/3315801.html.
[3]ELF文件头结构.CSDN博客.
ELF文件头结构_elf32头-CSDN博客
[4]read和write系统调用以及getchar的实现.CSDN博客.
read和write系统调用以及getchar的实现_getchar和read-CSDN博客
[5]GCC online documentation.GCC online documentation-GNU Project
[6]深入理解计算机系统-之-内存寻址(三)--分段管理机制(段描述符,段选择子,描述符表).CSDN博客.
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/2301_80219340/article/details/147966487



