资讯详情

HIT计算机系统CSAPP-ICS2022大作业程序人生

本文主要以hello生活作为写作线索,介绍了程序从写作到运行的各个阶段,以及与计算机系统相关的结构和原理。主要部分为预处理、编译、汇编、链接、计算机系统流程管理、存储管理等。

关键词:Hello;预处理;编译;汇编;链接;流程管理;存储管理;计算机系统;

第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 本章小结 - 13 -

第4章 汇编 - 14 -

4.1 汇编的概念和功能 - 14 -

4.2 在Ubuntu下面汇编的命令 - 14 -

4.3 可重定位目标elf格式 - 14 -

4.4 Hello.o的结果解析 - 18 -

4.5 本章小结 - 19 -

第5章 链接 - 20 -

5.1 链接的概念和功能 - 20 -

5.2 在Ubuntu下链接命令 - 20 -

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

5.4 hello虚拟地址空间 - 22 -

5.5 链接重定位过程分析 - 23 -

5.6 hello的执行流程 - 24 -

5.7 Hello动态链接分析 - 25 -

5.8 本章小结 - 26 -

第6章 hello进程管理 - 27 -

6.1 过程的概念和作用 - 27 -

6.2 简述壳Shell-bash作用及处理过程 - 27 -

6.3 Hello的fork创建过程的过程 - 27 -

6.4 Hello的execve过程 - 28 -

6.5 Hello的进程执行 - 28 -

6.6 hello异常和信号处理 - 29 -

6.7本章小结 - 32 -

第7章 hello的存储管理 - 33 -

7.1 hello存储地址空间 - 33 -

7.2 Intel从逻辑地址到线性地址的转换-段管理 - 35 -

7.3 Hello的线性地址到物理地址的变换-页式管理 - 36 -

7.4 TLB支持四级页面VA到PA的变换 - 38 -

7.5 三级Cache支持的物理内存访问 - 39 -

7.6 hello进程fork时间内存映射 - 40 -

7.7 hello进程execve时的内存映射 - 40 -

7.8 缺页故障和缺页中断处理 - 40 -

7.9动态存储分配管理 - 41 -

7.10本章小结 - 42 -

结论 - 43 -

附件 - 45 -

参考文献 - 45 -

第1章 概述

1.1 Hello简介

根据Hello自白,利用计算机系统术语,简述Hello的P2P,整个过程020。

P2P:从代码到执行。首先是编辑器(IDE)编写完整的C语言代码Hello.c,然后通过翻译器(cpp,cc1,as)翻译成目标文件hello.o,最后,通过链接器生成完整的可执行目标文件。翻译过程分为预处理、编译和汇编cpp,cc1和as来完成。生成的可执行文件保存在存储器中(存储器、磁盘、固态硬盘或外部服务器)。在运行前,应通过操作系统接收执行文件hello命令,然后系统分配内存空间并将其加载到内存中(包括代码、数据和一系列相关部分),过程管理工具将创建一个新的过程,并在这个新的过程中执行hello。

[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-usUhsxzs-1652962368942)(media/6383fd76cd3ea144eaf50c0c56a4bd39.png)]

O2O:将进程管理器hello将其加载到内存并运行过程中,分配一个内存空间和相应的过程信息。hello操作完成后,监控进程管理器和操作系统hello终止时,卸载内存,清除相关流程(操作)信息,释放资源。

1.2 环境与工具

列出你写这篇论文的想法Hello在整个过程中,软硬件环境,以及开发和调试工具。

软件环境:windows10,Ubuntu20.04.

硬件环境:IntelCorei7-8750H 16GB RAM

开发、调试工具:VSCode、codeblocks、vim、gcc、EDB、GDB、OBJDUMP

1.3 中间结果列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。

hello.i(预处理代码) 通过hello.c预处理得到
hello.s(汇编代码) 通过hello.i编译得到
hello.o(可重定位文件) 通过hello.s汇编得到
hello(可执行文件) 通过hello.o链接得到
elf.txt hello.o的elf头等各种头表
hex.txt hello.o机器表示(十六进制)
objd_full.txt hello.o的objdump反汇编结果
out_elf.txt hello的elf头等各种头表
out_obj.txt hello的objdump反汇编结果
ld_elf.txt ld-2.32.so.2的elf头等各种头表
ld_obj.txt ld-2.31.so.2的objdump反汇编

1.4 本章小结

本文介绍了全文的主要内容,总结了软硬件环境和中间结果。

第2章 预处理

2.1 预处理的概念和作用

