计算机系统大作业

摘  要

尽管hello程序非常简单,但是为了让它实现运行,系统的每个主要组成部分都需要协调工作,本篇论文就是解释说明在系统上执行hello程序时,系统发生了什么以及为什么会这样。

我们通过跟踪hello程序的生命周期开始系统讲解——从它被程序员创建开始,到在系统运行,输出简单的信息,然后终止,本论文将沿着这个程序的生命周期,较为清楚的介绍一些逐步出现的关键概念,专业术语和组成部分。从预处理,编译,汇编,链接等几个方便展现了hello从.c到生成可执行文件的过程;再从进程管理、存储管理、IO管理几个方面分析了hello过程的各种情况,更深入理解Linux系统下的存储层次结构、异常控制流、虚拟内存等相关内容。

让我们一起走进hello的一生。

关键词:计算机系统;程序的一生;                          

目  录

第1章 概述... - 5 -

1.1 Hello简介... - 5 -

1.2 环境与工具... - 5 -

1.3 中间结果... - 5 -

1.4 本章小结... - 5 -

第2章 预处理... - 6 -

2.1 预处理的概念与作用... - 6 -

2.2在Ubuntu下预处理的命令... - 6 -

2.3 Hello的预处理结果解析... - 6 -

2.4 本章小结... - 8 -

第3章 编译... - 9 -

3.1 编译的概念与作用... - 9 -

3.2 在Ubuntu下编译的命令... - 9 -

3.3 Hello的编译结果解析... - 9 -

3.3.1数据:局部变量... - 9 -

3.3.2main函数中的两个参数... - 10 -

3.3.3比较跳转... - 10 -

3.3.4赋值操作... - 11 -

3.3.5算数操作... - 11 -

3.3.6数组操作... - 11 -

3.3.7控制转移... - 12 -

3.3.8函数调用... - 12 -

3.4 本章小结... - 14 -

第4章 汇编... - 14 -

4.1 汇编的概念与作用... - 14 -

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

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

4.3.1生成ELF文件... - 14 -

4.3.2ELF头... - 15 -

4.3.3节头部表... - 16 -

4.3.4重定位节... - 16 -

4.3.4符号表... - 17 -

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

4.5 本章小结... - 20 -

第5章 链接... - 21 -

5.1 链接的概念与作用... - 21 -

5.2 在Ubuntu下链接的命令... - 21 -

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

5.3.1可执行目标文件的ELF格式... - 22 -

5.3.2ELF头... - 22 -

5.3.3节头... - 23 -

5.3.4符号表... - 24 -

5.4 hello的虚拟地址空间... - 24 -

5.5 链接的重定位过程分析... - 25 -

5.6 hello的执行流程... - 27 -

5.7 Hello的动态链接分析... - 27 -

5.8 本章小结... - 28 -

第6章 hello进程管理... - 29 -

6.1 进程的概念与作用... - 29 -

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

6.3 Hello的fork进程创建过程... - 29 -

6.4 Hello的execve过程... - 30 -

6.5 Hello的进程执行... - 30 -

6.6 hello的异常与信号处理... - 30 -

6.6.1运行中不停乱按... - 31 -

6.6.2运行中输入回车:... - 31 -

6.6.3运行中按ctrl-c。... - 32 -

6.6.4运行中按ctrl-z. - 32 -

6.6.5ctrl-z后运行fg. - 32 -

6.6.6ctrl-z后运行kill - 33 -

6.6.7ctrl-z后运行ps、jobs、pstree命令,输出相关信息。... - 34 -

6.7本章小结... - 36 -

第7章 hello的存储管理... - 37 -

7.1 hello的存储器地址空间... - 37 -

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

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

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本章小结... - 40 -

结论... - 41 -

附件... - 42 -

参考文献... - 43 -

第1章 概述

1.1 Hello简介

P2P:P2P就是将源程序文件(Program)经过一系列操作变成fork进程(Process)。具体过程为:将写好的hello.c(附件已给出)源程序经过预处理器(cci)处理后得到hello.i(修改了的源程序文本),然后经过编译器(ccl)生成hello.s(汇编程序),在经过汇编器(as)将hello.s翻译成机器指令,得到hello.o(可重定位目标程序),最后经过链接器(ld)生成hello(可执行目标文件)。在shell上输入执行命令(./hello)后,为它fork进程(Process)。

020 : 经过“P2P”后,我们通过exceve在进程的上下文中加载并运行fork的子进程,映射到对应虚拟内存,然后程序依需求载入物理内存。在CPU的操作下,为其分配时间片执行逻辑控制流,按顺序执行程序命令,将最终结果显示在屏幕上。在程序运行结束后,父进程回收子进程,hello完成了它的一生。

1.2 环境与工具

X64 CPU;2GHz;2G RAM;256GHD Disk 以上;

Windows7 64位以上;VirtualBox/Vmware 11以上;Ubuntu 16.04 LTS 64位/优麒麟 64位;

开发与调试工具:gcc,vim,edb,readelf,HexEdit

1.3 中间结果

hello.i     预处理文件(文本)

hello.s     汇编语言文件(文本)

hello.o         可重定位目标文件(二进制)

hello       可执行目标文件(二进制)

hello.elf   hello.o的elf格式文件

hello hxy.elf    hello的elf格式文件

1.4 本章小结

介绍了hello的P2P和020的过程,并列出了实验环境以及所用工具,简述了hello.c生成的中间结果文件名字。

