HITICS-2018大作业报告

计算机系统

大作业

题 目 程序人生-Hello’s P2P

计算机科学与技术学院

2018年12月

摘 要

摘要是论文内容的高度概括,应具有独立性和自含性,即不阅读论文的全文,就能获得必要的信息。摘要应包括本论文的目的、主要内容、方法、成果及其理论与实际意义。摘要中不宜使用公式、结构式、图表和非公知公用的符号与术语,不标注引用文献编号,同时避免将摘要写成目录式的内容介绍。

关键词:computer system,hello.c, 预处理,编译,汇编,链接,进程管理,存储管理,I/O管理。

(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)

目 录

第1章 概述 - 4 -

1.1 Hello简介 - 4 -

1.2 环境与工具 - 4 -

1.3 中间结果 - 4 -

1.4 本章小结 - 4 -

第2章 预处理 - 5 -

2.1 预处理的概念与作用 - 5 -

2.2在Ubuntu下预处理的命令 - 5 -

2.3 Hello的预处理结果解析 - 5 -

2.4 本章小结 - 5 -

第3章 编译 - 6 -

3.1 编译的概念与作用 - 6 -

3.2 在Ubuntu下编译的命令 - 6 -

3.3 Hello的编译结果解析 - 6 -

3.4 本章小结 - 6 -

第4章 汇编 - 7 -

4.1 汇编的概念与作用 - 7 -

4.2 在Ubuntu下汇编的命令 - 7 -

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

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

4.5 本章小结 - 7 -

第5章 链接 - 8 -

5.1 链接的概念与作用 - 8 -

5.2 在Ubuntu下链接的命令 - 8 -

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

5.4 hello的虚拟地址空间 - 8 -

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

5.6 hello的执行流程 - 8 -

5.7 Hello的动态链接分析 - 8 -

5.8 本章小结 - 9 -

第6章 hello进程管理 - 10 -

6.1 进程的概念与作用 - 10 -

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

6.3 Hello的fork进程创建过程 - 10
-

6.4 Hello的execve过程 - 10 -

6.5 Hello的进程执行 - 10 -

6.6 hello的异常与信号处理 - 10 -

6.7本章小结 - 10 -

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

7.1 hello的存储器地址空间 - 11 -

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

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

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

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

7.6 hello进程fork时的内存映射 - 11 -

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

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

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

7.10本章小结 - 12 -

第8章 hello的IO管理 - 13 -

8.1 Linux的IO设备管理方法 - 13 -

8.2 简述Unix IO接口及其函数 - 13
-

8.3 printf的实现分析 - 13 -

8.4 getchar的实现分析 - 13 -

8.5本章小结 - 13 -

结论 - 14 -

附件 - 15 -

参考文献 - 16 -


第1章 概述

1.1 Hello简介

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

P2P(From Program to
Process):首先使用高级语言处理得到hello.i,然后编译成hello.s,hello.s经过汇编成为hello.o,最后与函数库里的函数链接形成ELF二进制文件。在这个过程中,操作系统用fork函数形成一个子进程,并且用execve函数加载进程。

O2O: From Zero-0 to Zero-0:
在程序的执行过程中会使用到内存中的数据。这些数据通过各级存储,包括磁盘、主存、Cache等,并使用页表等辅助存储,实现访存的加速。在这个过程中还涉及操作系统的信号处理,控制进程,使得系统资源得到充分利用。而IO管理与信号处理通过软硬结合,完成程序从键盘、主板、显卡,再到屏幕的工作。当进程执行结束后,操作系统进行进程回收

1.2 环境与工具

列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开发与调试工具。

硬件环境:Intel Core i7-6700HQ x64CPU,16G RAM,256G SSD +1T HDD.

软件环境:Ubuntu16.04.1 LTS

开发与调试工具:gcc,gdb,objdump,readelf,HexEdit

1.3 中间结果

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

Hello.i: 预处理得到的中间结果.

Hello.s:经过编译后得到的汇编文件。

Hello.i:可重定位目标文件

Hello.out:链接后形成的可执行文件。

1.4 本章小结

概括了hello.c执行的全过程,操作系统对p2p,o2o的执行,从整体上对hello.c的一生有了一个较为清晰的认识。

(第1章0.5分)


第2章 预处理

2.1 预处理的概念与作用