预处理器(cpp)根据以字符#开头的命令,修改原始命令 C程序。include<stdio.h>命令告诉预处理器读取系统头文件stdio.h内容,并将他直接插入程序文本。另一个C程序通常是用来获得的.i作为文件扩展名。

  1. 处理宏定义。define在预处理过程中于替换字符串。

  2. 包含文件。当前插入指定文件(代码)。include<stdio.h>就是将stdio.h内容直接插入当前程序文本。

  3. 条件编译。ifdef #else #endif根据相应的条件选择性地替换和编译文本。

    一般来说,使用预处理功能可以方便程序的修改、阅读、移植和调试,以及模块化程序设计。

2.2在Ubuntu下一个预处理命令

[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-MfZnVcfk-1652962368943)(media/4452fd48b3f4cdfa76b7098791caee30.png)]

使用预处理器cpp预处理源代码。

2.3 Hello预处理结果分析

[外链图片转失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0nJkwzzI-1652962368943)(media/58a37a0fac75cdefae1005c79f3b82c6.png)]

可以看到,原来#include部分被替换成了对应的源码,而原本的main函数仍然存在。

2.4 本章小结

从源代码编译的第一个步骤是预处理,这个操作通过预处理器cpp完成,将源代码中的各种地方进行文本替换,生成方便下一步处理的完整代码。

第3章 编译

3.1 编译的概念与作用

注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序

编译过程就是把预处理完的文件进行一系列的词法分析,语法分析,语义分析及优化后生成相应的汇编代码(从.i到.s)。编译器将预处理之后的源代码按照不同的机器(CPU)进行对应的到汇编代码的翻译。生成的汇编语言程序能够将不同的高级语言在硬件上得到统一。

3.2 在Ubuntu下编译的命令

应截图,展示编译过程!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WbDVEWHu-1652962368943)(media/c46c0d633d967285ab580978a169ed58.png)]

由于编译器cc1默认并不在系统环境变量里,需要手动找到路径运行。

3.3 Hello的编译结果解析

此部分是重点,说明编译器是怎么处理C语言的各个数据类型以及各类操作的。应分3.3.1~ 3.3.x等按照类型和操作进行分析,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-00JqE68j-1652962368944)(media/9641fe103c9e29b81caeb6c53e3d3d33.png)]

常量:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZEv3fYf6-1652962368944)(media/36e608becd07d8a231a6ecc70c01bc1d.png)]常量在汇编语言中通过立即数的形式来表示,立即数存储在代码段(.text),跟在对应的指令后。其中与某个具体操作立即数之外的常量数据,例如printf和scanf函数的格式串通常存储在.rodata段。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-asHxBgtd-1652962368945)(media/93ef6920d5841c4a32e3252ea10c3fcc.png)]

全局变量:全局变量一般不保存在程序运行栈中,而是保存在执行文件的.bss和.data段,用到的时候需要先通过mov指令从内存中对应位置加载到寄存器中。

局部变量:当寄存器有空闲时,通过编译器优化,局部变量会申请在寄存器中。否则对于一般情况,局部变量会被保存在程序运行栈中,例如hello.c中的int i:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AKIA9EuJ-1652962368945)(media/da613657e0dc683515ef58b21caf8e50.png)]。

数据类型:不同的数据都是以二进制(汇编代码中16进制)表示,长度由对应的数据类型决定,例如int型由4个字节保存,在寄存器中表示为寄存器的低32位。 而字符类型根据不同的字符编码有不同的长度,但在存储上每一个char都只占1个字节。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m9Soessn-1652962368945)(media/3acb3b7b34a2cdbd3eb788158bc356e2.png)]

不同的数据字长在指令后缀上也有体现,b对应1字节的数据,w对应2字节,l对应4字节,q对应8字节。

不同数据字长也可体现在寄存器的命名上:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vwVBq4Ya-1652962368947)(media/968d6dd837fb4682de16f0476096b985.png)]

赋值:汇编代码中赋值一般通过mov和leaq指令实现,其中mov的作用为在内存、寄存器和代码立即数间进行搬运。leaq通过一个简单的运算表达式求出结果并赋值给目标寄存器。另外,部算术运算指令在完成运算之后也会将运算结果赋值给第二个寄存器。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A049cMhD-1652962368947)(media/4b146f077f2261c6f11c708b396057cd.png)]

运算:加减乘除,其中长乘和除法会涉及到两个寄存器来存储一个操作数。

一般情况下算术运算的结果会保存在第二个寄存器中。对于二元运算,第一个操作数可以是立即数、寄存器和内存,第二个操作数可以是寄存器和内存,但二者不能同时为内存。当第二个操作数是内存时,运算会有从内存中读取和将运算结果保存到内存中两次内存的访问。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eH21GD1c-1652962368948)(media/601590b293f0045c58b90c868d279be4.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Mpgg19F-1652962368948)(media/1e3a1575c0968f31b8fa16d992dbed4f.png)]