(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用

预处理一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。典型地,由预处理器对程序源代码文本进行处理,得到的结果再由编译器核心进一步编译。在该过程中,可能进行以下操作:文件包含,条件编译、布局控制,宏替换和删除注释等

在hello.c的预处理阶段中:预处理器(cpp)根据以字符#开头的命令,修改原始的C程序,比如hello。C中的第一行的#include<stdio.h>命令告诉预处理器读取系头文件stdio.h的内容,并把它直接插入程序文本中,结果就得到了另一个hello.c程序,通常以.i作为扩展名。

2.2在Ubuntu下预处理的命令

gcc -E hello.c -o hello.i

图1

2.3 Hello的预处理结果解析

图2

图3

经过预处理器的处理,生成的hello.i文件中的内容相较于hello.c增长了许多,原因是hello.i文件中#include被展开,并加入程序文本,所以文本后仍保有hello.c的内容。源程序中的注释已经删除。同时#include被替换成了相应的头文件。

2.4 本章小结

本章阐述了预处理的概念与作用,并展现了预处理器将程序进行预处理生成.i文件的过程。

通过将hello.c进行预处理,对比二者结果验证预处理的作用。

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

编译是指编译器(ccl)将预处理生成的后缀.i的文件进行编译,生成后缀.s文件的过程。

在编译过程中,编译器通过进行词法分析,语法分析,语义检查和中间代码生成,代码优化等工作过程对源程序进行分析,在分析过程中如果发现错误则给出提示信息,否则将预处理后的文件翻译成汇编程序文件

本文中编译器(ccl)将文本文件hello.i翻译成hello.s,它包含一个汇编语言程序,该程序包含函数main的定义,如下所示

3.2 在Ubuntu下编译的命令

gcc -S hello.i -o hello.s

图4

3.3 Hello的编译结果解析

3.3.1数据:局部变量

在main函数中定义局部变量i:

图5

编译器对其存入栈中

图6

字符串常量

图7

3.3.2main函数中的两个参数

图8

第一个参数是int类型的argc,第二个是char* 数组argv

编译器将他们分别存储在寄存器%edi和%rsi中

图9

3.3.3比较跳转

图10

Argc和4比较,变量i和8比较

图11

图12

3.3.4赋值操作

图13

将0赋值给i,通过mov指令实现

图14

3.3.5算数操作

(1)i++

图15

图16

(2)加法add

图17

3.3.6数组操作

图18

取出argv数组中的值:

图19

3.3.7控制转移

(1)条件控制if

图20

在hello.s中分支结构用跳转指令实现

图21

(2)循环控制for

图22

当i<8时,会跳转至L4,执行循环

图23

3.3.8函数调用

=对printf函数的调用:

图24

将寄存器%rdi,%rsi和%rdx的当做参数传给printf

=对exit函数的调用

图25

将寄存器%rdi中1作为参数传给exit

=对atoi参数的调用

图26

利用寄存器%rdi中的数当做参数

=对sleep函数的调用:

图27

将atoi函数的返回值传给寄存器%edi,并作为参数传给sleep。

=对getchar函数的调用,没有参数传递

图28

3.4 本章小结

本章阐述了编译的概念和作用,并且详细说明了编译器将预处理后得到的hello.i文件进一步翻译为存储在hello.s中的汇编语言的过程。

(第32分)

第4章 汇编

4.1 汇编的概念与作用

汇编器将编译生成的.s后缀文件翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在.o后缀文件中。

汇编的作用是把汇编语言翻译成机器语言,用二进制码代替汇编语言中的符号,即让它成为机器可以直接识别的程序。

4.2 在Ubuntu下汇编的命令

as hello.s -o hello.o

图29

4.3 可重定位目标elf格式

4.3.1生成ELF文件

使用命令 readelf -a hello.o > hello.elf

图30

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

4.3.2ELF头

图31

根据头信息我们可以看到ELF头以一个16字节的序列开始,这个序列描述了生成文件的系统的字大小和字节顺序。ELF头的剩下部分帮助链接器语法分析和解释目标文件的信息:包括ELF头的大小、目标文件的类型、机器类型、节头部表的文件偏移,以及节头部表中条目的大小和数量。

4.3.3节头部表

图32

节头部表,目标文件中的每个节都有一个固定条目体现在表中,指明各个节的信息。

4.3.4重定位节

  .rela.text节是一个.text节中位置的列表。当链接器把这个目标文件和其他文件组合时,需要修改这些位置。一般而言,任何调用外部函数或者应用全局变量的指令都需要修改。

  其中,“偏移量”是需要被修改的引用的字节偏移(在代码节或数据节的偏移),“信息”指示了重定位目标在.symtab中的偏移量和重定位类型,“类型”表示不同的重定位类型,例如图中R_X86_64_PC32就表示重定位一个使用32位PC相对地址的引用。“符号名称”表示被修改引用应该指向的符号,“加数”是一个有符号常数,一些类型的重定位要用他对被修改引用的值做偏移调整。

图33

4.3.4符号表

图34

符号表中存放了程序中定义和引用的函数和全局变量。在hello.c中我们没有定义全局变量,所以符号表中没有他们的身影,只能看到函数。符号表,存放程序中定义和引用的函数和全局变量的信息

4.4 Hello.o的结果解析

objdump -d -r hello.o

图35

(1)机器语言的构成,与汇编语言的映射关系:

·  机器语言:

计算机执行的二进制命令,都是0和1表示的,本质上是由“0”和“1”组成的二进制数。计算机发明之初,人们只能计算机的语言去命令计算机干这干那。向计算机每发出一条指令,就要写出一串串由“0”和“1”组成的指令序列。

机器指令是指我们的CPU能够直接识别并执行操作的指令,它的表现形式是二进制编码。机器指令通常由操作码和操作数两部分组成,操作码代表了操作的类型,操作数是指进行运算的数值。

机器指令又与CPU相关联。不同种类的CPU对应的机器指令也不同,而且指令系统往往相差挺大。

机器语言是用来直接描述机器指令、使用机器指令的规则等。它是CPU能直接的语言。 

·  汇编语言:

用助记符代替机器指令的操作码(如:ADD表示加法),于是人们对其进行了改进 使用一些有含义的助记符 组成了另一种编程语言 汇编语言。

汇编语言是汇编指令、伪指令集和使用它们规则的统称。伪指令是在程序设计师所需要的一些辅助性说明指令,机器并不会执行这些指令。

(2)分析hello.o的反汇编,hello.o的反汇编里的机器语言与hello.s中的汇编语言比较,主要有以下差异:

·  操作数:

在hello.s中为十进制

图36

在反汇编中为十六进制

图37

·  分支转移:

在hello.s中为”L2”等段名称,在hello.s中,分支转移函数通过Lable实现表明跳转地址,如下图

图38

在反汇编中为相对偏移地址,而在hello.o的反汇编文件中,因为我们给每一条指令赋予了一个地址,所以直接使用<函数名 + 偏移量>的形式表明跳转地址,如下图:

图39

·  函数调用

依然使用call指令调用函数,call后跟着的是下一条指令的地址。

在hello.s中为函数名

图40

在反汇编中是调用函数的相对偏移地址

图41

4.5 本章小结

本章阐述了汇编的作用和概念,并详细分析了可重定位目标文件的elf格式,同时将hello.o与hello.s文件做了比较。

(第41分)

第5章 链接

5.1 链接的概念与作用

链接是将各种代码和数据片段收集并组合成一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行于编译时,也就是在源代码被编译成机器代 码时;也可以执行于加载时,也就是在程序被加载器加载到内存并执行时;甚至于运行时,也就是由应用程序来执行。链接是由叫做链接器的程序执行的。链接器在软件开发扮演着一个关键的角色,因为它们使得分离编译成为可能。概念:链接(linking)是将各种代码和数据片段收集并组合成一个大一文件的过程,这个这个文件可被家在(复制)到内存并执行。

作用:链接使分离编译成为了可能。我们不用将一个大型的应用程序组织值为一个巨大的源文件,而是看到一把他分解为更小,更好管理的模块,可以俄uli地修改和编译这些模块。

5.2 在Ubuntu下链接的命令

在查询资料后可以得到链接命令:

图42

Ld -o hello -dynamic-linker /lib64/ld-linux-x86-64.so.2 /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

使用ld的链接命令

图42

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

5.3.1可执行目标文件的ELF格式

在终端中输入指令readelf -a hello打印可执行文件的ELF格式。

图43

5.3.2ELF头

图44

在ELF 头中类型为EXEC表明hello是一个可执行目标文件,有27个节

5.3.3节头

根据 节头中的信息我们就可以用 HexEdit 定位各个节所占的区间(起始位置,大小),地址是程序被载入到虚拟地址的起始地址。

图45

5.3.4符号表

图46

5.4 hello的虚拟地址空间

使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。命令行:edb --run bomb(文件名) 。

  

图47

通过edb可知hello的虚拟地址空间开始于0x00401000,结束于0x00401ff0

5.5 链接的重定位过程分析

在命令行输入指令 objdump -d hello > hello.txt 生成可执行文件年的反汇编代码,通过将其与hello.o的反汇编代码比较,可以分析了解重定位过程。

图48

图49

·  分析hello与hello.o的不同:

不同:

(1)hello的汇编代码中出现了_init,.plt,puts@plt,printf@plt等函数名,而hello.o的汇编代码中只出现了main函数名

(2)hello汇编代码中地址已确定

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

在重定位过程中,链接器首先将符号解析完成,并将代码中的所有符号引用与一个符号定义关联。链接器按照各个内容的具体大小重定位,合并输入模块,为每个符号分配运行时的地址。链接器将所有输入到hello中相同类型的节合并为同一类型的新的节,然后链接器将运行时内存地址赋给新的节,赋给输入模块定义的每个节,以及赋给输入模块定义的每一个符号。最后是重定位节中的符号引用,链接器会修改hello中的代码节和数据节中对每一个符号的引用,使得他们指向正确的运行地址。

5.6 hello的执行流程

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

edb中打开hello,在所有函数处断点,运行程序,记录从开始到程序终止调用的所有函数。

表1

函数

地址

_start ()

0x00000000004010f0

puts()

0x0000000000401030

_init ()

0x0000000000401000

main ()

0x0000000000401125

printf@plt ()

0x0000000000401040

atoi@plt ()

0x0000000000401060

sleep@plt ()

0x0000000000401080

getchar@plt ()

0x0000000000401050

_fini ()

0x00000000004011c0

5.7 Hello的动态链接分析

 动态链接库中的函数在程序执行的时候才会确定地址,所以编译器无法确定其地址。为避免运行时修改调用模块的代码段,链接器采用延迟绑定的策略。延迟绑定通过两个数据结构之间简洁但又有些复杂的交互来实现,即过程链接表(PLT)和全局偏移量表(GOT)。

 过程链接表(PLT):PLT是一个数组,其中每个条目是16字节代码。PLT [0]是一个特殊条目,它跳转到动态链接器中。每个被可执行程序调用的库函数都有它自己的PLT条目。每个条目都负责调用一个具体的函数。每个条目都负责调用一个具体的函数。

全局偏移量表(GOT):GOT是一个数组,其中每个条目是8字节地址。和PLT联合使用时,GOT [0]和GOT [1]包含动态链接器在解析函数地址时会使用的信息。GOT [2]是动态链接器在1d-linux.so模块中的入口点。其余的每个条目对应于一个被调用的函数,其地址需要在运行时被解析。每个条目都有一个相匹配的PLT条目。

通过节头目表可知,hello的.got.plt段的起始位置是0x404000,使用edb查看

调用前的内容:

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

hello的elf中got表的位置

图50

图51  dl_init前

图52  dl_init后

5.8 本章小结

本章中主要介绍了链接的概念与作用,并且详细阐述了hello.o是怎么链接成为一个可执行目标文件的过程,详细介绍了hello.o的ELF格式和各个节的含义,并且分析了hello的虚拟地址空间、重定位过程、执行流程、动态链接过程。

(第51分)

第6章 hello进程管理

6.1 进程的概念与作用

进程:是一个程序在操作系统中的执行实例,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

进程为用户提供了这样的假象,我们的程序好像是系统中当前运行的唯一程序一样,我们的程序好像是独占的使用处理器和内存,处理器好像是无间断地执行我们程序中地指令,我们程序中的代码和数据好像是系统内存中唯一的对象。

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

Shell概念: shell本意是指壳,在计算机术语中,shell是指用户操作接口的意思。操作系统运行起来后都会给用户提供一个操作接口,这个操作接口就叫shell。

用户可以通过shell来调用操作系统内部的复杂实现。

Shell 是一个应用程序,它连接了用户和 Linux 内核,让用户能够更加高效、安全、低成本地使用 Linux 内核,这就是 Shell 的本质。

Shell 本身并不是内核的一部分,它只是在内核的基础上编写的一个应用程序,它和 QQ、迅雷、Firefox 等其它软件没有什么区别。

然而 Shell 也有着它的特殊性,就是开机立马启动,并呈现在用户面前;用户通过 Shell 来使用 Linux,不启动 Shell 的话,用户就没办法使用 Linux。

功能:shell应用程序提供了一个界面,用户通过访问这个界面访问操作系统内核的服务。 

处理流程:

1)从终端读入输入的命令。