概念:预处理器cpp根据特殊的关键字(#include,#define和#if)
,修改源程序,生成处理之后的源程序.i文件。

作用:

  1. 插入#include命令的文件

  2. 拓展所有#define声明指定的宏

  3. 根据#if 后面的条件决定需要编译的代码

2.2在Ubuntu下预处理的命令

.gcc hello.c -E -o hello.i

2.3 Hello的预处理结果解析

经过预处理之后,hello.c文件转化为hello.i文件。原文件中的宏进行了宏展开,头文件中的内容被包含进该文件中。打开该文件可以发现,文件长度变为3126行。文件的内容增加,且仍为可以阅读的C语言程序文本文件。

2.4 本章小结

这一章通过命令行完成了对hello的预处理并对预处理结果进行解析,为hello编译为汇编文本提供条件。

(第2章0.5分)


第3章 编译

3.1 编译的概念与作用

概念:将文本文件hello.i(ASCII文本)翻译成文本文件hello.s(ASCII文件),hello.s是一个由汇编语言构成的程序,这个处理过程称为编译。

作用:将C语言语句翻译成汇编语言语句,完成这个过程主要由以下五个步骤构成:

1)词法解析:从左至右逐个字符地对源程序进行扫描,产生一个个的单词符号,把作为字符串的源程序改造成为单词符号串的中间程序

2)语法解析:单词符号作为输入,分析单词符号串是否形成符合语法规则的语法单位,将词法分析得到结果构建一颗语法树。

3)生成中间代码:作用是可使编译程序的结构在逻辑上更为简单明确。

4)代码优化:代码优化是指对程序进行多种等价变换,使得从变换后的程序出发,能生成更有效的目标代码。

5)生成目标代码:目标代码生成器把语法分析后或优化后的中间代码变换成目标代码。

3.2 在Ubuntu下编译的命令

.gcc hello.i -S -o hello.s

应截图,展示编译过程!

3.3 Hello的编译结果解析

3.3.1 全局变量与全局函数

在hello.c中,包含一个全局变量int sleepsecs=2;以及一个全局函数int main(int
argc,char
*argv[]);。经过编译之后,sleepsecs被存放在.rodata节中。而main函数中使用的字符串常量也被存放在数据区。

3.3.2 主函数的参数

主函数的参数部分给出了int argc,char
*argv[]两个参数。在汇编代码中,分别将其存放在栈中rbp寄存器指向地址-20和-32处,如下图所示。其中%edi代表argc,%rsi代表argv[]。

3.3.3 条件判断语句及分支

接着在main函数中,使用if语句进行了条件判断。cmpl语句进行判断条件的比较。如果条件满足则继续顺序执行,调用puts输出给定字符串(这里puts是对printf的优化),然后使用参数1调用exit结束程序。对应的汇编代码如下。

3.3.4 循环结构及主函数结尾部分

接下来进入for循环语句部分。该部分使用了一个局部变量i,该变量存放在栈中rbp寄存器指向地址-4处。首先对其置零进行初始化(35行)。接着使用jump
to
middle模式进入.L3使用cmpl语句先进行条件判断。如果条件满足,那么进入.L4循环体部分调用printf函数和sleep函数。

在调用printf的过程中,进行了数组访问(argv[1]和argv[2])。而argv是指针数组,所以会进行二次寻址。在汇编代码中,38至40行取出argv[2]对应的内容,并放入三号参数寄存器%rdx中。41至44行取出argv[1]对应的内容,并放入二号参数寄存器%rsi中。45行将格式字符串放到一号参数寄存器%edi中,然后调用printf函数进行显示。48-50行读取sleepsecs全局变量并调用sleep函数。最后51行对计数量进行加一,结束循环体部分。

最后调用getchar函数,将返回值设为0,主函数正常返回。

此部分是重点,说明编译器是怎么处理C语言的各个数据类型以及各类操作的。应分3.3.1~
3.3.x等按照类型和操作进行分析,只要hello.s中出现的属于大作业PPT中P4给出的参考C数据与操作,都应解析

3.4 本章小结

本章通过对hello.s的说明,分析hello.s的汇编语句,本章通过与源文件C程序代码进行比较,完成了对汇编代码的解析工作。完成该阶段转换后,可以进行下一阶段的汇编处理。

(第3章2分)


第4章 汇编

