hit_2019_计算机系统大作业_tlx

在这里插入图片描述

计算机系统

大作业

题 目 程序人生-Hello’s P2P
专 业 计算机
学   号 1183710127
班   级 1837101
学 生 陶亮旭   
指 导 教 师 史先俊

计算机科学与技术学院
2019年12月
摘 要
本文通过对hello程序的P2P和020过程的研究,简述了一个hello.c的完整的生命周期,从它被编译开始,一直到汇编、链接,然后在系统中运行,通过shell及其他Linux内置程序对进程运行过程进行了分析,以运行hello程序为核心,展开的介绍了程序的进程管理,相关数据的存储管理,和I/O管理等内容。
关键词:预处理;编译;汇编;链接;进程;shell;

(摘要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 本章小结 - 6 -
第3章 编译 - 7 -
3.1 编译的概念与作用 - 7 -
3.2 在UBUNTU下编译的命令 - 7 -
3.3 HELLO的编译结果解析 - 7 -
3.4 本章小结 - 10 -
第4章 汇编 - 12 -
4.1 汇编的概念与作用 - 12 -
4.2 在UBUNTU下汇编的命令 - 12 -
4.3 可重定位目标ELF格式 - 12 -
4.4 HELLO.O的结果解析 - 15 -
4.5 本章小结 - 16 -
第5章 链接 - 17 -
5.1 链接的概念与作用 - 17 -
5.2 在UBUNTU下链接的命令 - 17 -
5.3 可执行目标文件HELLO的格式 - 17 -
5.4 HELLO的虚拟地址空间 - 19 -
5.5 链接的重定位过程分析 - 20 -
5.6 HELLO的执行流程 - 21 -
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本章小结 - 30 -
第7章 HELLO的存储管理 - 31 -
7.1 HELLO的存储器地址空间 - 31 -
7.2 INTEL逻辑地址到线性地址的变换-段式管理 - 31 -
7.3 HELLO的线性地址到物理地址的变换-页式管理 - 33 -
7.4 TLB与四级页表支持下的VA到PA的变换 - 34 -
7.5 三级CACHE支持下的物理内存访问 - 34 -
7.6 HELLO进程FORK时的内存映射 - 35 -
7.7 HELLO进程EXECVE时的内存映射 - 35 -
7.8 缺页故障与缺页中断处理 - 35 -
7.9动态存储分配管理 - 36 -
7.10本章小结 - 36 -
第8章 HELLO的IO管理 - 37 -
8.1 LINUX的IO设备管理方法 - 37 -
8.2 简述UNIX IO接口及其函数 - 37 -
8.3 PRINTF的实现分析 - 38 -
8.4 GETCHAR的实现分析 - 38 -
8.5本章小结 - 39 -
结论 - 39 -
附件 - 40 -
参考文献 - 41 -

第1章 概述
1.1 Hello简介
P2P:From program to process,即从编程到可执行文件。在Linux中,hello.c经过预处理、编译、汇编、链接变成了hello.out,就是P2P的全过程。
预处理阶段:预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。
编译阶段:编译器(ccl)将文本文件hello.i翻译成hello.s。
汇编阶段:汇编器(as)将hello.s翻译成机器语言指令,并把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件hello.o中。
连接阶段:hello程序调用了printf函数,它存在于一个名为printf.o的单独的预编译好了的目标文件中,而这个文件必须以某种方式合并到我们的hello.o程序中,连接器(ld)就负责处理这种合并。
在这里插入图片描述

020的过程:shell为其execve,映射虚拟内存,进入程序入口后程序开始载入物理内存,然后进入 main函数执行目标代码,CPU为运行的hello分配时间片执行逻辑控制流。当程序运行结束后,shell父进程负责回收hello进程,内核删除相关数据结构。
1.2 环境与工具
硬件环境:在这里插入图片描述
软件环境:Window 10、Ubuntu 18.04
开发与调试工具:gcc,Codeblocks,edb,readelf ,as,ld,
1.3 中间结果
hello.c :hello源代码
hello.i :预处理后的文本文件
hello.s :hello.i编译后的汇编文件
hello.o :hello.s汇编后的可重定位目标文件
hello_objdump :hello的反汇编代码
hello.o_objdump :hello.o的反汇编代码
hello :链接后的可执行文件
1.4 本章小结
本章简述了hello的P2P和020的过程,以及在进行试验过程中的软硬件环境和开发与调试工具,还列出了在中间过程中生成的中间结果文件。
(第1章0.5分)

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

2.2在Ubuntu下预处理的命令
命令:gcc -E hello.c -o hello.i
在这里插入图片描述
2.3 Hello的预处理结果解析
预处理就是把.c添加头文件,扩展成一个新的.i文本文件,打开hello.i可以发现他前面的头文件等被展开了,变成了很多以#开头的内容,hello.i的最下面是main函数。
2.4 本章小结
本章完成了对hello.c的预处理工作,是P2P的第一步,将.c转换成了.i,对宏定义、文件包含、条件编译进行了展开,但此时代码还是属于高级语言,完成该阶段后,便可进行汇编。
(第2章0.5分)

第3章 编译
3.1 编译的概念与作用
编译器(ccl)将文本文件.i翻译成.s,它包含一个汇编语言程序。该程序包含函数main的定义。如:在这里插入图片描述

作用:将高级语言转变为更易于计算机读懂的汇编语言,汇编语言是一种低级语言,它是高级语言和机器语言之间的桥梁。此外,编译程序还具有语法检查、调试措施、修改手段、覆盖处理、目标程序优化、不同语言合用以及人机联系等重要功能。
3.2 在Ubuntu下编译的命令
命令:gcc -S hello.i -o hello.s 在这里插入图片描述
3.3 Hello的编译结果解析
3.3.1常量
Hello.c常量都是在语句中出现的,这种数都用立即数表示。
3.3.2 变量
hello.c中有个全局变量sleepsecs,编译后被存放在.rotate节中,源文件中sleepsecs定义为int型,编译器将其转为long,原来赋初值2.5被转换为2.
此外还有一个局部变量i,可看出是保存在栈里
3.3.3 赋值
一是对全局变量sleepsecs的初始化赋值,在.data声明
二是对局部变量i赋值,用了movl,通过mov操作将茶树0赋值给i。
3.3.4 类型转换
hello.c中存在一个隐式的类型转换,将定义的int型全局变量sleepsecs从浮点数转为整形,其值浮点数2.5向偶舍入为整形2,以对程序起到优化作用。
3.3.5 算术操作
hello.c中循环语句中的循环变量i++便是一处算术操作,通过ADD操作实现。在这里插入图片描述
此外还有减、乘等操作
SUB S, D D←D-S 减法
IMUL S, D D←D*S 乘法
3.3.6 关系操作
关系操作在判断语句中,根据比较的结果进行跳转。
以hello.c中的第一个判断为例,其汇编代码为 在这里插入图片描述就是先作差,然后根据标志位决定是否跳转到L2.
3.3.7 数组
数组,就是一段数据类型相同的物理位置相邻的变量集合。
hello.c中也有数组在这里插入图片描述 ,数组char *argv[]是main的参数,调用时根据栈指针加上偏移量来寻址。
3.3.8 控制转移
控制转移是在条件判断时出现的。比如 在这里插入图片描述, 通过cmpl进行判断,访问条件码,用jump指令配合条件控制来执行控制转移。
再比如:在这里插入图片描述 ,这里是循环语句,每次将计数器的值与7进行比较,在这里插入图片描述 从汇编代码中可以看出,每次符合条件就跳到L4执行。
3.3.9函数操作
参数传递:在函数的参数传递中使用不同的寄存器来保存参数。在这里插入图片描述
函数调用:使用call语句来实现。
函数返回:函数的返回值保存在%rax中,将需要返回的变量值存在%rax中,在进行函数的操作之后ret即可返回%rax中的值。
在hello.c中除了有main、printf、sleep、getchar、exit五个函数调用外,在汇编代码中还可看到 在这里插入图片描述puts,在这里插入图片描述 atoi这两个函数。
其中只有getchar没有函数调用。

3.4 本章小结
编译把源程序代码转为汇编语言代码,通过.i生成了.s,得到了汇编语言源代码文本,通过与源文件C程序代码进行比较,完成了对汇编代码的解析工作。汇编后的文件仍为文本文件。汇编代码为接下来生成机器代码奠定了基础。
(第3章2分)

第4章 汇编
4.1 汇编的概念与作用
概念:汇编器(as)将.S翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序(relocatable object program)的格式,并将结果保存在目标文件.o中。

作用:将汇编代码转换为机器指令,使之在连接后能够直接被计算机执行。

4.2 在Ubuntu下汇编的命令
命令:gcc -c hello.s -o hello.o
在这里插入图片描述
4.3 可重定位目标elf格式
先上表
在这里插入图片描述

ELF头:ELF头描述了生成该文件的系统的字的大小和字节顺序,并且包含帮助链接器语法分析和解释目标文件的信息。
在这里插入图片描述
节头部表:节头部表描述了不同节的位置和大小,其中目标文件中每个节都有一个固定大小的条目。
在这里插入图片描述
从上图中我们可以看出hello.o是可重定位文件,节头大小为64字节,数量13,字符串表索引节头为12。每个节的起始地址都为0,便于重定位操作。

重定位节:可以看到.rodata、puts、exit、printf、atoi、sleep、getchar等的偏移。链接器会根据重定向节的信息对可重定向的目标文件进行连接得到可执行文件。
在这里插入图片描述
.symtab节:是一个符号表,它存放着在程序中定义和引用的函数和全局变量的信息。
在这里插入图片描述
4.4 Hello.o的结果解析
在这里插入图片描述 在这里插入图片描述
从总体来看,hello.o与hello.s的总体流程相同。hello.o的反汇编与 hello.s进行对照分析,发现hello.o的比hello.s在左侧多了一些十六进制的数字,这些就是机器码,能看出每行十六进制数代表着一个指令操作,存在一一对应的关系。
此外主要有一下的差别
操作数:hello.o中反汇编的操作数是十六进制,而hello.s中的操作数是十进制。
分支转移:hello.s中使用标志来进行分支转移,而在hello.o中则使用相对寻址方式进行分支操作,变得更加具体,从跳转到段名称变成了跳转到指定偏移地址。因为函数只有在链接之后才能确定运行执行的地址,因此会变式将call指令后的地址设为0.
函数调用:函数调用的地址从函数名称变成了主函数首地址加上偏移量,hello.s中是call函数名,而hello.o反汇编文件中调用函数的操作为call下条指令的地址
对全局变量的引用:hello.s中是.LC0(%rip),hello.o中则是$0x0

4.5 本章小结
通过汇编生成可重定位的目标文件hello.o,文件类型从文本文件转换为二进制文件,汇编语言转化为机器语言。hello.o可以和其他目标文件一起链接成最后的可执行目标文件。通过比较汇编代码与机器代码的异同,更深刻的理解了汇编语言到机器语言的转变。
(第4章1分)

第5章 链接
5.1 链接的概念与作用
概念:链接程序将分别在不同的目标文件中编译或汇编的代码收集到一个可直接执行的文件中。它还连接目标程序和用于标准库函数的代码,以及连接目标程序和由计算机的操作系统提供的资源。

作用:将模块化编写的程序链接起来,成为一个整体,实现程序功能,这样提高了空间利用率,源程序文件中无需包含共享库的所有代码,直接调用即可。可执行文件运行时的内存中只需要包含所调用的函数的代码。

5.2 在Ubuntu下链接的命令
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
在这里插入图片描述
5.3 可执行目标文件hello的格式

文件头:可看出该文件是可执行目标文件,有31个节。
在这里插入图片描述

节头表:
在这里插入图片描述
符号表:
在这里插入图片描述

根据上图可以得到各段的基本信息。hello.out是可执行目标文件,所以每个段的起始地址都不同,他们的起始地址分别对应着装载到虚拟内存中的虚拟地址。这样可以直接从文件起始处得到各段的起始位置,以及各段所占空间的大小。
5.4 hello的虚拟地址空间
根据5.3的信息,使用edb可以查看本进程虚拟地址的空间,从0x400000开始,到0x400fff结束,这一部分连续存放的是elf中的section headers中的address。
在这里插入图片描述
.interp节:
在这里插入图片描述
.init节:
在这里插入图片描述
.plot节:
在这里插入图片描述

.text节:
在这里插入图片描述
.data节:
在这里插入图片描述

5.5 链接的重定位过程分析
在这里插入图片描述
通过对比,发现如下不同:
链接增加新的函数:在hello中链接加入了在hello.c中用到的函数,如exit、prinf、sleep、getchar等函数。
增加节:hello中增加了.init和.plt节,和一些节中定义的函数。
函数调用:hello中无hello.o中的重定位条目,并且跳转和函数调用的地址在hello中都变成了虚拟内存地址。
地址访问:hello.o中的相对偏移地址变成了hello中的虚拟内存地址。我们可以发现,在链接后.text和.plt的相对位置确定,因此在跳转时无需再利用偏移量,可以直接通过:目标地址 = PC + 偏移量,来计算出函数地址来进行跳转。

根据hello与hello.o的不同,得出链接过程为:链接器ld将各个目标文件组装在一起,就是把.o文件中的各个函数段按照一定规则累积在一起,并生成可执行文件。
5.6 hello的执行流程
0x400488 init
0x4004a0 .plt
0x4004b0 puts@plt
0x4004c0 printf@plt
0x4004do geychar@plt
0x4004e0 exit@plt
0x4004f0 sleep@plt
0x400500 _start
0x400530 _dl_relocate_static_pie
0x400532 main
0x4005c0 _libc_csu_init
0x400630 _libc_csu_fini
0x400634 fini
5.7 Hello的动态链接分析
在dl_init调用前,对于每条PIC函数调用,调用的目标地址都实际指向PLT中的代码逻辑,GOT中存放的是PLT中函数调用指令的下一条指令地址。动态链接采用了延迟加载的策略,即在调用函数时才进行符号的映射,下图是调用do_init前的,可看出0x00601000开始的global_offset表是全0的状态。
在这里插入图片描述

do_init调用后:
在这里插入图片描述
由上图可以看出,在执行过dl_init之后该地址被赋上了相应的偏移量的值。这说明dl_init操作是给程序赋上当前执行的内存地址偏移量 。

5.8 本章小结
通过本章理解了Linux中链接的过程,这是hello在成为process之前的最后一步,通过查看hello的虚拟地址空间,并且对比hello和hello.o的反汇编代码,更好的掌握了链接尤其是重定位的过程。经过链接,ELF可重定位的目标文件变成可执行的目标文件,链接后,程序便能作为进程通过虚拟内存机制直接运行。

(第5章1分)

第6章 hello进程管理
6.1 进程的概念与作用
概念:进程是运行中的程序的实例,每一个进程都有它自己的地址空间,一般包括文本区域、数据区域、和堆栈。文本区域存储处理器执行的代码。进程为程序的运行创建上下文,即为程序运行搭建环境,系统中每个程序都运行在某个进程的上下文中。

作用:进程创造了一种假象,即程序独占地使用处理器和内存,并且不间断地执行其指令。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。
6.2 简述壳Shell-bash的作用与处理流程
Shell的作用:Shell是一个命令解释器,它解释由用户输入的命令并且把它们送到内核,提供了用户与内核进行交互操作的一种接口,它允许用户编写由shell命令组成的程序,它是用户使用Linux的桥梁。
处理流程:
shell 首先从终端读入输入的命令,将输入字符串切分获得所有的参数,检查命令是否是内部命令,如果是则立即执行。若不是再检查是否是一个应用程序,fork子进程,启动加载器在当前进程中加载并运行程序。如果键入的命令不是一个内部命令并且在路径里没有找到这个可执行文件,将会显示一条错误信息。shell应该接受键盘输入信号,并对这些信号进行相应处理。
6.3 Hello的fork进程创建过程
shell作为父进程通过fork函数为hello创建一个新的进程,供其执行。通过fork函数,子进程得到与父进程用户级虚拟地址空间相同的(但是独立的)一份副本,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事,父进程和新创建的子进程之间最大的差别在于它们有不同的PID。
父进程与子进程是并发运行的独立进程,内核能够以任意方式交替执行它们的逻辑控制流的指令。在子进程执行期间,父进程默认选项是显示等待子进程的完成,在父进程中,fork返回子进程的PID,在子进程中,fork返回0。
6.4 Hello的execve过程
fork之后,execve函数在当前进程的上下文中加载并运行一个新程序,即我们的可执行目标文件hello,execve调用启动加载器来执行hello程序,加载器删除子进程现有的虚拟内存段,并创建一组新的代码、数据、堆和栈段,新的代码与数据段被初始化为可执行文件的内容,exceve做的最后一件事就是设置当前进程的上下文中的程序计数器,使之指向代码区域的入口点,跳到_start,_start最终调用hello中的main函数。
6.5 Hello的进程执行
多个流并发地执行的一般现象被称为并发。一个进程和其他进轮流运行的概念称为多任务。一个进程执行它的控制流的一部分的每一时间段叫做时间片。因此,多任务也叫做时间分片。
上下文:进程的物理实体(代码和数据等)和支持进程运行的环境合称为进程的上下文;由进程的程序块、数据块、运行时的堆和用户栈(两者通称为用户堆栈)等组成的用户空间信息被称为用户级上下文,用户级上下文地址空间和系统级上下文地址空间一起构成了一个进程的整个存储器映像。
hello程序执行过程中同样存储时间分片,与操作系统的其他进行并发运行。并发执行涉及到操作系统内核采取的上下文交换策略。内核为每个进程维持一个上下文,上下文就是内核重新启动一个先前被抢占的进程所需的状态。为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换。进程上下文切换有四步,分别为决定是否作上下文切换以及是否允许作上下文切换,保存当前执行进程的上下文,使用进程调度算法,选择一处于就绪状态的进程,恢复或装配所选进程的上下文,将CPU控制权交到所选进程手中。
sleep调用完成后,系统再进入hello进程,回到用户态。
6.6 hello的异常与信号处理
hello执行过程中可能出现四类异常:中断、陷阱、故障和终止。
中断是来自I/O设备的信号,异步发生,中断处理程序对其进行处理,返回后继续执行调用前待执行的下一条代码,就像没有发生过中断,信号为信号SIGTSTP。
陷阱是有意的异常,是执行一条指令的结果,调用后也会返回到下一条指令,用来调用内核的服务进行操作。帮助程序从用户模式切换到内核模式。
故障是由错误情况引起的,它可能能够被故障处理程序修正。如果修正成功,则将控制返回到引起故障的指令,否则将终止程序。
终止是不可恢复的致命错误造成的结果,通常是一些硬件的错误,处理程序会将控制返回给一个abort例程,该例程会终止这个应用程序,信号为信号SIGINT。
在这里插入图片描述
上图在hello运行时什么都不按,程序执行完成后,按回车结束。
在这里插入图片描述

如上图,运行时乱按,不会影响什么,当按到回车后,getchar会读入回车符,后面输入的字符串会当作shell命令行输入。
在这里插入图片描述

如上图,运行时按Ctrl+Z后父进程收到SIGTSTP信号,将hello进程挂起,此时子进程并不会被回收。当其收到特定信号时,会继续执行。
然后运行ps:列出来了当前系统中的进程。
在这里插入图片描述
然后运行jobs:列出来了当前shell环境中已启动的任务状态。
在这里插入图片描述
然后运行pstree:pstree命令是以树状图显示进程间的关系。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

然后是fg:将进程调到前台。
在这里插入图片描述
然后是kill:发送信号给一个或多个进程。
在这里插入图片描述
Ctrl-c:hello进程终止,并向父进程发送SIGINT(进程终止)信号,由父进程负责完成子进程的回收。
在这里插入图片描述
6.7本章小结
进程的概念让程序执行时仿佛独占使用处理器和内存,使得并发执行的过程变得井然有序,方便了程序运行时的分析。通过本章,了解了hello进程的执行过程,明白了了Shell的一般处理流程,调用fork创建新进程,调用execve执行hello,hello的进程执行,hello的异常与信号处理,探究了在进程运行过程中不同信号的作用。
(第6章1分)

第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址:源代码经过预处理、编译、汇编后出现在汇编程序中地址,包含在机器语言中用来指定一个操作数或一条指令的地址。每一个逻辑地址都由一个段(segment)和偏移量(offset)组成,偏移量指明了从段开始的地方到实际地址之间的距离。就是hello.o里相对偏移地址。

线性地址:地址空间是一个非负整数的有序集合,如果地址空间中的整数是连续的,那么我们说它是一个线性地址空间。线性地址就是线性地址空间中的地址。是hello中的虚拟内存地址。

虚拟地址:在采用虚拟内存的系统中,CPU从一个有N = 2n个地址的地址空间中生成虚拟地址,这个地址空间称为虚拟地址空间,里面的地址就是虚拟地址。

物理地址:CPU通过地址总线的寻址,找到真实的物理内存对应地址。 CPU对内存的访问是通过连接着CPU和北桥芯片的前端总线来完成的。在前端总线上传输的内存地址都是物理内存地址。地址翻译会将hello的一个虚拟地址转化为物理地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
要理解段式管理,首先要知道段寄存器,段寄存器对应着内存不同的段,有栈段寄存器(SS)、数据段寄存器(DS)、代码段寄存器(CS)和辅助段寄存器(ES/GS/FS)。如下图:
在这里插入图片描述
Intel处理器在通过段式管理寻址时,首先通过段描述符得到段基址,然后与偏移量结合得到线性地址,从而得到了虚拟地址。至于偏移量,基址寄存器还是变址寄存器有不同的计算方法,后者需要经过乘比例因子等处理。
在这里插入图片描述

7.3 Hello的线性地址到物理地址的变换-页式管理
线性地址(书里的虚拟地址VA)到物理地址(PA)之间的转换通过分页机制完成。而分页机制是对虚拟地址内存空间进行分页。
虚拟页存在未分配的、缓存的、未缓存的三种状态。其中缓存的页对应于物理页。
CPU中的一个控制寄存器,页表基址寄存器指向当前页表,n位的虚拟地址由虚拟页面偏移(VPO, n位)和虚拟页号(VPN, n - p位)组成。MMU利用VPN选择适当的PTE,然后将页表条目中的物理页号(PPN)与虚拟地址中的VPO串联起来,得到物理地址。
在这里插入图片描述
翻译的过程如果命中则完全由硬件处理,处理器生成一个虚拟地址,并将其传送给MMU,MMU生成PTE地址,并从高速缓存/内存中请求得到它,高速缓存/内存向MMU返回PTE(即MMU 使用内存中的页表生成PTE),MMU构造物理地址,将其传送给高速缓存/主存高速缓存/主存返回所请求的数据字给处理器:如果缺页需要操作系统内核与硬件合作完成,处理器生成一个虚拟地址,并将其传送给MMU,MMU生成PTE地址,并从高速缓存/内存中请求得到它,高速缓存/内存向MMU返回PTE(即MMU 使用内存中的页表生成PTE),PTE中的有效位为零, 因此 MMU 触发缺页异常,缺页处理程序确定物理内存中牺牲页 (若页面被修改,则换出到磁盘),缺页处理程序调入新的页面,并更新内存中的PTE,缺页处理程序返回到原来进程,再次执行导致缺页的指令。

7.4 TLB与四级页表支持下的VA到PA的变换
每次CPU产生一个虚拟地址,MMU就必须查阅相应的PTE,这显然造成了巨大的时间开销,为了消除这样的开销,MMU中存在一个关于PTE的小的缓存,称为翻译后备缓冲器(TLB)。
CPU产生虚拟地址VA,VA传送给MMU,MMU使用前36位VPN作为TLBT(前32位)+TLBI(后4位)向TLB中匹配,如果命中,则得到PPN(40bit)与VPO(12bit)组合成PA(52bit)。如果不命中再从内存中将PTE复制到TLB,如果查询PTE的时候发现不在物理内存中,则引发缺页故障。如果发现权限不够,则引发段错误。
在这里插入图片描述
7.5 三级Cache支持下的物理内存访问
在上一步中已经获得了物理地址VA,使用CI(后六位再后六位)进行组索引,每组8路,对8路的块分别匹配CT(前40位)如果匹配成功且块的valid标志位为1,则命中(hit),根据数据偏移量CO(后六位)取出数据返回。否则按顺序对L2cache、L3cache、内存进行相同操作,直到出现命中,将这个块存储到某个高速缓存行中(可能会驱逐一个有效的行),然后返回,接着向上级cache返回直到L1cache。
7.6 hello进程fork时的内存映射
当fork函数被shell进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的PID,fork函数为新进程创建虚拟内存。创建当前进程的的mm_struct,、vm_area_struct和页表的原样副本,两个进程中的每个页面都标记为只读,两个进程中的每个区域结构(vm_area_struct)都标记为私有的写时复制(COW)。
当fork在新进程中返回时,新进程现在的虚拟内存刚好的和调用fork时存在的虚拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面,因此,也就是为每个进程保持了私有地址空间的概念。
7.7 hello进程execve时的内存映射
execve函数在当前进程中加载并运行新程序hello.out时:
先删除已存在的用户区域,也就是将shell与hello都有的区域结构删除。
然后映射私有区域,即将栈、堆、.bss请求二进制0,.data和.text节由hello的相应节提供。
接着映射共享区域, hello程序与共享对象libc.so链接,libc.so是动态链接到这个程序中的,然后再映射到用户虚拟地址空间中的共享区域内。
最后设置当前进程上下文中的程序计数器,使之指向代码区域的入口点。
(以下格式自行编排,编辑时删除)
7.8 缺页故障与缺页中断处理
DRAM 缓存不命中称为缺页,即虚拟内存中的字不在物理内存中。缺页导致页面出错,产生缺页异常,内核调用缺页处理程序。
首先判断虚拟地址是否合法,缺页处理程序会搜索区域结构的链表,把该地址和每个区域结构中的vm_start和vm_end做比较。如果指令不合法则触发段错误,从而终止该进程。
然后检查进程是否有读、写或执行该区域页面的权限,如果不具有则触发保护异常,程序终止。
在两步检查都无误后,根据页式管理的规则,内存会确定一个牺牲页,若页面被修改,则换出到磁盘,再将新的目标页替换牺牲页写入,缺页处理程序返回到原来的进程,重启导致缺页的指令。
7.9动态存储分配管理
在程序运行时程序员使用动态内存分配器(如malloc)获得虚拟内存。动态内存分配器维护着一个进程的虚拟内存区域,称为堆。堆是一个请求二进制零的区域,它紧接在未初始化的数据区后开始,并向上生长(向更高的地址)。分配器将堆视为一组不同大小的块的集合来维护。每个块要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可以用来分配。空闲块保持空闲,直到它显示地被应用程序所分配。分配器的类型包括显式分配器和隐式分配器。
显式分配器:要求应用显式地释放任何已分配的块。
隐式分配器:要求分配器检测一个已分配块何时不再使用,那么就释放这个块,自动释放未使用的已经分配的块的过程叫做垃圾收集。
隐式空闲链表:隐式空闲链表通过头部中的大小字段隐含地连接所有块。
显式空闲链表:显式空闲链表在空闲块中使用指针连接空闲块。它将空闲块组织为某种形式的显式数据结构,只保留空闲块链表,而不是所有块。在每个空闲块中,都包含一个前驱(pred)和后继(succ)指针。
合并空闲块:块回收时需要调用块合并的操作。块合并通过修改header和footer的值进行。块合并时会遇到四种情况:前块和后块都不为空,前块为空后块不为空,后块为空前块不为空,前后块都为空。分别处理四种情况即可完成块合并操作。
动态内存管理的策略:首次适配、下一次适配和最佳适配。
7.10本章小结
通过本章,了解了hello的存储器地址空间、intel的段式管理、hello的页式管理,以intel Core7在指定环境下介绍了VA到PA的变换、物理内存访问,同时介绍了hello进程fork时的内存映射、execve时的内存映射、缺页故障与缺页中断处理、动态存储分配管理,复习了与内存管理相关的重要的概念和方法。加深了对动态内存分配的认识和了解。 (第7章 2分)

第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:一个Linux文件就是一个m个字节的序列,所有的I/O设备(例如网络、磁盘和终端)都被模型化为文件,而所有的输入和输出都被当作对相应文件的读和写来执行。
设备管理:这个设备映射为文件的方式,允许Linux内核引出一个简单、低级的应用接口,称为Unix I/O,这使得输入和输出都能以一种统一且一致的方式的来执行。
8.2 简述Unix IO接口及其函数
打开文件,内核返回一个非负整数的文件描述符,通过对此文件描述符对文件进行所有操作。
Linux shell创建的每个进程开始时都有三个打开的文件:标准输入(描述符为0)、标准输出(描述符为1)和标准错误(描述符为2)。头文件<unistd.h>定义了常量STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO,它们可以用来代替显式的描述符值。
对于每个打开的文件,内核保持着一个文件位置k,初始为0,即从文件开头起始的字节偏移量,应用程序能够通过执行seek操作来显式的改变其值。
读写文件:读操作,就是从文件复制n个字节到内存,并将文件位置k增加为k + n。当k大于等于文件大小时,触发EOF条件,即读到文件的尾部。写操作就是从内存中复制n>0个字节到一个文件,从当前文件位置k开始,然后更新k。
最后,在结束对文件的访问后,会通过内核关闭这个文件,内核将释放打开这个文件时创建的数据结构,并将描述符恢复到可用的描述符池中。
打开文件:int open (char *filename, int flags, mode_t mode);
关闭文件:int close (int fd);
读文件:ssize_t read (int fd, void *buf, size_t n);
写文件:ssize_t write (int fd, const void *buf, size_t n);
将文件指针定位到相应位置: off_t lseek(int fd, off_t offset,int whence);

8.3 printf的实现分析
printf函数代码如下所示:
int printf(const char fmt, …)
{
int i;
char buf[256];
va_list arg = (va_list)((char)(&fmt) + 4);
i = vsprintf(buf, fmt, arg);
write(buf, i);
return i;
}
在printf中调用了两个函数:vsprint和write,观察vsprintf,它接受确定输出格式的格式字符串fmt,将所有参数内容格式化后存入buf,返回格式化数组的长度,再看write,它将buf中的i个元素的值写到终端。
所以printf函数执行过程如下:
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall。字符显示驱动子程序根据传入的ASCII码到字模库读取字符对应的点阵,然后通过vram(显存)对字符串进行输出。显示芯片将按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量),最终实现printf中字符串在屏幕上的输出。
8.4 getchar的实现分析
getchar函数通过调用read函数返回字符。其中read函数的第一个参数是描述符,0代表标准输入。第二个参数输入内容的指针,也就是字符的地址,最后一个参数是1,代表读入一个字符,符号getchar函数读一个字符的设定。read函数的返回值是读入的字符数,如果为1说明读入成功,那么直接返回字符,否则说明读到了buf的最后。
异步异常-键盘中断的处理:当用户按键时,运行键盘中断处理子程序,子程序取得按键扫描码转成ascii码,保存到系统的键盘缓冲区。
然后read函数调用的系统函数可以对缓冲区ASCII码进行读取,直到接受回车键返回。这样getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。