2)将输入字符串切分获得所有的参数

3)如果是内置命令则立即执行

4)否则调用相应的程序执行

5)shell 应该接受键盘输入信号,并对这些信号进行相应处理

6.3 Hello的fork进程创建过程

运行shell,在shell上运行hello程序,当读到./hello命令时,shell作为父进程通过fork函数为hello创建一个新的进程作为子进程。通过fork函数,子进程得到与父进程用户级虚拟地址空间相同但独立的一份副本,包括代码和数据段、堆、共享库、用户栈。hello进程还获得与父进程任何打开文件描述符相同的副本,这就意味着当父进程调用fork时,子进程还可以读写父进程中打开的任何文件。父进程和新创建的子进程之间最大的区别在于它们有不同的PID。

6.4 Hello的execve过程

fork子进程创建后,shell调用execve函数加载并运行可执行目标文件hello,且带参数列表argv和环境变量列表envp。之后当出现错误时,例如找不到hello,execve才会返回到调用程序。

在execve加载了hello后,它调用启动代码,启动代码设置栈,并将控制转移给新程序的主函数main,此时用户栈已经包含了命令行参数和环境变量,进入main函数后开始逐步运行程序。

6.5 Hello的进程执行

在hello的进程执行中,内核为每个进程维持一个上下文,上下文就是内核重新启动一个被抢占的进程所需要的状态,它由通用寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构等对象的值构成。

调度过程:

从hello进程进入内核;

内核进行上下文切换,执行和hello并发的其他进程;

Sleep休眠时间结束;

并发进程进入内核;

内核进行上下文切换,继续执行hello进程。

6.6 hello的异常与信号处理

=异常:中断、陷阱、故障、终止

=信号:SIGSTP、SIGCONT、SIGKILL、SIFGINT等。

=中断是来自I/O设备的信号,异步发生,中断处理程序对其进行处理,返回后继续执行调用前待执行的下一条代码,就像没有发生过中断。

=陷阱是有意的异常,是执行一条指令的结果,调用后也会返回到下一条指令,用来调用内核的服务进行操作。帮助程序从用户模式切换到内核模式。

=故障是由错误情况引起的,它可能能够被故障处理程序修正。如果修正成功,则将控制返回到引起故障的指令,否则将终止程序。

=终止是不可恢复的致命错误造成的结果,通常是一些硬件的错误,处理程序会将控制返回给一个abort例程,该例程会终止这个应用程序。

6.6.1运行中不停乱按

图53

结果:无影响。

6.6.2运行中输入回车:

图54

将输入的内容保存到缓冲区,待当前hello进程结束后,shell读入缓冲区内容。

6.6.3运行中按ctrl-c。

图55

原理:shell父进程收到SIGINT信号,信号处理函数将hello进程终止并回收。

6.6.4运行中按ctrl-z

图56

原理:shell父进程收到SIGSTP信号,信号处理函数将hello进程挂起。

6.6.5ctrl-z后运行fg

图57

原理:发送SIGCONT信号继续执行hello。

6.6.6ctrl-z后运行kill

图58

原理:命令发送SIGKILL信号杀死hello。

6.6.7ctrl-z后运行ps、jobs、pstree命令,输出相关信息。

图59

原理:ps命令输出当前系统中的进程;jobs命令输出当前已启动的任务状态;pstree命令输出进程间的树状关系。

6.7本章小结

本章介绍了程序在shell执行及进程的相关概念,说明了hello程序在shell中通过fork函数及execve创建新的进程并执行程序的过程,内核的调度及上下文切换等机制。研究了hello执行过程中各种操作可能引发的异常和信号处理。

(第61分)

第7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:逻辑地址指的是机器语言指令中,用来指定一个操作数或者是一条指令的地址,即程序的机器代码中保存的地址。

线性地址(也叫虚拟地址):现代系统提供了一种对主存的抽象概念,叫做虚拟内存。使用虚拟寻址,CPU通过生成一个虚拟地址来访问主存,这个虚拟地址在被送到内存之前先转换为适当的物理地址。

物理地址:物理地址是用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。

图60

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

一个逻辑地址由两部分组成,段标识符和段内偏移量。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号。后面3位包含一些硬件细节。可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就描述了一个段。一些全局的段描述符,就放在“全局段描述符表(GDT)”中,一些局部的,例如每个进程自己的,就放在所谓的“局部段描述符表(LDT)”中。

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

页表是一个存放在物理内存中的数据结构,将虚拟页映射到物理页。每次地址翻译硬件将一个虚拟地址转换为物理地址时读取页表。操作系统负责维护页表中的内容,以及再磁盘与DRAM之间来回传送页。