4.1 汇编的概念与作用

汇编器(as)将.s
汇编程序翻译成机器语言指令,把这些指令打包成可重定位目标程序的格式,并将结果保存在.o
目标文件中,.o
文件是一个二进制文件,它包含程序的指令编码。这个过程称为汇编,亦即汇编的作用。

注意:这儿的汇编是指从 .s 到 .o
即编译后的文件到生成机器语言二进制程序的过程。

4.2 在Ubuntu下汇编的命令

.gcc hello.s -o hello.o

应截图,展示汇编过程!

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

在命令行中输入readelf -a hello.o > hello_o_elf.txt,获得hello.o文件的ELF。

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

使用readelf -S hello.o查看节头表信息:

使用readelf -s hello.o查看hello.o的符号表信息:

ELF头:以16字节序列Magic开始,Magic 描述了生成该文件的系统的字的大小和字节顺序,
ELF 头剩下的部分包含帮助链接器语法分析和解释目标文件的信息, 其中包括 ELF
头的大小、目标文件的类型、 机器类型、字节头部表(section header
table)的文件偏移,以及节头部表中条目的大小和数量等信息。

根据hello.o的节头表可以得到各节的基本信息。由于是可重定位目标文件,所以每个节都从0开始,用于重定位。在文件头中得到节头表的信息,然后再使用节头表中的字节偏移信息得到各节在文件中的起始位置,以及各节所占空间的大小。同时可以观察到,代码段是可执行的,但是不能写;数据段和只读数据段都不可执行,而且只读数据段也不可写。

4.4 Hello.o的结果解析

使用objdump -d -r hello.o指令显示hello.o的反汇编结果。

总体观察hello.o的反汇编结果后发现,除去显示格式之外两者差别不大,主要差别如下:

1)
全局变量:对于全局变量的访问与hello.s不同,在hello.s中,对于全局变量的访问,通过字节使用声明中的助记符+off+%rip来访问;而在反汇编代码中,对于全局变量的访问,用$0x0(%rip)代替,这是因为.data与.rodata段中的数据地址在运行时才确定的,故对全局变量的访问需要重定位,所以对全局变量的访问需要添加重定位条目。

   2)

分支转移:反汇编代码转跳指令语句中目的地址使用的是实际地址,而hello.s中转跳指令语句中目的地址使用的是助记符.LX,由此可以确定目的地址映射关系,在汇编语言映射到机器语言时,目的地址的助记符会被替换成实际地址。

3)
函数调用:在hello.s文件中,函数调用之后直接跟着函数名称,而在反汇编程序中,call
的目标地址是当前下一条指令地址(注:这并不是目标函数的地址)。这是因为hello.c中调用的函数不是本地函数,而是共享库中的函数,需要通过动态链接器才能确定函数的运行时真正地址,在生成为机器语言的时候,这些地址并不确定,对于这些不确定地址的函数调用,将其call指令后的相对地址设置为全目标地址正是下一条指令,然后在.rela.text节中为其添加重定位条目,在连接阶段静态链接会重定位。

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

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

4.5 本章小结

本章查看hello.o的elf可重定位文件,对其进行反编译,并与hello.s比较,完成该阶段的工作之后,可以进行下一阶段的链接。

(第4章1分)


第5章 链接

5.1 链接的概念与作用

链接是将各种代码和数据片段收集并组合成一个单一文件的过程,这个文件可被加载到内存并执行。链接可以执行于编译时,也就是在源代码被编译成机器代码时;也可以执行于加载时,也就是在程序被加载器加载到内存并执行时;甚至于运行时,也就是由应用程序来执行。链接是由叫做链接器的程序执行的,链接器使得分离编译成为可能。

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

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

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

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

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

图5-2 hello.out文件的文件头

使用readelf –h
hello.out查看文件头信息。根据文件头的信息,可以知道该文件是可执行目标文件,有31个节,如图5-2。使用readelf
–S
hello.out查看节头表。从而得知各节的大小,以及他们可以进行的操作,如图5-3。使用readelf
–s hello.out可以查看符号表的信息,如图5-4。

图5-3 hello.out的段头表

图5-4 hello.out的符号表

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