条件分支:对于if语句和条件表达式,一般通过cmp指令和test进行不赋值运算,并在运算过程中更新条件码,然后通过条件跳转指令,根据对应的条件码是否为1进行跳转。例如:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3h1WZiJ5-1652962368949)(media/107b19d78a56ebf578244c73659738a2.png)]通过计算argc和常数4的差值,更新条件码来决定是否执行条件语句。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UarLLdo0-1652962368949)(media/6eaaf03a4a13e83460aa9a601d64cffa.png)],循环语句的循环条件判断也是同样的原理:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ki8UuBsY-1652962368950)(media/787bb40cb9a28179bb39f9d7291c8a79.png)]。

参数传递:当寄存器有足够的空闲时,会将函数参数存储在寄存器中,例如main函数的参数argc和*argv[]被分别保存在edi和rsi中:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yvKKH7BD-1652962368950)(media/0a47672bb2aff52f9fbee3dba80accd7.png)]

函数调用:对应汇编指令为call <函数>,在汇编代码的二进制表示中,call指令后存储对应函数的起始地址。Call指令作用是改变rip寄存器(程序计数器),将执行地址指向目标函数,并将当前指令的下一条指令的地址压入栈中保存,作为函数返回时的返回地址。

函数返回:函数返回时将返回值(如果有的话)保存在rax寄存器中,然后通过ret指令,读取之前保存的返回地址,将其赋值给程序计数器。在返回前如果有当前函数的栈空间会先进行释放。

数组:通常在内存(运行时栈或者程序data段)中保存为连续的一段,对于每个数组,以数组第一个元素的地址作为数组的地址。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3sM94U94-1652962368951)(media/e9e4af74ff9bc43ee8d1b572668d69fd.png)]

例如main的参数**argv被保存在rbp中。通过对数组首地址基础上进行偏置得到数组的后续元素。

指针:指针的汇编表示为一个地址,实现上可以看成是只有一个元素的数组。

3.4 本章小结

本章介绍c语言编译器如何将源代码翻译成对应的汇编语言。

第4章 汇编

4.1 汇编的概念与作用

汇编(Assembler)是将汇编语言翻译为机器语言的过程。翻译结束后这些机器语言会被打包成一种叫做可重定位目标程序的个是,并将结果保存在目标文件hello.o中。

4.2 在Ubuntu下汇编的命令

使用as指令(汇编器):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7EhEPrIW-1652962368951)(media/d17e59730fe10e8206409f0f293a83eb.png)]

4.3 可重定位目标elf格式

分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。

hello.o的ELF头如下图所示,左下角是ELF64的结构体定义,右上角是用readelf读取的格式化的ELF头,右下角是用od读取的文件的机器码,其中前四行(对应64字节)是hello.o的ELF头。ELF头的e_ident项包含了16个字节,其中前四个字节对应ascii码的.ELF用作ELF头的通用标识,其后的12个字节分别表示从class到ABI Version的各项机器无关信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ry8hsHgD-1652962368952)(media/d7b79131d9c3546bb82d08b2f24702cc.png)]

section header的结构体定义如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6mjYsDjQ-1652962368952)(media/a3a7901d3387b2ee6f295c9df35d8e83.png)]

根据在ELF头中的start of section headers项,确定section header在文件中的起始偏移1160(对应十六进制488)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Glea1fb2-1652962368953)(media/16bcaeaaaa6c5f32a69ba2160b9c3df2.png)]

同样使用hexdump找到section header部分的数据,可以根据结构体定义逐一解释每个section。对于每个section在表中记录了它的名字(以索引表示,具体的表示名字的字符串被保存在第13个节section header string table中,根据索引对应表中的字符串),类型,地址(因为还没有通过连接器进行重定向,因此暂时没有分配在内存中的具体地址),偏移(相对二进制文件起始),大小(例如hello.c中并没有定义全局和静态变量,因此.data的size为0),标记(是否是只读只写)等信息。

ELF符号表的结构体定义如下图:

hello.o的符号表解析如下:

其中name对应符号名称,size为对应符号的大小,type为类型(函数,数据或是其他),bind表示是本地还是全局,Ndx对应符号所在的节的序号。例如main是一个在 .text(对应节序号为1)偏移值为0处开始的142个字节长的函数。Ndx为UND时表示对应符号并未在当前文件被定义,例如引用其他头文件的函数。

重定位条目的结构体定义如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6f2apUcw-1652962368954)(media/696fb63207a804ee959c79e1c8deb755.png)]

hello.o的重定位节:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wD1AdQ65-1652962368955)(media/4a06fde3dffaf495f251af4cf6e5c084.png)]