页式管理,是将虚拟地址空间划分为一个一个固定大小的块(称作虚页),同一时候也让实际地址空间也划分为一个一个相同大小的页(称作实页)。在页式管理中,由虚拟地址寻到实际主存地址的步骤为先找到页表基址寄存器,获得相应页表的基地址,然后用基址与虚页号做一次加法找到相应的实页号,由实页号和页内位移组成实际主存地址。

图61

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

Core i7 MMU 使用四级的页表将虚拟地址翻译成物理地址。36位VPN 被划分成四个9 位VPN,分别用于一个页表的偏移量。

CPU产生虚拟地址VA,VA传送给MMU,MMU使用前36位VPN作为TLBT+TLBI向TLB中匹配,如果命中,则得到40位PPN+12位VPO组合成52位物理地址PA。如果没有命中,MMU向页表中查询,CR3确定第一级页表的起始地址,9位VPN1确定在第一级页表中的偏移量,查询出第一部分PTE,以此类推最终在四级页表都访问完后获得PPN,与VPO结合获得PA,并向TLB中更新。

图62

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

得到物理地址之后,先将物理地址拆分成CT(标记)+CI(索引)+CO(偏移量),然后在一级cache内部找,如果未能寻找到标记位为有效的字节(miss)的话就去二级和三级cache中寻找对应的字节,找到之后返回结果。

图63

7.6 hello进程fork时的内存映射

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

7.7 hello进程execve时的内存映射

删除已存在的用户区域,创建新的区域结构,代码和初始化数据映射到.text和.data区(目标文件提供),.bss和栈映射到匿名文件,设置PC,指向代码区域的入口点。

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

DRAM 缓存不命中称为缺页,即虚拟内存中的字不在物理内存中。缺页导致页面出错,产生缺页异常。缺页异常处理程序选择一个牺牲页,然后将目标页加载到物理内存中。最后让导致缺页的指令重新启动,页面命中。

7.9本章小结

本章阐述了Linux存储器的地址空间,Intel的段式管理和页式管理机制,以及TLB与多级页表支持下的VA到PA的转换,三级cache支持下的物理内存访问。说明了hello的fork和execve内存映射,缺页故障与缺页中断处理程序。

(第7 2分)

 

结论

hello一生的经历了:程序编写,预处理,编译,汇编,链接中生成可执行文件hello。

然后开始运行:在shell中输入命令行./hello, shell进程调用fork为其创建子进程,shell调用execve,execve调用启动加载器,加映射虚拟内存,进入程序入口后程序开始载入物理内存,然后进入 main函数

接着:执行指令,访问内存,动态申请内存,异常控制流(如果运行途中键入ctr-c或ctr-z或进行其他操作,则调用shell的信号处理函数进行相应处理)。

最后结束:shell父进程回收子进程,内核删除为hello进程创建的所有数据结构。

至此,hello结束从系统中消失。

经过本课程的学习,大大提高了我对计算机的兴趣,hello是我们作为程序员来说最早接触的程序,也是最简单的程序之一,而如此小的一个程序,在如此短的时间内,需要接触计算机系统的各个方面,这也许恰恰体现了计算机系统这门课的魅力,感谢编写这本书的作者以及将知识传授给我们的老师们,感谢在计算机行业做出贡献的科研工作者们!

(结论0分,缺失 -1分,根据内容酌情加分)

附件

hello.c     源文件

hello.i     预处理文件

hello.s     汇编语言文件

hello.o         可重定位目标文件

hello       可执行目标文件

hello.elf   hello.o的elf格式文件

hello hxy.elf    hello的elf格式文件

参考文献

  1. 深入理解计算机系统(CSAPP)

[2]菜菜2022. ( 2020-03-03 16:15:13 发布). 编译预处理详述. csdn. 

https://blog.csdn.net/kz_java/article/details/104632270?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168482645516800182723803%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=168482645516800182723803&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-3-104632270-null-null.142^v87^control_2,239^v2^insert_chatgpt&utm_term=%E9%A2%84%E5%A4%84%E7%90%86%E7%9A%84%E6%A6%82%E5%BF%B5%E5%90%88%E4%BD%9C%E7%94%A8&spm=1018.2226.3001.4187

摘  要

尽管hello程序非常简单,但是为了让它实现运行,系统的每个主要组成部分都需要协调工作,本篇论文就是解释说明在系统上执行hello程序时,系统发生了什么以及为什么会这样。

我们通过跟踪hello程序的生命周期开始系统讲解——从它被程序员创建开始,到在系统运行,输出简单的信息,然后终止,本论文将沿着这个程序的生命周期,较为清楚的介绍一些逐步出现的关键概念,专业术语和组成部分。从预处理,编译,汇编,链接等几个方便展现了hello从.c到生成可执行文件的过程;再从进程管理、存储管理、IO管理几个方面分析了hello过程的各种情况,更深入理解Linux系统下的存储层次结构、异常控制流、虚拟内存等相关内容。

让我们一起走进hello的一生。

关键词:计算机系统;程序的一生;                          

目  录

第1章 概述... - 5 -

1.1 Hello简介... - 5 -

1.2 环境与工具... - 5 -

1.3 中间结果... - 5 -

1.4 本章小结... - 5 -

第2章 预处理... - 6 -

2.1 预处理的概念与作用... - 6 -

2.2在Ubuntu下预处理的命令... - 6 -

2.3 Hello的预处理结果解析... - 6 -

2.4 本章小结... - 8 -

第3章 编译... - 9 -

3.1 编译的概念与作用... - 9 -

3.2 在Ubuntu下编译的命令... - 9 -

3.3 Hello的编译结果解析... - 9 -

3.3.1数据:局部变量... - 9 -

3.3.2main函数中的两个参数... - 10 -

3.3.3比较跳转... - 10 -

3.3.4赋值操作... - 11 -

3.3.5算数操作... - 11 -

3.3.6数组操作... - 11 -

3.3.7控制转移... - 12 -

3.3.8函数调用... - 12 -

3.4 本章小结... - 14 -

第4章 汇编... - 14 -

4.1 汇编的概念与作用... - 14 -

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

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

4.3.1生成ELF文件... - 14 -

4.3.2ELF头... - 15 -

4.3.3节头部表... - 16 -

4.3.4重定位节... - 16 -

4.3.4符号表... - 17 -

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

4.5 本章小结... - 20 -

第5章 链接... - 21 -

5.1 链接的概念与作用... - 21 -

5.2 在Ubuntu下链接的命令... - 21 -

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

5.3.1可执行目标文件的ELF格式... - 22 -

5.3.2ELF头... - 22 -

5.3.3节头... - 23 -

5.3.4符号表... - 24 -

5.4 hello的虚拟地址空间... - 24 -

5.5 链接的重定位过程分析... - 25 -

5.6 hello的执行流程... - 27 -

5.7 Hello的动态链接分析... - 27 -

5.8 本章小结... - 28 -

第6章 hello进程管理... - 29 -

6.1 进程的概念与作用... - 29 -

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

6.3 Hello的fork进程创建过程... - 29 -

6.4 Hello的execve过程... - 30 -

6.5 Hello的进程执行... - 30 -

6.6 hello的异常与信号处理... - 30 -

6.6.1运行中不停乱按... - 31 -

6.6.2运行中输入回车:... - 31 -

6.6.3运行中按ctrl-c。... - 32 -

6.6.4运行中按ctrl-z. - 32 -

6.6.5ctrl-z后运行fg. - 32 -

6.6.6ctrl-z后运行kill - 33 -

6.6.7ctrl-z后运行ps、jobs、pstree命令,输出相关信息。... - 34 -

6.7本章小结... - 36 -