根据图5-3可以得到各段的基本信息。由于是可执行目标文件,所以每个段的起始地址都不相同,它们的起始地址分别对应着装载到虚拟内存中的虚拟地址。这样可以直接从文件起始处得到各段的起始位置,以及各段所占空间的大小。同时可以观察到,代码段是可执行的,但是不能写;数据段和只读数据段都不可执行,而且只读数据段也不可写。

hello的虚拟地址空间

可以看出程序是在0x00400000地址开始加载的,结束的地址大约是0x00400fff。

再来分析分析elf里面的Program Headers:

PHDR:程序头表

INTERP:程序执行前需要调用的解释器

LOAD:程序目标代码和常量信息

DYNAMIC:动态链接器所使用的信息

NOTE::辅助信息

GNU_EH_FRAME:保存异常信息

GNU_STACK:使用系统栈所需要的权限信息

GNU_RELRO:保存在重定位之后只读信息的位置

其余的从.dynamic到.strtab节的内容是存放在0x00400fff后面

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

链接的重定位过程分析

使用objdump -d -r
hello.out得到反汇编结果。可以明显发现该结果与hello.o的反汇编结果不同。可执行文件的反汇编结果中给出了重定位结果,即虚拟地址的确定。而hello.o的反汇编结果中,各部分的开始地址均为0。

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

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

hello的执行流程

通过使用objdump查看反汇编代码,以及使用gdb单步运行,可以找出.text节中main函数前后执行的函数名称。在main函数之前执行的程序有:_start、__libc_start_main@plt、__libc_csu_init、_init、frame_dummy、register_tm_clones。在main函数之后执行的程序有:exit、cxa_thread_atexit_impl、fini。

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

5.7 Hello的动态链接分析

1)
装载时重定位:对于动态共享库中的函数,在编译阶段无法获得其真实地址;在动态连接阶段,连接器将共享函数设置一个重定位项,其中包含重定位信息(ADDR(.plt)+offset)和符号表信息,然后在运行时根据重定位项信息才能确定其真实地址。

2)
PIC(地址无关代码):动态链接库希望所有进程共享指令段而各自拥有数据段的私有副本,为了实现这个目标,就要采用与地址无关代码的技术。该实现的基本思想是:把指令中需要修改的部分分离出来,跟数据部分放在一起,这样指令部分就可以保持不变,而数据部分则在每个进程拥有一个副本。

与地址无关的代码,也就是需要考虑代码中会对地址进行引用的情况,共享对象(GCC中的动态链接文件)中地址引用可以分为以下几种情况:

a)
模块内函数调用、跳转:由于此时调用者与被调者都是位于同一个模块,所以调用者与被调者之间的相对位置是固定的,因此,对被调者的调用就可以使用相对地址来替代绝对地址,对于这种指令就是不需要重定位的。

b)
模块内数据的访问,如模块内定义的静态变量,全局变量:与上面分析同理,由于数据定义与引用指令是位于同一个模块的,因此它们之间的相对位置是固定的。但是此时有一些区别,现代体系结构中,数据的相对寻址没有基于当前指令的寻址方式,因此
ELF
采用了一个巧妙的方法来获取当前的PC(程序计数器)的值,再在该基础上添加一个偏移,即可访问到变量。

c)
模块外部的函数调用、跳转:此时对外部符号的引用显然是与地址有关的,按照先前说的基本思想,此时需要将与地址相关的部分放到数据段里。ELF
的做法是在数据段中建立一个指向这些函数的指针数组,也即是全局偏移表(GOT,Global
Offset Tabel),当代码需要引用这些外部函数时,则可以通过GOT
中的相对应的项间接引用。动态链接器在装载模块的时候会查找每个函数所在地址,并填充GOT中的各个表项,以保证每个指针均指向正确的地址。同时由于GOT本身是放在数据段的,因此它可以在模块装载的时候被修改,并且每个进程都可有自己的副本。

d)
模块外部的数据的访问,比如别的模块定义的全局变量:该方法与模块外部的函数访问方法相同,同样引入
GOT ,只是此时GOT 的表项中存储的是变量的地址。

5.8 本章小结

本章主要介绍了链接的概念与作用,在Linux系统下如何进行连接,分析可执行文件的hello格式,分析hello的虚拟地址空间,逐一解析链接的重定位过程分析,分析hello的执行流程以及分析Hello的动态链接分析。

   链接器在软件开发中扮演着一个关键的角色,因为它们使得分离编译(separate

compilation)成为可能。我们不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其他文件。