其中每项条目中offset表示未重定位符号相对于原始.text起始的偏移,type为重定位类型,PC32代表使用相对计数器PC重定位,name对应待重定位的符号名称。

其中addend的偏移值通常为-4,因为相对地址的计算是用的当前指令的下一条指令的地址来进行计算,所以需要-4表示当前地址。

4.4 Hello.o的结果解析

objdump -d -r hello.o 分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。

说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JhnpCEHb-1652962368956)(media/addd69b8ab1c48d3ff3340f4b91e7fcc.png)]

机器语言中指令由二进制编码(十六进制显示)保存,而汇编语言中为字符。对于分支转移和函数调用,在汇编语言中直接用对应的符号表示(.L)而在.o文件中由相对地址表示,其中有些地方如call后因为尚未重定位,没有具体的地址信息。

4.5 本章小结

本章写了从汇编语言到机器语言的过程,当前阶段尚未对生成的文件进行重定位,需要在下节中的链接器进行链接和重定位操作后才能生成可以直接放入内存中执行的可执行文件。

第5章 链接

5.1 链接的概念与作用

注意:这儿的链接是指从 hello.o 到hello生成过程。

链接是将各种代码和数据片段收集并组合成为一个单一的文件的过程,这个文件可以被加载到内存并执行。在现代系统中,链接是由叫做连接器的程序自动执行的。

5.2 在Ubuntu下链接的命令

使用ld的链接命令,应截图,展示汇编过程! 注意不只连接hello.o文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bcc2cHJp-1652962368956)(media/137e02a3fd05b306e308cbf4e79892fc.png)]

其中最重要的是-lc,表示将libc.a链接进来,对应printf等各种函数的定义所在。

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

分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YEyYmdCf-1652962368956)(media/2f0be26ffd29697975fcbf5c31d28abb.png)]

elf头大体上和重定向前一样,type变成了可执行文件,entry point address从0

变成了0x4010f0,新增了程序头(program headers),节头数量边多。

hello的程序头表如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zux9NPEB-1652962368957)(media/e92756ca0e4b65c3fcef0e88c5603ca4.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t2hB18qe-1652962368957)(media/1e898e00f2c5fb5957a23a968f78ea9e.png)]

根据节表中记录的各个节的偏移和节的大小,我们可以对应出程序头表中各个段包括哪些内容,例如第一个type为load的段包含程序起始到.init之前的所有内容,第二个load段包括.init到.rodata前的所有内容(代码段,可读可执行),以此类推。

5.4 hello的虚拟地址空间

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-85bxejYm-1652962368958)(media/44913a29cecc2ccbe47421b868b85896.png)]

edb中地址从0x401000开始,对应第二个load段开始(.init)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c80YJVmS-1652962368958)(media/48d45e2a976863e63ad022d057a2cdba.png)]

对应main函数的起始位置正好是0x4011d6

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VKGd9AEB-1652962368959)(media/96ff84461ae3a237778ce75c3d676d3e.png)]

edb结束地址对应第二个load段(程序段)的结束。

5.5 链接的重定位过程分析

objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。

结合hello.o的重定位项目,分析hello中对其怎么重定位的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hQjJBIul-1652962368960)(media/8f0a863346d752a4cc6fd466cc08c90f.png)]

对于.rodata等本地内容(scanf和printf的格式串)直接用重定位分配的地址表示,例如4011ef处的mov将格式串在.rodata中的地址传到edi寄存器作为参数。

对于引用的外部c标准库函数,通过PIC函数调用实现(涉及GOT和PLT以及外部动态链接器程序的跳转)。

5.6 hello的执行流程

使用edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程。请列出其调用与跳转的各个子程序名或程序地址。

使用edb加载hello,首先第一条指令来自ld-2.31.so(动态链接器)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vguRnhaz-1652962368960)(media/d405842feebbb8c040f56bd6f397643a.png)]

随后通过call调用libc.so

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y4vecFdj-1652962368961)(media/2bb13ce30e045df2cbd9f2ef0277044b.png)]

然后调用hello,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mjcn25Ag-1652962368961)(media/14d20f68ef7b0b4fdcaf6b8a7955fd7d.png)]

对应地址为__libc_csu_init的函数起始地址

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VkxoEcpu-1652962368962)(media/c996b6a3817db8ed2bf2297affebc362.png)]

然后调用_init,也就是代码段的起点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HAIoUtcJ-1652962368962)(media/b77096bf08ab2f49a228a535cc3eca7a.png)]

通过init调用start函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6bqfXsJZ-1652962368963)(media/c358439e99d3457a2d106a0d13031ea6.png)]