第7章 hello的存储管理... - 37 -

7.1 hello的存储器地址空间... - 37 -

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

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

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本章小结... - 40 -

结论... - 41 -

附件... - 42 -

参考文献... - 43 -

第1章 概述

1.1 Hello简介

P2P:P2P就是将源程序文件(Program)经过一系列操作变成fork进程(Process)。具体过程为:将写好的hello.c(附件已给出)源程序经过预处理器(cci)处理后得到hello.i(修改了的源程序文本),然后经过编译器(ccl)生成hello.s(汇编程序),在经过汇编器(as)将hello.s翻译成机器指令,得到hello.o(可重定位目标程序),最后经过链接器(ld)生成hello(可执行目标文件)。在shell上输入执行命令(./hello)后,为它fork进程(Process)。

020 : 经过“P2P”后,我们通过exceve在进程的上下文中加载并运行fork的子进程,映射到对应虚拟内存,然后程序依需求载入物理内存。在CPU的操作下,为其分配时间片执行逻辑控制流,按顺序执行程序命令,将最终结果显示在屏幕上。在程序运行结束后,父进程回收子进程,hello完成了它的一生。

1.2 环境与工具

X64 CPU;2GHz;2G RAM;256GHD Disk 以上;

Windows7 64位以上;VirtualBox/Vmware 11以上;Ubuntu 16.04 LTS 64位/优麒麟 64位;

开发与调试工具:gcc,vim,edb,readelf,HexEdit

1.3 中间结果

hello.i     预处理文件(文本)

hello.s     汇编语言文件(文本)

hello.o         可重定位目标文件(二进制)

hello       可执行目标文件(二进制)

hello.elf   hello.o的elf格式文件

hello hxy.elf    hello的elf格式文件

1.4 本章小结

介绍了hello的P2P和020的过程,并列出了实验环境以及所用工具,简述了hello.c生成的中间结果文件名字。