8.5本章小结
本章描述了Linux下I/O的机制,将系统的IO设备抽象成文件,简述了打开、关闭、读写文件等操作,分析并了解了printf函数和getchar函数的机制。
(以下格式自行编排,编辑时删除)
(第8章1分)
结论
hello所经历的过程:
1、hello被I/O设备通过cb等编译器写出来,以文件形式存储在主存中。
2、hello.c通过预处理器被预处理,生成hello.i
3、hello.i通过编译器被编译生成hello.s汇编文件
4、hello.s通过汇编器被汇编成可重定位目标文件hello.o
5、链接器将hello.o和其他外部文件一起链接,完成相应节的合并与符号的解析,确定程序运行时函数的真实地址,生成可执行文件hello
6、在shell中经过了fork和execve,把hello加载到其中;
7、磁盘读取、虚拟内存映射、CPU执行指令、内核调度、缓存加载数据、信号处理、Unix I/O输入与输出;
8、进程终止、shell与内核对其进行回收。

感悟:一个简单的hello程序涉及一系列复杂的编译器、操作系统、硬件实现机制,运行结果看起来简单,但后面好多机制等等的一起工作,才使它完美的呈现在我们面前,这让我理解了深入理解计算机系统的必要性,
(结论0分,缺失 -1分,根据内容酌情加分)

附件
hello.c :hello源代码
hello.i :预处理后的文本文件
hello.s :hello.i编译后的汇编文件
hello.o :hello.s汇编后的可重定位目标文件
hello_objdump :hello的反汇编代码
hello.o_objdump :hello.o的反汇编代码
hello :链接后的可执行文件
(附件0分,缺失 -1分)

参考文献
为完成本次大作业你翻阅的书籍与网站等
[1] 林来兴. 空间控制技术[M]. 北京:中国宇航出版社,1992:25-42.
[2] 辛希孟. 信息技术与信息服务国际研讨会论文集:A集[C]. 北京:中国科学出版社,1999.
[3] 赵耀东. 新时代的工业工程师[M/OL]. 台北:天下文化出版社,1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).
[4] 谌颖. 空间交会控制理论与方法研究[D]. 哈尔滨:哈尔滨工业大学,1992:8-13.
[5] KANAMORI H. Shaking Without Quaking[J]. Science,1998,279(5359):2063-2064.
[6] CHRISTINE M. Plant Physiology: Plant Biology in the Genome Era[J/OL]. Science,1998,281:331-332[1998-09-23]. http://www.sciencemag.org/cgi/ collection/anatmorp.
(参考文献0分,缺失 -1分)

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值