最后进入main函数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L4B1y2AV-1652962368963)(media/523bf8141452dbea85d27d7e96db8ddc.png)]

5.7 Hello的动态链接分析

分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的内容变化。要截图标识说明。

dl_init前的.got.plt:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yz8FjCwe-1652962368964)(media/2db8132eb39e1ac750dd181f8b98d690.png)]

dl_init后的.got.plt:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7tij4a3Q-1652962368965)(media/7a90dbcffe39a97f8c5e33c0a47fb0a3.png)]

5.8 本章小结

本章介绍了程序从可重定位文件通过链接生成最终可执行文件的过程,以及一些动态链接和库的概念。

第6章 hello进程管理

6.1 进程的概念与作用

进程是一个执行中程序的实例,系统中的每一个程序都运行在某个进程的上下文中。上下文是由程序正确运行所需的状态组成的,这个状态包括存放在内存中的程序的代码和数据,他的栈,通用目的寄存器的内容,程序计数器,环境变量以及打开文件描述符的集合。

6.2 简述壳Shell-bash的作用与处理流程

shell是一个交互型的应用级程序,它代表用户运行其他程序,bash就是其中一种广泛使用的shell。

处理流程一般如下:

首先从终端读取用户输入的命令行。然后通过内置的语法分析器,从输入的字符串中区分出命令和各种参数参数。如果输入的命令是shell的内置命令,则直接通过shell进行处理,否则shell会认为输入的是一个可执行的外部程序,会到相应的地址寻找这个文件。如果找到了这个程序,则会通过fork创建一个新的子进程,并在这个进程中通过execve加载并执行程序。如果没有指定后台运行(&),则会等待程序运行结束,否则通过维护作业集合的方式,监测并回收运行结束的后台程序。

6.3 Hello的fork进程创建过程

父进程通过调用fork函数创建一个新的运行的子进程,子进程获得与父进程用户级虚拟地址空间相同的副本(独立),包括代码段,数据段,堆,共享库和用户栈。子进程还获得与父进程任何打开文件描述符相同的副本,这就意味着当父进程调用fork时,子进程可以获得读写父进程中打开的任何文件。

fork函数的返回值根据是父进程还是子进程会有所不同。fork在父进程中的返回值是新创建的子进程的PID,而在子进程中的返回值通常是0,由于新创建的子进程的PID不可能等于0,我们可以凭借这一点来区分当前是父进程还是子进程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4u9UC5LF-1652962368965)(media/c71cdf0c029ac9d6e4b9420af433da85.png)]

如上图所示,当我们在shell中运行hello时,shell会通过fork创建一个新的进程用于运行hello,shell这时负责在前台处理和hello的交互。

6.4 Hello的execve过程

execve函数在当前进程的上下文中加载并运行一个新程序。execve函数加载并运行可执行目标文件filename,且带参数列表argv和环境变量列表envp。只有当出现错误时,例如找不到filename,execve才会返回到调用程序。如上节所示,shell在通过fork创建新的子进程后,使用execve加载hello并运行。

6.5 Hello的进程执行

结合进程上下文信息、进程时间片,阐述进程调度的过程,用户态与核心态转换等等。

上下文是由程序正确运行所需的状态组成的,这个状态包括存放在内存中的程序的代码和数据,他的栈,通用目的寄存器的内容,程序计数器,环境变量以及打开文件描述符的集合。

在hello程序运行时,系统中也同时有许多其他程序正在运行,而进程制造了这样一种假象,让每个程序都好像独占硬件资源,而对于我们来说,这个假象是同时有很多程序一起运行。但是事实上,系统通过逻辑控制流,使得不同的进程轮流使用处理器资源,每个进程执行它的控制流的一部分的每一段时间叫做时间片,当所有这些时间片足够短的时候(切换频繁),就给我们造成了上述的错觉。

不同进程的切换通常是由上下文切换来完成的,而上下文的切换通常由内核来进行决定。内核根据异常,信号,程序中断和程序运行时长等不同的事件来决定什么时候进行上下文切换,这种决策也叫做调度,由内核中的调度器完成处理。

上下文切换:1)保存当前进程的上下文,2)恢复某个先前被抢占的进程被保存的上下文,3)将控制传递给这个新恢复的进程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RDuohKvS-1652962368966)(media/3af67081ccfc8af1f81f0c3a1ae67699.png)]

6.6 hello的异常与信号处理

hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。

程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。

异常总共可以分为四类:中断,陷阱,故障和终止。

在正常运行的hello程序中,可能发生中断和陷阱,当进行debug调试设置断点时就通过中断异常来实现在目标指令处暂停运行。而当加载运行hello时,需要向内核请求一些服务,比如读取文件等,这时通过syscall指令向内核发送一个陷阱异常,来告诉内核进行相应的系统调用,例如通过execve加载并运行hello。