(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用

预处理一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。典型地,由预处理器对程序源代码文本进行处理,得到的结果再由编译器核心进一步编译。在该过程中,可能进行以下操作:文件包含,条件编译、布局控制,宏替换和删除注释等

在hello.c的预处理阶段中:预处理器(cpp)根据以字符#开头的命令,修改原始的C程序,比如hello。C中的第一行的#include<stdio.h>命令告诉预处理器读取系头文件stdio.h的内容,并把它直接插入程序文本中,结果就得到了另一个hello.c程序,通常以.i作为扩展名。

2.2在Ubuntu下预处理的命令

gcc -E hello.c -o hello.i

图1

2.3 Hello的预处理结果解析

图2

图3

经过预处理器的处理,生成的hello.i文件中的内容相较于hello.c增长了许多,原因是hello.i文件中#include被展开,并加入程序文本,所以文本后仍保有hello.c的内容。源程序中的注释已经删除。同时#include被替换成了相应的头文件。

2.4 本章小结

本章阐述了预处理的概念与作用,并展现了预处理器将程序进行预处理生成.i文件的过程。

通过将hello.c进行预处理,对比二者结果验证预处理的作用。

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

编译是指编译器(ccl)将预处理生成的后缀.i的文件进行编译,生成后缀.s文件的过程。

在编译过程中,编译器通过进行词法分析,语法分析,语义检查和中间代码生成,代码优化等工作过程对源程序进行分析,在分析过程中如果发现错误则给出提示信息,否则将预处理后的文件翻译成汇编程序文件

本文中编译器(ccl)将文本文件hello.i翻译成hello.s,它包含一个汇编语言程序,该程序包含函数main的定义,如下所示

3.2 在Ubuntu下编译的命令

gcc -S hello.i -o hello.s

图4

3.3 Hello的编译结果解析

3.3.1数据:局部变量

在main函数中定义局部变量i:

图5

编译器对其存入栈中

图6

字符串常量

图7

3.3.2main函数中的两个参数

图8

第一个参数是int类型的argc,第二个是char* 数组argv

编译器将他们分别存储在寄存器%edi和%rsi中

图9

3.3.3比较跳转

图10

Argc和4比较,变量i和8比较

图11

图12

3.3.4赋值操作

图13

将0赋值给i,通过mov指令实现

图14

3.3.5算数操作

(1)i++

图15

图16

(2)加法add

图17

3.3.6数组操作

图18

取出argv数组中的值:

图19

3.3.7控制转移

(1)条件控制if

图20

在hello.s中分支结构用跳转指令实现

图21

(2)循环控制for

图22

当i<8时,会跳转至L4,执行循环

图23

3.3.8函数调用

=对printf函数的调用:

图24

将寄存器%rdi,%rsi和%rdx的当做参数传给printf

=对exit函数的调用

图25

将寄存器%rdi中1作为参数传给exit

=对atoi参数的调用

图26

利用寄存器%rdi中的数当做参数

=对sleep函数的调用:

图27

将atoi函数的返回值传给寄存器%edi,并作为参数传给sleep。

=对getchar函数的调用,没有参数传递

图28

3.4 本章小结

本章阐述了编译的概念和作用,并且详细说明了编译器将预处理后得到的hello.i文件进一步翻译为存储在hello.s中的汇编语言的过程。

(第32分)

第4章 汇编

4.1 汇编的概念与作用

汇编器将编译生成的.s后缀文件翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在.o后缀文件中。

汇编的作用是把汇编语言翻译成机器语言,用二进制码代替汇编语言中的符号,即让它成为机器可以直接识别的程序。

4.2 在Ubuntu下汇编的命令

as hello.s -o hello.o

图29

4.3 可重定位目标elf格式

4.3.1生成ELF文件

使用命令 readelf -a hello.o > hello.elf

图30

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

4.3.2ELF头

图31

根据头信息我们可以看到ELF头以一个16字节的序列开始,这个序列描述了生成文件的系统的字大小和字节顺序。ELF头的剩下部分帮助链接器语法分析和解释目标文件的信息:包括ELF头的大小、目标文件的类型、机器类型、节头部表的文件偏移,以及节头部表中条目的大小和数量。

4.3.3节头部表

图32

节头部表,目标文件中的每个节都有一个固定条目体现在表中,指明各个节的信息。

4.3.4重定位节

  .rela.text节是一个.text节中位置的列表。当链接器把这个目标文件和其他文件组合时,需要修改这些位置。一般而言,任何调用外部函数或者应用全局变量的指令都需要修改。

  其中,“偏移量”是需要被修改的引用的字节偏移(在代码节或数据节的偏移),“信息”指示了重定位目标在.symtab中的偏移量和重定位类型,“类型”表示不同的重定位类型,例如图中R_X86_64_PC32就表示重定位一个使用32位PC相对地址的引用。“符号名称”表示被修改引用应该指向的符号,“加数”是一个有符号常数,一些类型的重定位要用他对被修改引用的值做偏移调整。

图33

4.3.4符号表

图34

符号表中存放了程序中定义和引用的函数和全局变量。在hello.c中我们没有定义全局变量,所以符号表中没有他们的身影,只能看到函数。符号表,存放程序中定义和引用的函数和全局变量的信息

4.4 Hello.o的结果解析

objdump -d -r hello.o

图35

(1)机器语言的构成,与汇编语言的映射关系:

·  机器语言:

计算机执行的二进制命令,都是0和1表示的,本质上是由“0”和“1”组成的二进制数。计算机发明之初,人们只能计算机的语言去命令计算机干这干那。向计算机每发出一条指令,就要写出一串串由“0”和“1”组成的指令序列。

机器指令是指我们的CPU能够直接识别并执行操作的指令,它的表现形式是二进制编码。机器指令通常由操作码和操作数两部分组成,操作码代表了操作的类型,操作数是指进行运算的数值。

机器指令又与CPU相关联。不同种类的CPU对应的机器指令也不同,而且指令系统往往相差挺大。

机器语言是用来直接描述机器指令、使用机器指令的规则等。它是CPU能直接的语言。 

·  汇编语言:

用助记符代替机器指令的操作码(如:ADD表示加法),于是人们对其进行了改进 使用一些有含义的助记符 组成了另一种编程语言 汇编语言。

汇编语言是汇编指令、伪指令集和使用它们规则的统称。伪指令是在程序设计师所需要的一些辅助性说明指令,机器并不会执行这些指令。

(2)分析hello.o的反汇编,hello.o的反汇编里的机器语言与hello.s中的汇编语言比较,主要有以下差异:

·  操作数:

在hello.s中为十进制

图36

在反汇编中为十六进制

图37

·  分支转移:

在hello.s中为”L2”等段名称,在hello.s中,分支转移函数通过Lable实现表明跳转地址,如下图

图38

在反汇编中为相对偏移地址,而在hello.o的反汇编文件中,因为我们给每一条指令赋予了一个地址,所以直接使用<函数名 + 偏移量>的形式表明跳转地址,如下图:

图39

·  函数调用

依然使用call指令调用函数,call后跟着的是下一条指令的地址。

在hello.s中为函数名

图40

在反汇编中是调用函数的相对偏移地址

图41

4.5 本章小结

本章阐述了汇编的作用和概念,并详细分析了可重定位目标文件的elf格式,同时将hello.o与hello.s文件做了比较。

(第41分)

第5章 链接

5.1 链接的概念与作用

链接是将各种代码和数据片段收集并组合成一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行于编译时,也就是在源代码被编译成机器代 码时;也可以执行于加载时,也就是在程序被加载器加载到内存并执行时;甚至于运行时,也就是由应用程序来执行。链接是由叫做链接器的程序执行的。链接器在软件开发扮演着一个关键的角色,因为它们使得分离编译成为可能。概念:链接(linking)是将各种代码和数据片段收集并组合成一个大一文件的过程,这个这个文件可被家在(复制)到内存并执行。

作用:链接使分离编译成为了可能。我们不用将一个大型的应用程序组织值为一个巨大的源文件,而是看到一把他分解为更小,更好管理的模块,可以俄uli地修改和编译这些模块。

5.2 在Ubuntu下链接的命令

在查询资料后可以得到链接命令:

图42

Ld -o hello -dynamic-linker /lib64/ld-linux-x86-64.so.2 /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

使用ld的链接命令

图42

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

5.3.1可执行目标文件的ELF格式

在终端中输入指令readelf -a hello打印可执行文件的ELF格式。

图43

5.3.2ELF头

图44

在ELF 头中类型为EXEC表明hello是一个可执行目标文件,有27个节

5.3.3节头

根据 节头中的信息我们就可以用 HexEdit 定位各个节所占的区间(起始位置,大小),地址是程序被载入到虚拟地址的起始地址。

图45

5.3.4符号表

图46

5.4 hello的虚拟地址空间

使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。命令行:edb --run bomb(文件名) 。

  

图47

通过edb可知hello的虚拟地址空间开始于0x00401000,结束于0x00401ff0

5.5 链接的重定位过程分析

在命令行输入指令 objdump -d hello > hello.txt 生成可执行文件年的反汇编代码,通过将其与hello.o的反汇编代码比较,可以分析了解重定位过程。

图48

图49

·  分析hello与hello.o的不同:

不同:

(1)hello的汇编代码中出现了_init,.plt,puts@plt,printf@plt等函数名,而hello.o的汇编代码中只出现了main函数名

(2)hello汇编代码中地址已确定

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

在重定位过程中,链接器首先将符号解析完成,并将代码中的所有符号引用与一个符号定义关联。链接器按照各个内容的具体大小重定位,合并输入模块,为每个符号分配运行时的地址。链接器将所有输入到hello中相同类型的节合并为同一类型的新的节,然后链接器将运行时内存地址赋给新的节,赋给输入模块定义的每个节,以及赋给输入模块定义的每一个符号。最后是重定位节中的符号引用,链接器会修改hello中的代码节和数据节中对每一个符号的引用,使得他们指向正确的运行地址。

5.6 hello的执行流程

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

edb中打开hello,在所有函数处断点,运行程序,记录从开始到程序终止调用的所有函数。

表1

函数

地址

_start ()

0x00000000004010f0

puts()

0x0000000000401030

_init ()

0x0000000000401000

main ()

0x0000000000401125

printf@plt ()

0x0000000000401040

atoi@plt ()

0x0000000000401060

sleep@plt ()

0x0000000000401080

getchar@plt ()

0x0000000000401050

_fini ()

0x00000000004011c0

5.7 Hello的动态链接分析

 动态链接库中的函数在程序执行的时候才会确定地址,所以编译器无法确定其地址。为避免运行时修改调用模块的代码段,链接器采用延迟绑定的策略。延迟绑定通过两个数据结构之间简洁但又有些复杂的交互来实现,即过程链接表(PLT)和全局偏移量表(GOT)。

 过程链接表(PLT):PLT是一个数组,其中每个条目是16字节代码。PLT [0]是一个特殊条目,它跳转到动态链接器中。每个被可执行程序调用的库函数都有它自己的PLT条目。每个条目都负责调用一个具体的函数。每个条目都负责调用一个具体的函数。

全局偏移量表(GOT):GOT是一个数组,其中每个条目是8字节地址。和PLT联合使用时,GOT [0]和GOT [1]包含动态链接器在解析函数地址时会使用的信息。GOT [2]是动态链接器在1d-linux.so模块中的入口点。其余的每个条目对应于一个被调用的函数,其地址需要在运行时被解析。每个条目都有一个相匹配的PLT条目。

通过节头目表可知,hello的.got.plt段的起始位置是0x404000,使用edb查看

调用前的内容:

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

hello的elf中got表的位置

图50

图51  dl_init前

图52  dl_init后

5.8 本章小结

本章中主要介绍了链接的概念与作用,并且详细阐述了hello.o是怎么链接成为一个可执行目标文件的过程,详细介绍了hello.o的ELF格式和各个节的含义,并且分析了hello的虚拟地址空间、重定位过程、执行流程、动态链接过程。

(第51分)

第6章 hello进程管理

6.1 进程的概念与作用

进程:是一个程序在操作系统中的执行实例,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

进程为用户提供了这样的假象,我们的程序好像是系统中当前运行的唯一程序一样,我们的程序好像是独占的使用处理器和内存,处理器好像是无间断地执行我们程序中地指令,我们程序中的代码和数据好像是系统内存中唯一的对象。

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

Shell概念: shell本意是指壳,在计算机术语中,shell是指用户操作接口的意思。操作系统运行起来后都会给用户提供一个操作接口,这个操作接口就叫shell。

用户可以通过shell来调用操作系统内部的复杂实现。

Shell 是一个应用程序,它连接了用户和 Linux 内核,让用户能够更加高效、安全、低成本地使用 Linux 内核,这就是 Shell 的本质。

Shell 本身并不是内核的一部分,它只是在内核的基础上编写的一个应用程序,它和 QQ、迅雷、Firefox 等其它软件没有什么区别。

然而 Shell 也有着它的特殊性,就是开机立马启动,并呈现在用户面前;用户通过 Shell 来使用 Linux,不启动 Shell 的话,用户就没办法使用 Linux。

功能:shell应用程序提供了一个界面,用户通过访问这个界面访问操作系统内核的服务。 

处理流程:

1)从终端读入输入的命令。

2)将输入字符串切分获得所有的参数

3)如果是内置命令则立即执行

4)否则调用相应的程序执行

5)shell 应该接受键盘输入信号,并对这些信号进行相应处理

6.3 Hello的fork进程创建过程