(第5章1分)


第6章 hello进程管理

6.1 进程的概念与作用

概念:一个执行中程序的实例。

作用:每次用户通过向shell 输入一个可执行目标文件的名字,运行程序时, shell
就会创建一个新的进程,然后在这个新进程的上下文中运行这个可执行目标文件。应用程序也能够创建新进程,并且在这个新进程的上下文中运行它们自己的代码或其他应用程序。

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

Shell-bash的作用:Shell是一个用C语言编写的是一个命令行解释器,他是用户使用 Linux 的桥梁,为用户提供了一个向Linux内核发送请求以便运行程序的界面系统级程序,用户可以用shell来启动、挂起、停止甚至是编写一些程序。

处理流程:

1) 读取用户由键盘输入的命令行。

2) 分析命令,以命令名作为文件名,并将其它参数改造为系统调用execve( )内部处理所要求的形式。

3) 终端进程调用fork( )建立一个子进程。

4) 终端进程本身调用waitpid()来等待子进程完成(如果是后台命令,则不等待)。当子进程运行时调用execve(),子进程根据文件名到目录中查找有关文件,调入内存,执行这个程序。

5) 如果命令末尾有&,则终端进程不用执行系统调用waitpid (),立即发提示符,让用户输入下一条命令;否则终端进程会一直等待,当子进程完成工作后,向父进程报告,此时中断进程醒来,作必要的判别工作后,终端发出命令提示符,重复上述处理过程。

6.3 Hello的fork进程创建过程

一个进程,包括代码、数据和分配给进程的资源。fork函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。一个进程调用fork函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。

6.4 Hello的execve过程

使用execve就是一次系统调用,首先要做的将新的可执行文件的绝对路径从调用者(用户空间)拷贝到系统空间中。在得到可执行文件路径后,就找到可执行文件打开,由于操作系统已经为可执行文件设置了一个数据结构,就初始化这个数据结构,保存一个可执行文件必要的信息。可执行文件不是真正上能够自己运行的,需要有代理人来代理。在系统内核中有一个formats队列,循环遍历这个队列,看看现在被初始化的这个数据结构是哪个代理人可以代理的。如果没有就继续查看数据结构中的信息。按照系统配置了是否可以动态加载模块,加载一次模块,再循环遍历看是否有代理人前来认领。找到正确的代理人后,代理人首先要做的就是放弃以前从父进程继承来的资源。主要是对信号处理表,用户空间和文件大资源的处理。将父进程的信号处理表复制过来,放弃原来的用户空间。然后载入真正的程序代码和数据段,开辟堆栈,映射执行参数和环境变量。

6.5 Hello的进程执行

Linux 系统中的每个程序都运行在一个进程上下文中,有自己的虚拟地址空间。当shell
运行一个程序时,父shell
进程生成一个子进程,它是父进程的一个复制。子进程通过execve
系统调用启动加载器。加载器删除子进程现有的虚拟内存段,并创建一组新的代码、数据、堆和栈段。新的栈和堆段被初始化为零。通过将虚拟地址空间中的页映射到可执行文件的页大小的片(chunk),
新的代码和数据段袚初始化为可执行文件的内容。最后,加载器跳转到_start地址,它最终会调用应用程序的main
函数。

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

6.6 hello的异常与信号处理

Hello在执行的过程中,可能会出现处理器外部I/O设备引起的异常,执行指令导致的陷阱、故障和终止。第一种被称为外部异常,常见的有时钟中断、外部设备的I/O中断等。第二种被称为同步异常。陷阱指的是有意的执行指令的结果,故障是非有意的可能被修复的结果,而终止是非故意的不可修复的致命错误。

在发生异常时会产生信号。例如缺页故障会导致OS发生SIGSEGV信号给用户进程,而用户进程以段错误退出。常见信号种类如下表所示。

ID 名称 默认行为 相应事件

2 SIGINT 终止 来自键盘的中断

9 SIGKILL 终止 杀死程序(该信号不能被捕获不能被忽略)

11 SIGSEGV 终止 无效的内存引用(段故障)

14 SIGALRM 终止 来自alarm函数的定时器信号

17 SIGCHLD 忽略 一个子进程停止或者终止

程序运行时键入ctrl c:

键入ctrl z:

Ctrl-z后可以运行ps查看进程及其运行时间,结果如图6-4所示。可以运行jobs查看当前暂停的进程,结果如图6-5所示。可以运行fg使进程在前台执行,结果如图6-6所示。也可以使用kill杀死特定进程,如图6-7所示。

图6-4 输入ps查看进程及其运行时间

图6-5 输入jobs查看当前暂停的进程

图6-6 输入fg使进程在前台执行

图6-7 使用kill杀死特定进程

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

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

6.7本章小结

在本章中,阐明了进程的定义与作用,介绍了Shell的一般处理流程,调用fork创建新进程,调用execve执行hello,hello的进程执行,hello的异常与信号处理。

(第6章1分)


第7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:由程序产生的与段相关的偏移地址部分,逻辑地址由两个16位的地址分量构成,一个为段基值,另一个为偏移量。

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

物理地址:计算机系统的主存被组织成一个由M个连续的字节大小的单元组成的数组,每一个字节都有一个唯一的物理地址。

虚拟地址:由CPU生产,经过MMU转换可以转换为物理地址,虚拟地址实际上就是一种线性地址。

逻辑地址出现在hello.o的反汇编代码中,虚拟地址出现在hello反汇编代码中,因为在连接阶段进行了重定位操作,指令以及大部分引用都已经被映射到虚拟内存空间中。

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

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

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

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

CPU的页式内存管理单元,负责把一个线性地址,最终翻译为一个物理地址。从管理和效率的角度出发,线性地址被分为以固定长度为单位的组,称为页(page),例如一个32位的机器,线性地址最大可为4G,可以用4KB为一个页来划分,这页,整个线性地址就被划分为一个tatol_page[2^20]的大数组,共有2的20个次方个页。这个大数组我们称之为页目录。目录中的每一个目录项,就是一个地址——对应的页的地址。另一类“页”,我们称之为物理页,或者是页框、页桢的。是分页单元把所有的物理内存也划分为固定长度的管理单位,它的长度一般与内存页是一一对应的。

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

Core i7 MMU 使用四级的页表将虚拟地址翻译成物理地址。36位VPN 被划分成四个9
位VPN,分别用于一个页表的偏移量。具体结构如图7-1所示。

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

首先CPU发出一个虚拟地址,在TLB里面寻找。如果命中,那么将PTE发送给L1Cache,否则先在页表中更新PTE。然后再进行L1根据PTE寻找物理地址,检测是否命中的工作。这样就能完成Cache和TLB的配合工作。具体流程如图7-2所示。

7.6 hello进程fork时的内存映射

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

7.7 hello进程execve时的内存映射

   execve通过调用某个驻留在存储器中称为加载器的操作系统代码来运行hello,加载器将可执行目标文件中的代码和数据从磁盘中复制到内存中,然后通过跳转到程序的第一条指令或者入口点来运行该程序,当加载器运行时,会hello创建内存映像。加载并运行 hello 需要以下几个步骤:

1)删除已存在的用户区域:删除当前进程虚拟地址的用户部分中的已存在的区域结构。

2)映射私有区域:为新程序的代码、数据、bss和栈区域创建新的区域结构。所有这些新的区域都是私有的、写时复制的。代码和数据区域被映射为 hello 文件中的.text和.data区,bss区域是请求二进制零的,映射到匿名文件,其大小包含在hello中,栈和堆地址也是请求二进制零的,初始长度为零。

3)映射共享区域:hello 程序与共享对象(或者目标)链接,libc.so是动态链库,那么这些对象都是接到这个程序中的,然后再映射到用户虚拟地址空间中的共享区域内。

4)设置程序计数器(PC):execve做的最后一件事情就是设置当前进程上下文的程序计数器,使之指向代码区域的入口点。

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

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

7.9动态存储分配管理

printf函数会调用malloc,下面简述动态内存管理的基本方法与策略:

动态内存分配器维护着一个进程的虚拟内存区域,称为堆。分配器将堆视为一组不同大小的块的集合来维护。每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可用来分配。空闲块保持空闲,直到它显式地被应用所分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。

   分配器分为两种基本风格:显式分配器、隐式分配器。

   显式分配器:要求应用显式地释放任何已分配的块。

隐式分配器:要求分配器检测一个已分配块何时不再使用,那么就释放这个块,自动释放未使用的已经分配的块的过程叫做垃圾收集。

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