linux下的信号种类:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lCikJqL3-1652962368966)(media/16e3a191c849f5c0b63edcd83bc6e6a4.png)]

常用的信号有:

SIGINT,SIGQUIT,SIGKILL,SIGTERM,SIGALRM,SIGCHLD,SIGSTOP等。

在hello运行时,通过CTRL+C键向shell发送SIGINT信号,并由shell转发给前台运行的hello程序,使得hello终止。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FAkbmGAY-1652962368968)(media/fb3f54af164a474da40e1f03bb750f40.png)]

同样的使用CTRL+Z可以使得hello暂停运行,这时hello不再运行在前台,但是我们可以通过ps和jobs指令看到hello仍然在后台中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vVD94Rwz-1652962368968)(media/129fa79a66d0853a5c1c35e91eb3b9d1.png)]

这时我们可以通过fg指令来将暂停运行的后台的hello重新到前台执行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QzZaZx6S-1652962368969)(media/159c4790866c97a67a7a58b83e9c1729.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3csH0lb7-1652962368970)(media/95a16aa3ea1227c8ef9f7e67d93cecfe.png)]

同样我们可以使用pstree和jobs看到后台挂起的hello进程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ntzYIOih-1652962368970)(media/95a16aa3ea1227c8ef9f7e67d93cecfe.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mt1HucnF-1652962368971)(media/a472657a90ac7a2bed31a38548dc6fed.png)]

我们也可以手动通过kill指令向进程发送指定的信号。如下图所示,我们向后台挂起的hello发送了SIGCONT指令后,hello程序继续开始执行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-paSTLdyA-1652962368971)(media/99080688fff16646084c263dd6d9e2b7.png)]

6.7本章小结

本章介绍了linux下进程的相关概念,包括进程的调度大概原理,异常处理和信号系统。并简单介绍了shell(BASH)的运作方式。

第7章 hello的存储管理

7.1 hello的存储器地址空间

结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。

逻辑地址:在有地址变换功能的计算机中,访内指令给出的地址 (操作数) 叫逻辑地址,也叫相对地址。比如hello的代码段第一条指令的起始地址总是0。

例如:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KyjWjbjd-1652962368971)(media/d11c5e9d3dd2cb59bc428d3d17a2476e.png)]

实际定义的逻辑地址还应该包含相应的段描述符:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ynI1Imfw-1652962368972)(media/01fbd9345282f42e5948d3e5548653fb.jpeg)]

线性地址:线性地址(Linear Address)是逻辑地址到物理地址变换之间的中间层。在分段部件中逻辑地址是段中的偏移地址,然后加上(段)基地址就是线性地址。

例如:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gcJEdNGK-1652962368972)(media/07b804103a0c45bb14f30acdae098d8b.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fW0Ryqt3-1652962368973)(media/bfed28e5b83785269c87132a20da9da8.png)]

虚拟地址:线性地址别称。每个进程看到的虚拟地址都是一样的,也就是所有的进程都可以看作独享所有的虚拟地址空间。

物理地址:CPU地址总线传来的地址,由硬件电路控制(现在这些硬件是可编程的了)其具体含义。物理地址中很大一部分是留给内存条中的内存的,但也常被映射到其他存储器上(如显存、BIOS等)。在没有使用虚拟存储器的机器上,虚拟地址被直接送到内存总线上,使具有相同地址的物理存储器被读写;而在使用了虚拟存储器的情况下,虚拟地址不是被直接送到内存地址总线上,而是送到存储器管理单元MMU,把虚拟地址映射为物理地址。

对于hello来说,hello不管在编译(地址跳转,分支,函数调用),链接重定向还是运行时(运行时虚拟内存),都会涉及许多的内存地址,这些内存地址是供hello任意使用的(按照一些规定),但是在实际运行时,对应于存储器的地址并不是hello中所看到的虚拟地址,而是经过一系列翻译过程后得到的物理地址。虚拟地址是为了程序编写,编译和运行过程中的方便,而物理地址才是实际数据存储在内存中的地址。

7.2 Intel逻辑地址到线性地址的变换-段式管理

仍然使用之前的图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oAiRsBpB-1652962368973)(media/d11c5e9d3dd2cb59bc428d3d17a2476e.png)]

上图是hello.o的main函数段反汇编代码,忽略尚未重定向的内容,最左边的指令地址便是main函数里的逻辑地址。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nyoZrSca-1652962368974)(media/bfed28e5b83785269c87132a20da9da8.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ctIvielp-1652962368974)(media/07b804103a0c45bb14f30acdae098d8b.png)]