运行shell,在shell上运行hello程序,当读到./hello命令时,shell作为父进程通过fork函数为hello创建一个新的进程作为子进程。通过fork函数,子进程得到与父进程用户级虚拟地址空间相同但独立的一份副本,包括代码和数据段、堆、共享库、用户栈。hello进程还获得与父进程任何打开文件描述符相同的副本,这就意味着当父进程调用fork时,子进程还可以读写父进程中打开的任何文件。父进程和新创建的子进程之间最大的区别在于它们有不同的PID。

6.4 Hello的execve过程

fork子进程创建后,shell调用execve函数加载并运行可执行目标文件hello,且带参数列表argv和环境变量列表envp。之后当出现错误时,例如找不到hello,execve才会返回到调用程序。

在execve加载了hello后,它调用启动代码,启动代码设置栈,并将控制转移给新程序的主函数main,此时用户栈已经包含了命令行参数和环境变量,进入main函数后开始逐步运行程序。

6.5 Hello的进程执行

在hello的进程执行中,内核为每个进程维持一个上下文,上下文就是内核重新启动一个被抢占的进程所需要的状态,它由通用寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构等对象的值构成。

调度过程:

从hello进程进入内核;

内核进行上下文切换,执行和hello并发的其他进程;

Sleep休眠时间结束;

并发进程进入内核;

内核进行上下文切换,继续执行hello进程。

6.6 hello的异常与信号处理

=异常:中断、陷阱、故障、终止

=信号:SIGSTP、SIGCONT、SIGKILL、SIFGINT等。

=中断是来自I/O设备的信号,异步发生,中断处理程序对其进行处理,返回后继续执行调用前待执行的下一条代码,就像没有发生过中断。

=陷阱是有意的异常,是执行一条指令的结果,调用后也会返回到下一条指令,用来调用内核的服务进行操作。帮助程序从用户模式切换到内核模式。

=故障是由错误情况引起的,它可能能够被故障处理程序修正。如果修正成功,则将控制返回到引起故障的指令,否则将终止程序。

=终止是不可恢复的致命错误造成的结果,通常是一些硬件的错误,处理程序会将控制返回给一个abort例程,该例程会终止这个应用程序。

6.6.1运行中不停乱按

图53

结果:无影响。

6.6.2运行中输入回车:

图54

将输入的内容保存到缓冲区,待当前hello进程结束后,shell读入缓冲区内容。

6.6.3运行中按ctrl-c。

图55

原理:shell父进程收到SIGINT信号,信号处理函数将hello进程终止并回收。

6.6.4运行中按ctrl-z

图56

原理:shell父进程收到SIGSTP信号,信号处理函数将hello进程挂起。

6.6.5ctrl-z后运行fg

图57

原理:发送SIGCONT信号继续执行hello。

6.6.6ctrl-z后运行kill

图58

原理:命令发送SIGKILL信号杀死hello。

6.6.7ctrl-z后运行ps、jobs、pstree命令,输出相关信息。

图59

原理:ps命令输出当前系统中的进程;jobs命令输出当前已启动的任务状态;pstree命令输出进程间的树状关系。

6.7本章小结

本章介绍了程序在shell执行及进程的相关概念,说明了hello程序在shell中通过fork函数及execve创建新的进程并执行程序的过程,内核的调度及上下文切换等机制。研究了hello执行过程中各种操作可能引发的异常和信号处理。

(第61分)

第7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:逻辑地址指的是机器语言指令中,用来指定一个操作数或者是一条指令的地址,即程序的机器代码中保存的地址。

线性地址(也叫虚拟地址):现代系统提供了一种对主存的抽象概念,叫做虚拟内存。使用虚拟寻址,CPU通过生成一个虚拟地址来访问主存,这个虚拟地址在被送到内存之前先转换为适当的物理地址。

物理地址:物理地址是用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。

图60

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

一个逻辑地址由两部分组成,段标识符和段内偏移量。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号。后面3位包含一些硬件细节。可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就描述了一个段。一些全局的段描述符,就放在“全局段描述符表(GDT)”中,一些局部的,例如每个进程自己的,就放在所谓的“局部段描述符表(LDT)”中。

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

页表是一个存放在物理内存中的数据结构,将虚拟页映射到物理页。每次地址翻译硬件将一个虚拟地址转换为物理地址时读取页表。操作系统负责维护页表中的内容,以及再磁盘与DRAM之间来回传送页。

页式管理,是将虚拟地址空间划分为一个一个固定大小的块(称作虚页),同一时候也让实际地址空间也划分为一个一个相同大小的页(称作实页)。在页式管理中,由虚拟地址寻到实际主存地址的步骤为先找到页表基址寄存器,获得相应页表的基地址,然后用基址与虚页号做一次加法找到相应的实页号,由实页号和页内位移组成实际主存地址。

图61

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

Core i7 MMU 使用四级的页表将虚拟地址翻译成物理地址。36位VPN 被划分成四个9 位VPN,分别用于一个页表的偏移量。

CPU产生虚拟地址VA,VA传送给MMU,MMU使用前36位VPN作为TLBT+TLBI向TLB中匹配,如果命中,则得到40位PPN+12位VPO组合成52位物理地址PA。如果没有命中,MMU向页表中查询,CR3确定第一级页表的起始地址,9位VPN1确定在第一级页表中的偏移量,查询出第一部分PTE,以此类推最终在四级页表都访问完后获得PPN,与VPO结合获得PA,并向TLB中更新。

图62

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

得到物理地址之后,先将物理地址拆分成CT(标记)+CI(索引)+CO(偏移量),然后在一级cache内部找,如果未能寻找到标记位为有效的字节(miss)的话就去二级和三级cache中寻找对应的字节,找到之后返回结果。

图63

7.6 hello进程fork时的内存映射

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

7.7 hello进程execve时的内存映射

删除已存在的用户区域,创建新的区域结构,代码和初始化数据映射到.text和.data区(目标文件提供),.bss和栈映射到匿名文件,设置PC,指向代码区域的入口点。

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

DRAM 缓存不命中称为缺页,即虚拟内存中的字不在物理内存中。缺页导致页面出错,产生缺页异常。缺页异常处理程序选择一个牺牲页,然后将目标页加载到物理内存中。最后让导致缺页的指令重新启动,页面命中。

7.9本章小结

本章阐述了Linux存储器的地址空间,Intel的段式管理和页式管理机制,以及TLB与多级页表支持下的VA到PA的转换,三级cache支持下的物理内存访问。说明了hello的fork和execve内存映射,缺页故障与缺页中断处理程序。

(第7 2分)

 

结论

hello一生的经历了:程序编写,预处理,编译,汇编,链接中生成可执行文件hello。

然后开始运行:在shell中输入命令行./hello, shell进程调用fork为其创建子进程,shell调用execve,execve调用启动加载器,加映射虚拟内存,进入程序入口后程序开始载入物理内存,然后进入 main函数

接着:执行指令,访问内存,动态申请内存,异常控制流(如果运行途中键入ctr-c或ctr-z或进行其他操作,则调用shell的信号处理函数进行相应处理)。

最后结束:shell父进程回收子进程,内核删除为hello进程创建的所有数据结构。

至此,hello结束从系统中消失。

经过本课程的学习,大大提高了我对计算机的兴趣,hello是我们作为程序员来说最早接触的程序,也是最简单的程序之一,而如此小的一个程序,在如此短的时间内,需要接触计算机系统的各个方面,这也许恰恰体现了计算机系统这门课的魅力,感谢编写这本书的作者以及将知识传授给我们的老师们,感谢在计算机行业做出贡献的科研工作者们!

(结论0分,缺失 -1分,根据内容酌情加分)

附件

hello.c     源文件

hello.i     预处理文件

hello.s     汇编语言文件