7.10本章小结

本章通过hello的内存管理,复习了与内存管理相关的重要的概念和方法。加深了对动态内存分配的认识和了解。

(第7章 2分)


第8章 hello的IO管理

8.1 Linux的IO设备管理方法

所有的I/O设备都被模型化为文件,内核也被映射为文件,而所有的输入和输出都被当做对相应文件的读和写来执行,这种将设备优雅地映射为文件的方式,允许Linux内核引出一个简单、低级的应用接口称为Unix I/O。

8.2 简述Unix IO接口及其函数

linux 提供如下 IO 接口函数:

read 和 write – 最简单的读写函数;

readn 和 writen – 原子性读写操作;

recvfrom 和 sendto – 增加了目标地址和地址结构长度的参数;

recv 和 send – 允许从进程到内核传递标志;

readv 和 writev – 允许指定往其中输入数据或从其中输出数据的缓冲区;

recvmsg 和 sendmsg –
结合了其他IO函数的所有特性,并具备接受和发送辅助数据的能力。

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;

}

(char*)(&fmt) + 4)
表示的是…可变参数中的第一个参数的地址。而vsprintf的作用就是格式化。它接受确定输出格式的格式字符串fmt。用格式字符串对个数变化的参数进行格式化,产生格式化输出。接着从vsprintf生成显示信息,到write系统函数,直到陷阱系统调用
int
0x80或syscall。字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。

从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall.

字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。

显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。

8.4 getchar的实现分析

异步异常-键盘中断的处理:当用户按键时,键盘接口会得到一个代表该按键的键盘扫描码,同时产生一个中断请求,中断请求抢占当前进程运行键盘中断子程序,键盘中断子程序先从键盘接口取得该按键的扫描码,然后将该按键扫描码转换成ASCII码,保存到系统的键盘缓冲区之中。

getchar函数落实到底层调用了系统函数read,通过系统调用read读取存储在键盘缓冲区中的ASCII码直到读到回车符然后返回整个字串,getchar进行封装,大体逻辑是读取字符串的第一个字符然后返回。

异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。

getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。

8.5本章小结

本章通过介绍hello中包含的函数所对应的unix
I/O,大致了解了I/O接口及其工作方式,同时也了解了硬件设备的使用和管理的技术方法。

(第8章1分)

结论

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

编写,通过editor将代码键入hello.c

预处理,将hello.c调用的所有外部的库展开合并到一个hello.i文件中

编译,将hello.i编译成为汇编文件hello.s

汇编,将hello.s会变成为可重定位目标文件hello.o

链接,将hello.o与可重定位目标文件和动态链接库链接成为可执行目标程序hello

运行:在shell中输入./hello 1170500704 weixiaowen

创建子进程:shell进程调用fork为其创建子进程

运行程序:shell调用execve,execve调用启动加载器,加映射虚拟内存,进入程序入口后程序开始载入物理内存,然后进入
main函数。

执行指令:CPU为其分配时间片,在一个时间片中,hello享有CPU资源,顺序执行自己的控制逻辑流

访问内存:MMU将程序中使用的虚拟内存地址通过页表映射成物理地址。

动态申请内存:printf会调用malloc向动态内存分配器申请堆中的内存。

信号:如果运行途中键入ctr-c ctr-z则调用shell的信号处理函数分别停止、挂起。

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

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

计算机系统的设计与实现,处处体现着抽象的含义。比如,程序的本质是01二进制码,也就是机器语言。而汇编语言实现了对机器语言的抽象,高级语言实现了对汇编语言的抽象。再比如,各种物理内存的实现方式各不相同,有磁盘、软盘等。使用虚拟内存的概念实现了对各种物理内存的抽象,而具体实现则交给IO设备进行处理,使得上层在使用的时候非常方便。概念上的抽象使得对概念的使用变得简单,这就是我对计算机系统设计实现的一个感悟。

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


附件

列出所有的中间产物的文件名,并予以说明起作用。

中间结果文件名 文件作用

hello.i 预处理得到的中间结果

hello.s hello.i编译后得到的汇编语言文本文件

hello.o hello.s汇编后得到的可重定位目标文件

hello.out 链接后得到的可执行目标文件

(附件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分)

你可能感兴趣的:(作业)