在生成为可执行文件后,可以看到main函数中指令地址通过链接重定向后不再是从0开始,而是紧跟在.text其他函数后面。相应的代码段起始地址为0x401000,如果把中间插入的其他函数(包括相同段内.init等其他节和相同节.text内的其他函数)的总大小作为main函数偏移值,现在main函数里指令的 线性地址 = 段起始地址 + 偏移值 + 逻辑地址 。

7.3 Hello的线性地址到物理地址的变换-页式管理

从线性地址到物理地址,中间过程的一个重要概念是页(Page):页是数据在主存和磁盘间传输的基本单元,对于虚拟内存来说叫做虚拟页,对于物理地址来说叫做物理页,他们的大小相同,通常为4kB。

当程序访问某个地址(虚拟地址)时,首先需要知道对应地址的内容是否已经被加载到内存中,如果是则访问在内存中对应的位置数据,否则则调用缺页异常,通知磁盘将对应地址所在的页加载到内存中。

而用于判断一个虚拟页是否已经被缓存到内存的方法便是使用页表。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pXAnRgyv-1652962368974)(media/6a81e459b34ddb217c1aa531221453b3.png)]

页表通过为每一个虚拟地址保存一条PTE(页表条目),用于记录对应页是否被分配,是否被加载到内存,或者被保存在内存的哪个位置。

页表的缓存叫做TLB,作用类似于高速缓存和内存的关系。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y6585Y8h-1652962368976)(media/65b6197b5b690ada40c7092485b837bd.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0YPk5Ifq-1652962368976)(media/35ba5110affd3fd591c29fa3ed68d776.png)]

当MMU(内存管理单元)通过上述一系列过程确定了目标地址所在页的物理地址后,加上页偏移量就得到了对应的物理地址。

7.4 TLB与四级页表支持下的VA到PA的变换

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nXu1gjLW-1652962368976)(media/ed0de15bf725074e35d9a7ec7a5fbfe2.png)]

TLB和四级页表支持下从虚拟地址到物理地址,首先通过TLB查找对应的页表缓存条目,如果找到则直接得到物理页号,加上地址偏移,生成物理地址。

如果TLB不明中,则到四级页表中查询,从一级页表开始,每级页表对应下级页表中的512条。如果这个范围内有至少一条PTE,则当前页表便会指向下级页表对应范围的PTE,直到最后一级页表对应虚拟内存中的4kB的页。(类似trie树的查找过程,由于空结点不占用空间,可以节省大量内存)。如果找到了则得到物理页号,否则调用缺页异常,从磁盘加载页后从头再来。

7.5 三级Cache支持下的物理内存访问

首先访问L1cache,根据物理地址中的组索引定位到目标条目,匹配有效位和标记,如果成功则在缓存块内按照块偏移读取数据。

L2和L3的读取过程相似。如果缓存不命中,则从内存中进行读写,并缓存到各级缓存中。

7.6 hello进程fork时的内存映射

shell调用fork时,内核为新进程创建各种数据结构并分配唯一的PID,为了给这个新进程创建虚拟内存,它创建了当前进程的 mm_struct、区域结构和页表的原样副本。它将两个进程的两个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。

7.7 hello进程execve时的内存映射

在创建了一个子进程后,子程序调用execve函数在上下文加载hello程序: 1.删除已存在的用户区域:删除当前虚拟地址中已存在的用户区域。 2.映射私有区域:为新程序建立新的区域结构,这些区域结构是私有的,虚拟地址空间的代码和数据区域被映射为hello文件的.txt和.data区,bss区域是请求二进制零的,映射匿名文件,其大小包含在hello文件中。栈和堆区域也是请求二进制零的,初始长度为零。 3.映射共享区域:如果hello与共享对象链接,那么这些对象都被动态链接到这个程序,然后映射到用户虚拟地址空间中的共享区域。 4.设置程序计数器(PC):使之指向代码区域的入口点,下次调用这个进程时,从这个入口点开始执行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K5xiDTbB-1652962368977)(media/46f10dd0aa75f34e39f79dac10a982f0.png)]

7.8 缺页故障与缺页中断处理

当MMU在试图翻译某个虚拟地址时,发现该地址所在页面并不在内存中,此时就会触发一个缺页故障。故障发出的缺页异常会调用内核处理程序,首先判断虚拟地址是否合法,针对虚拟地址的操作是否合法(读写执行),确认合法后就可以通过一系列算法确定牺牲页,并从磁盘换入页面,更新页表。处理完成后内核将PC重新设置为之前的缺页触发指令重新执行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jJQFOP0O-1652962368977)(media/df083e654b0c7ea79526253d8b51492e.png)]