hello.o         可重定位目标文件

hello       可执行目标文件

hello.elf   hello.o的elf格式文件

hello hxy.elf    hello的elf格式文件

参考文献

  1. 深入理解计算机系统(CSAPP)

[2]菜菜2022. ( 2020-03-03 16:15:13 发布). 编译预处理详述. csdn. 

https://blog.csdn.net/kz_java/article/details/104632270?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168482645516800182723803%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=168482645516800182723803&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-3-104632270-null-null.142^v87^control_2,239^v2^insert_chatgpt&utm_term=%E9%A2%84%E5%A4%84%E7%90%86%E7%9A%84%E6%A6%82%E5%BF%B5%E5%90%88%E4%BD%9C%E7%94%A8&spm=1018.2226.3001.4187

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/53077.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

程序人生-Hello’s P2P

计算机系统 大作业 题 目 程序人生-Hello’s P2P 专 业 未来技术 学   号 2021112807 班 级 21WL021 学 生 马铭杨 指 导 教 师 史先俊 …

Google reCAPTCHA ----------验证码

现有验证码的产品形态调研范围如下&#xff0c;基本涵盖了比较主流的验证码平台&#xff1a; Google reCAPTCHA极验阿里云腾讯云点触网易易盾螺丝帽FunCaptcha 产品背景 ‍‍reCAPTCHA起初是由CMU&#xff08;卡耐基梅隆大学&#xff09;设计&#xff0c;将OCR&#xff08;光…

关于captcha验证码演示

转载&#xff1a;https://blog.csdn.net/dayonglove2018/article/details/106612549 import com.wf.captcha.SpecCaptcha; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springfr…

使用reCAPTCHA实现验证码

文章目录 HTML代码JS代码Java代码项目开源地址参考资料 HTML代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>登录</title><link rel"stylesheet" type"text/css" href"css/json-v…

手把手教你验证码检验的登录

在网站实际应用过程中&#xff0c;为了防止网站登录接口被机器人轻易地使用&#xff0c;产生一些没有意义的用户数据&#xff0c;所以&#xff0c;采用验证码进行一定程度上的拦截&#xff0c;当然&#xff0c;我们采用的还是一个数字与字母结合的图片验证码形式&#xff0c;后…

如何识别高级的验证码

http://sebug.net/paper/pst_WebZine/pst_WebZine_0x02/html/PSTZine_0x02_0x09.html Ph4nt0m Security TeamIssue 0x02, Phile #0x09 of 0x0A|---------------------------------------------------------------------------| |-----------------------[ 如何识别高级的验证码…

hcaptcha 我是人类验证码怎么跳过怎么验证自动识别

相信这个验证码很多人都见过&#xff0c;这个叫hcaptcha验证码 在网页上偶尔出现&#xff0c;提示需要你证明“我是人类” 这种验证码与谷歌的reCaptcha有异曲同工之处&#xff0c;但是其实hcaptcha与recaptcha是完全不同的产品&#xff0c;不是同一个公司出品的。 这种hcapt…

手把手教你识别FunCaptcha验证码

今天&#xff0c;我们将专注于FunCaptcha&#xff0c;这是一种独特而具有挑战性的CAPTCHA类型&#xff0c;在整个网络上越来越流行。我们将深入探讨FunCaptcha是什么&#xff0c;不同类型的FunCaptcha挑战&#xff0c;如何使用CapSolver解决它们等等。 什么是FunCaptcha&#…

基于openai chatgpt和embeddings制作私有知识库聊天机器人

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、原理、流程二、制作预料库三、制作问答功能总结 如果有问题可以联系我**&#xff1a;https://gitee.com/xiaoyuren/gpt3 前言 在当今信息爆炸的时代&#…

基于 Quivr 搭建个人知识库

目录 Quivr介绍 Quivr特性 Quivr演示 Demo with GPT3.5: Demo of the new version&#xff1a; Quivr实战 Quiv 使用的主要技术 Quiv 实践依赖 创建Supabase项目 部署Quiv项目 第一步&#xff1a;现在源码 第二步&#xff1a;设置环境变量 第三步&#xff1a;执行sql 第…

标书打印分册小技巧

标书打印出来后&#xff0c;一般都有很多本&#xff0c;去打印店胶装标书时&#xff0c;需要把每一本标书分出来&#xff0c;黑帽大师用便签纸就能方便的分出标书。 把便签纸贴在每本标书的最后一页上&#xff0c;这样就能方便的分出每一本了。

学校计算机维护投标书,信息化系统硬件及应用系统安全运维服务投标书范本

这是一份信息化系统硬件及应用系统安全运维服务投标书范本&#xff0c;含运维服务方案&#xff0c;word格式&#xff0c;可编辑&#xff0c;有需要的朋友可以参考学习。 信息化系统硬件及应用系统安全运维服务 本次服务范围为XX局信息化系统硬件及应用系统&#xff0c;各类软硬…

招投标小程序开发功能及源码

一般获取招投标信息的渠道主要有三种&#xff0c;一&#xff0c;来源于官方、正规的政府网站、公共资源交易中心等&#xff1b;二&#xff0c;能提供针对性的招投标信息平台&#xff1b;三是通过个人的人脉资源来获取项目信息。今天我们重点讲下招投标平台怎么运营的&#xff0…

python制作标书_爬取比比网中标标书,并保存为PDF格式文件

前言 本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。 以下文章来源于CSDN&#xff0c;作者嗨学编程 python开发环境 python 3.6 pycharm import requests import parsel import pdfkit import time 相关模块pip安装即可 …

python制作标书_Python爬取比比网中标标书并保存成PDF格式

前言 本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。 python开发环境 python 3.6 pycharm requests parsel pdfkit time 相关模块pip安装即可 目标网页分析 1、先从列表页中获取详情页的URL地址 是静态网站,可以直接请求…

第一次写标书

由于工作需要开始写起标书。前后大概花了五天时间。 经过自我学习和老师指导&#xff0c;知道了一件事情&#xff0c;不管做什么&#xff0c;其实都是能够有所学习的。 而学习&#xff0c;为了有所收获&#xff0c;需要用心再去体会每一个过程&#xff0c;并记录下来&#xf…

小程序投标书_快来学习招投标小技巧!中标率提高50%(建议收藏)

99%的投标人使用【建企同盟APP】都中标了&#xff01; 建企同盟APP 招标信息不遮挡 订阅推送零费用 从保证中标的因素来看&#xff0c;三个因素最为重要&#xff0c;首先是关系&#xff0c;其次是能力&#xff0c;最后才是价格。关系指与用户的关系&#xff0c;既有最终用户又包…

小程序投标书_程序员接私活常用哪些平台?

给大家推荐国内外几个接外包比较靠谱的平台&#xff0c;相对来说规模和专业性都还不错。 想要接外包或者积累行业人脉的小伙伴都可以收藏一波&#xff1a; 国外篇 如果打算接国外的软件外包&#xff0c;首先以下几点能力需要提前掌握&#xff1a; 基本的英语沟通能力(能够基本沟…

重磅:AI 的 “iPhone 时刻” 已经到来

大家好&#xff0c;我是校长。 上周英伟达 CEO 黄仁勋在 GTC 大会主题演讲火爆了全网。 一起来看看黄仁勋说了什么。 英伟达 CEO 黄仁勋在 GTC 大会主题演讲上开场时这么说&#xff1a; “近四十年来&#xff0c;摩尔定律一直是引领计算机行业动态发展的重要规律&#xff0c;而…

AI内容生成检查器

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版&#xff0c;欢迎购买。点击进入详情 AI进行内容生成已经是是当下的人们话题&#xff0c;那么怎么判断文本的内容是不是AI生成的呢&#xff1f;AI 生成的句子将被突出显示&#xff0c;目前工具可以检测…