7.9动态存储分配管理

Printf会调用malloc,请简述动态内存管理的基本方法与策略。

动态内存分配器会维护一个进程的虚拟内存区域,称为堆。分配器将堆视为一组不同的大小的块的集合来维护。每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。分配器有两种基本风格。两种风格都是要求显示的释放分配块。 显式分配器:要求应用显示的释放任何已分配的块。例如C标准库提供一个叫做malloc程序包的显示分配器。 隐式分配器:要求分配器检测一个已分配块何时不再被程序使用,那么就释放这个块。隐式分配器也叫垃圾收集器。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-62PhNald-1652962368977)(media/36703e586dc430863e5b530304435846.png)]

通过malloc和free来动态分配内存的方式如上图所示。

7.10本章小结

本章从页表和缓存两方面,围绕虚拟内存和物理内存介绍了计算机的存储管理。

结论

用计算机系统的语言,逐条总结hello所经历的过程。

你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。

  1. 源代码。Hello轰轰烈烈一生,从它的源代码被一个字符一个字符轻轻敲下时便开始了。庞大精密的物理电路系统和灵性能动的人类的主观意志在这里交汇,每一行代码都是我们寄予这物质的客观世界的期许,所有的这一切,都将以最纯粹的方式带来回响。
  2. 预处理。这是机器理解hello的含义的第一步,预处理器通过约定俗成的符号,将我们的各种引用(库)和指代(define),展开铺平为最朴实的代码文本,完整且不加修饰。
  3. 编译。在这一阶段,所有的近似自然语言的高级语言都会按照机器的硬件功能被翻译成与机器指令一一对应的汇编语言。这一过程是从人的逻辑到物理实现方法的一一对应。
  4. 汇编。从汇编语言到可重定向文件,不仅是将字符翻译成数字的过程,也是从程序功能描述本身到适应实际系统的过程。独立的程序逻辑以文件的形式与系统相适应,成为计算机系统的一部分。
  5. 链接。链接是为了使文件形式能够同系统的其他部分协同工作的必要修饰,这一过程赋予了程序利用系统资源,同其他的系统模块交互沟通的基本条件,是沟通部分与整体的桥梁。
  6. 进程。进程的伟大之处,在于它给予了各式各样程序在计算机系统上当家作主人的权力。当我们通过shell下达运行hello的指令后,伴随着hello从磁盘中被载入内存,系统的各个部分便以信号为媒介产生共鸣,赋予这些二进制数字灵魂。
  7. 终止。当我们从屏幕上看到hello程序打印出的一行行“Hello 【学号】-【姓名】! ”,我们知道我们最初的期许终于得到了答复,Hello在处理器数以亿计的周期流转之后,也终于迎来了它可歌可泣生命的第一次谢幕。这是hello第一次使命的结束,在系统回收完毕它的残余之后,它便又被封存在磁盘表面细微精致的崎岖之间,沉睡于时间编织的摇篮之中,等待某主观意志的下一次唤醒。

附件

hello.i(预处理代码) 通过hello.c预处理得到
hello.s(汇编代码) 通过hello.i编译得到
hello.o(可重定位文件) 通过hello.s汇编得到
hello(可执行文件) 通过hello.o链接得到
elf.txt hello.o的elf头等各种头表
hex.txt hello.o的机器表示(十六进制)
objd_full.txt hello.o的objdump反汇编结果
out_elf.txt hello的elf头等各种头表
out_obj.txt hello的objdump反汇编结果
ld_elf.txt ld-2.32.so.2的elf头等各种头表
ld_obj.txt ld-2.31.so.2的objdump反汇编

参考文献

  1. 《深入理解计算机系统》CSAPP
  2. 百度
  3. 谷歌 岖之间,沉睡于时间编织的摇篮之中,等待某主观意志的下一次唤醒。

附件

hello.i(预处理代码) 通过hello.c预处理得到
hello.s(汇编代码) 通过hello.i编译得到
hello.o(可重定位文件) 通过hello.s汇编得到
hello(可执行文件) 通过hello.o链接得到
elf.txt hello.o的elf头等各种头表
hex.txt hello.o的机器表示(十六进制)
objd_full.txt hello.o的objdump反汇编结果
out_elf.txt hello的elf头等各种头表
out_obj.txt hello的objdump反汇编结果
ld_elf.txt ld-2.32.so.2的elf头等各种头表
ld_obj.txt ld-2.31.so.2的objdump反汇编

参考文献

  1. 《深入理解计算机系统》CSAPP
  2. 百度
  3. 谷歌

标签: 贴片电容cl05b102ko

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

 锐单商城 - 一站式电子元器件采购平台  

 深圳锐单电子有限公司