CS大作业论文

计算机系统

大作业

题     目  程序人生-Hello’s P2P 

专       业  计算学部               

学     号  1190202408             

班     级  1936601                

学       生  闫广泽                 

指 导 教 师  刘宏伟                 

计算机科学与技术学院

2021年6月

摘  要

本篇主要通过对hello的一生进行探讨。通过对hello.c文件到进程被回收这个质朴却复杂的计算机系统运行过程进行解释和说明,来对计算机系统的运行加以解释和说明。主要针对程序的编译过程、进程管理、存储管理、IO管理进行研究和探讨。

关键词:P2P;020;程序的生命周期;进程;                           

(摘要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简介

我是一个C语言程序,我每次都会在最后出现在屏幕上,我对我自己有简介:

P2P:在Linux中,经过预处理、编译、再链接

P2P:首先从一个写好的源程序程序文件(Program)开始,GCC编译器的驱动程序读取它,依次通过预处理器cpp将其预处理为一个修改了的源程序,通过编译器ccl把它变成汇编程序,汇编器as把它转化为可重定位的目标程序,最后再由链接器ld将其变为可执行的目标程序。这时,在shell中运行它,OS(进程管理)会通过fork来为其创建一个新的进程(Process)。这便是P2P的过程。

020:首先,通过Editor编写出一个源程序hello.c,然后通过GCC编译器的驱动程序读取它,并依次经过预处理,编译,汇编,链接来生成一个可执行文件,然后再shell中输入执行它的指令,shell通过fork为其创建新的子进程,然后通过exceve在进程的上下文中加载并运行hello,把它映射到对应虚拟内存区域,并依需求载入物理内存,在CPU的帮助下,它的指令被一步步执行,实现它拥有的功能,在程序运行结束后,父进程会对其进行回收,内核把它从系统中清除,hello完成了它的一生,这便是020的过程。

1.2 环境与工具

硬件环境:X64 CPU;2GHz;2G RAM;256GHD Disk 以上

软件环境:Windows10 64位以上;Vmware 11以上;Ubuntu 16.04 LTS 64位

开发工具:GDB/OBJDUMP;EDB;gedit+gcc;CodeBlocks 64位,gdb等。

1.3 中间结果

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

hello.c 源文件

hello.i 预处理后的文件

hello.s 编译后的汇编文件

hello.o 汇编后的可重定位文件

hello 链接后的可执行文件

hello.elf hello的elf文件

helloout.elf hello.o的elf文件

hello1.txt objdump hello的反汇编文件

hello0.txt objdump hello.o的反汇编文件

1.4 本章小结

本章主要介绍了hello的P2P,020过程,以及进行实验时的软硬件环境及开发与调试工具和在本论文中生成的中间结果文件。

(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用

以下格式自行编排,编辑时删除

预处理过程是整个编译过程的第一步,预处理输入的文件格式是c文件或者是h文件,经过预处理输出的就是i文件。在预处理过程中,预处理器会分析预处理指令(包括#include头文件和#define宏定义等)以及去除源代码中的注释。通常来说,编译器会直接将c文件编译成o文件,然后再去交给链接器链接为elf文件。如果希望通过命令行仅仅将c文件预处理成为i文件,只要在命令行中加上-E参数,就可以使编译器在预处理后停止下来,输出预处理后的源文件。

2.2在Ubuntu下预处理的命令

      例如我们的命令行就是,gcc -E hello.c -o hello.i,就会生成.i文件

hello.c截图

hello.i截图

2.3 Hello的预处理结果解析

可以看到,相比较于hello.c的不到三十行代码,hello.i有3000多行代码,其实就是对源文件中的宏定义进行了展开,增加的代码就是宏定义解析的代码

2.4 本章小结

本章节简单介绍了c语言在编译前的预处理过程,简单介绍了预处理过程的概念和作用,对预处理过程进行演示,并举例说明预处理的结果还有解析预处理的过程。

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

概念:编译器ccl将文本文件hello.i翻译成文本文件hello.s。它包含一个汇编语言程序。

作用:编译器做一些语法分析、词法分析、语义分析等,若检查无错误,便将高级程序设计语言(C语言)书写的程序转变为与其等价的汇编语言程序(可能依情况做一些优化),汇编语言是介于高级语言和机器语言之间的中间代码,它为不同高级语言的不同编译器提供了通用的输出语言,使他们产生的输出文件都是用的一样的输出语言。

3.2 在Ubuntu下编译的命令

gcc -S hello.i -o hello.s

hello.s

3.3 Hello的编译结果解析

和刚才动辄3000行代码的.i文件不同,.s文件只有80多行,认真观察hello.s文本,发现他其实只翻译了hello.i中main函数及其周围的部分,头文件里面的内容他并没有翻译。

3.3.1汇编文件伪指令

这部分内容主要声明了源文件的名字(.file),代码段(.text),数据段(.data),全局变量及其大小(.globl,.size),指令及数据存放地址的对齐方式(.align)等文件信息。

3.3.2数据类型

我们可以查看hello.c

发现主要有以下几种类型:

1.常量

字符串型常量,汇编语言中,输出字符串作为全局变量保存。汇编文件hello.s中,共有两个字符串,均作为printf参数,如下图:

2. 变量

定义了全局int型变量sleepsecs和局部int型变量iargc

其中sleepsecs在开头定义:

.c文件中是这样的

.s文件中是

可以看到sleepsecs占用4字节空间并且保存在只读代码段rodata中,并且发生了隐式转换,因为在.c中是定义的sleepsecsint型全局变量,但是赋值却是浮点型,所以编译器对2.5舍入得2

i作为局部变量保存在栈中(运行)时,如下(下面为给i赋初值为0

argv也是如此:

hello.c中,同时主要定义和使用了argv数组,而在hello.s中,通过以下代码可知

分析可知,argv[1]作为printf第二个参数,应当存于寄存器%rsi中,因此可推断argv[1]地址为-0x2A(%rbp)中,argv[2]作为printf函数的第三个参数,应当存于寄存器%rdx中,因此可推断argv[2]地址为-0x16(%rbp);数组首地址位于-0x32(%rbp) ,以上所占字节数为8。

3.3.3各类操作

1.赋值操作

如在for循环中,初始化局部变量i为0。在hello.s中,可以找到对应的操作,主要使用了数据传送指令,如下图:

在给printf传参数时,使用了加载有效地址指令,如下图:

2.类型转换操作

在前面我们已经介绍,这里不过多赘述

3.关系操作

hello.c中使用了if语句,其中使用了关系操作,如下图

通过cmpl指令比较了argv与3的大小关系

4. 控制转移操作

在hello.c中使用了if语句,那么紧接着就需要转移,当条件不满足时该怎样或者条件满足该怎样,如上,当比较argv和3相等的时候就会跳转到L2

5. 算术操作

算术操作对应于hello.c中的i++

hello.s中对应以上操作

6. for循环操作

hello.c中的for循环对应于hello.s中的

使用了条状指令jle和关系操作指令cmpl实现了for循环操作。

7. 函数操作。hello.c中调用了许多函数,如printf,exit,getchar,可以通过以下的指令实现,如下图printf的实现,通过过程调用指令call来实现函数操作,同时通过寄存器(如%rdi)来传递参数。

3.4 本章小结

编译器将预处理后的文本进行汇编处理,对于常量,编译器将它储存到一个特定的位置,记录它的一些信息,比如类型;对于一些特定的常量,比如printf()函数中的信息,编译器会把它提取出来保存。程序中的语句,例如赋值语句,编译器通过寄存器,栈等结构进行赋值;分支语句用je,jle,jge等条件跳转语句进行实现。每种语句都有对应的实现方法。程序中的函数,如果不是库函数,则会对函数进行逐句的语法分析和解析,如果是标准的库函数,编译器可以直接用call语句进行调用。

汇编语言相对于高级语言来说,它更加靠近底层机器且直接面对硬件的,所以也为高级语言提供了一种统一的面向机器的解释,它具有一些助记符,所以比直接的机器语言好理解,但相对于高级语言又显得难以掌握。汇编语言具有:机器相关性,高速度和高效率,编写和调试的复杂性等特性。

(第32分)

第4章 汇编

4.1 汇编的概念与作用

概念:汇编器as将hello.s翻译成机器语言指令,把这些指令打包成一种叫可重定位目标程序的格式,并将结果保存在目标文件hello.o中。(hello.o是一个二进制文件)。

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

4.2 在Ubuntu下汇编的命令

gcc -c hello.s -o hello.o

4.3 可重定位目标elf格式

使用readelf查看hello.o:

分析.elf文件中的内容:

分析.elf文件中的内容:

ELF头:ELF头(ELF header)以一个16字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序。ELF头剩下的部分包含了帮助链接器语法分析和解释目标文件的信息,其中包括ELF头的大小、目标文件的类型(如可重定位、可执行或者共享的)、机器类型(如x86-64)、节头部表(section header table)的文件偏移,以及节头部表中条目的大小和数量。不同节的位置和大小是有节头部表描述的,其中目标文件中每个节都有一个固定大小的条目(entry)。

节头:记录各节名称、类型、地址、偏移量、大小、全体大小、旗标、链接、信息、对齐。

重定位节:.rela.text,保存的是.text节中需要被修正的信息;任何调用外部函数或者引用全局变量的指令都需要被修正;调用外部函数的指令需要重定位;引用全局变量的指令需要重定位; 调用局部函数的指令不需要重定位;在可执行目标文件中不存在重定位信息。本程序需要被重定位的是printf、puts、exit、sleepsecs、getchar、sleep和.rodata中的.L0和.L1。

.rela.eh_frame节是.eh_frame节重定位信息。

符号表:.symtab,一个符号表,它存放在程序中定义和引用的函数和全局变量的信息,一些程序员错误地认为必须通过-g选项来编译一个程序,才能得到符号表信息。实际上每个可重定位目标文件在.symtab中都有一张符号表(除非程序员特意用STRIP命令去掉它)。然而,和编译器中的符号表不同,.symtab符号表不包含局部变量的条目。

具体内容:

1.ELF头

由图可知,ELF头以一个16字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序。

2.重定位节'.rela.text'

节头部表描述了不同节的位置与大小,并且目标文件的每个节都有一个固定大小的条目,其中记录了各节名称、类型、地址、偏移量、大小、全体大小、旗标、连接、信息、对齐信息。

由图可知'.rela.text'是一个.text节中位置的列表,包含.text节中需要进行重定位的信息,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。如图4.4,图中8条重定位信息分别是对.L0(第一个printf中的字符串)、puts函数、exit函数、.L1(第二个printf中的字符串)、printf函数、sleepsecs、sleep函数、getchar函数进行重定位声明。

其中偏移量为需要进行重定向的代码在.text或.data节中的偏移位置,信息包括symbol和type两部分,其中symbol占前4个字节,type占后4个字节,symbol代表重定位到的目标在.symtab中的偏移量,type代表重定位的类型,类型代表重定位到的目标的类型,符号名称和加数则指出了重定向到的目标的名称计算重定位位置的辅助信息。

3.重定位节 '.rela.eh_frame'

'.rela.eh_frame'包含了eh_frame节的重定位信息。

符号表

.symtab存放着程序中定义和引用函数和全局变量的信息。且不包含局部变量的条目。重定位需要引用的符号都在其中声明。

4.4 Hello.o的结果解析

hello.s 的内容

hello.o反汇编的内容

比较来看hello.s和hello.o操作数赖看hello.s是十进制,hello.o是十六进制,其中的内容其实大部分是差不太多的,但是左半边hello.o多了很多东西。有前面的机器指令的位置,以及后面的对应每一行汇编语句的机器指令,显然这是给机器读的以为机器是二进制实现的,0和1构成,方便查看所以使用十六进制

4.4.1 分支跳转语句

对比来看,不难发现,在hello.s文件中,跳转到的位置都是用.L3/.L4等来进行表示的,但是在hello.o中,跳转目标位置是由确实的地址来表示的。

4.4.2 函数调用

hello.s的函数调用是通过使用call+函数名,比如call getchar@PLT而在hello.o中是call+地址,例如callq 7e<main+0x7e>指的是相对于main函数的地址偏移量为7e的函数位置。

4.5 本章小结

汇编器对hello.s文件进行汇编,生成了可重定位文件。对文件的全局变量,函数,程序语句都进行了分析,给出了初始的相对位置信息,相当于对整个文件做出了一个初始的整理,为后面的链接等操作做准备。

(第41分)

5章 链接

5.1 链接的概念与作用

链接的概念:链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可以被加载到内存并执行。链接可以执行于编译时,也就是在源代码被编译成机器代码时;也可以执行于加载时,也就是在程序被加载器加载到内存并执行时;甚至于运行时,也就是由应用程序来执行。在早期的计算机系统中,链接是手动执行的。在现代系统中,链接是由叫做链接器的程序自动执行的。

链接的作用:

链接在软件开发中扮演着一个关键的角色,因为它们使得分离编译成为可能。链接能将各种代码和数据片段收集并组合成为一个单一文件,即链接多个目标文件成一个可执行文件。

在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的格式

使用readelf来查看hello:

ELF的各个作用

1.ELF头:描述文件的总体格式

2.段头部表:将连续的文件映射到运行时的内存段。

3…init节:定义了一个_init函数,程序的初始化代码会调用它。

4…text节:已编译程序的机器代码。

5…rodata节:只读数据,比如printf语句中的格式串和开关语句的跳转表

6…data节:已初始化的全局和静态C变量。局部C变量在运行时被保存在栈中,既不出现在.data节中,也不出现在.bss节中。

7…bss节:未初始化的全局和静态C变量,以及所有被初始化为0的全局或静态变量。

8…symtab节:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。

9…debug节:一个调试符号表,其条目是程序中定义的全局变量和类型定义,程序中定义和引用的全局变量,以及原始的C源文件。

10…line节:原始C源程序的行号和.text节中机器指令之间的映射

11…strtab节:一个字符串表,其内容包括.symtab和.debug节中的符号表,以及节头部中的节名字。

12.节头部表:描述目标文件的节。

首先来看ELF头

我们可以看到section headers节头表有27个

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

5.4 hello的虚拟地址空间

在edb的Data Dump一栏中可以看到程序的虚拟地址。

代码段开始于0x400000处,程序被载入,然后查看elf文件里面的programheaders,程序头表在执行时被使用,它负责提供链接器加载的内容并提供动态链接的信息分析elf里面的Program Headers:

PHDR:程序头表

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

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

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

NOTE::辅助信息

GNU_EH_FRAME:保存异常信息

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

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

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

5.5 链接的重定位过程分析

hello的objdump

hello.o的objdump

可以明显看出来可执行文件对每个函数给出了重定位处理,给出了它们的绝对物理地址;而在hello.o中,只是给出了每个函数对于程序首地址的偏移地址。

通过上面两个文件,可以了解到链接实现的过程:将可重定位文件中的.text节中函数以及全局变量的相对地址转变为了绝对地址,全局变量的寻址方式0x0%rsp也将0x0改为了确定的地址。

对hello.中的重定位节中的定位:在hello.o中,给出了函数和全局变量相对于EIF头的偏移量,所以在链接后,给定了程序首地址,然后根据偏移量,计算出函数和全局变量的绝对地址

链接主要分为两个过程:符号解析和重定位。

1)符号解析:目标文件定义和引用符号,符号解析将每个符号引用和一个符号定义关联起来。

2)重定位:编译器和汇编器生成从0开始的代码和数据节。链接器通过把每个符号定义与一个内存位置关联起来,从而重定位这些节,然后修改所有对这些符号的引用,使得它们指向这个内存位置。链接器使用汇编器产生的重定位条目的详细指令,不加甄别地执行这样的重定位。

对于hello来说,链接器把hello中的符号定义都与一个内存位置关联了起来,重定位了这些节,并在之后对符号的引用中把它们指向重定位后的地方。hello中每条指令都对应了一个虚拟地址,而且对每个函数,全局变量也都它关联到了一个虚拟地址,在函数调用,全局变量的引用,以及跳转等操作时都通过虚拟地址来进行,从而执行这些指令。

5.6 hello的执行流程

在main函数之前执行的函数有:

_start;

_libc_start_main@plt;

__libc_csu_init;

_init;

frame_dummy;

register_tm_clones;

在main函数之后执行的程序有:

Exit();

cxa_thread_atexit_impl;

fini;

5.7 Hello的动态链接分析

在进行动态链接前,首先进行静态链接,生成部分链接的可执行目标文件hello。此时共享库中的代码和数据没有被合并到hello中。加载hello时,动态链接器对共享目标文件中的相应模块内的代码和数据进行重定位,加载共享库,生成完全链接的可执行目标文件。

动态链接采用了延迟加载的策略,即在调用函数时才进行符号的映射。使用偏移量表GOT+过程链接表PLT实现函数的动态链接。GOT中存放函数目标地址,为每个全局函数创建一个副本函数,并将对函数的调用转换成对副本函数调用。

调用init之前的.got.plt

调用init之后的.got.plt

5.8 本章小结

通过本章了解了c语言的链接过程,明白了链接的概念及作用,学会了GCC下链接操作对应的指令,并学会了查看hello运行时的虚拟地址空间,了解了hello的重定位过程,对hello的执行流程有了一定的了解,同时对hello的动态链接过程进行了分析。

(第51分)

6章 hello进程管理

6.1 进程的概念与作用

概念:进程的经典定义是一个执行中的程序的实例。系统中的每个程序都运行在某个进程的上下文中。上下文是由程序正确运行所需的状态组成的。这个状态包括存放在内存中的代码和数据,它的栈、通用目的寄存器中的内容、程序计数器、环境变量和打开文件描述符的集合。进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。

作用:在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

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

作用:shell是命令行界面,是系统内核的一层壳,作用是用来保护内核同时传递入与计算机交互的信息.它只是系统的一个工具,我们可以使用它来操作计算机。

处理流程:

从终端读入输入的命令。

Shell对用户输入命令进行解析,判断是否为内置命令。

如果是内置命令则立即调用内置命令处理函数,否则调用execve函数创建一个子进程进行运行。

判断是否为前台运行程序,如果是,则调用等待函数等待前台作业结束;否则将程序转入后台,直接开始下一次用户输入命令。

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

6.3 Hello的fork进程创建过程

父进程通过调用fork函数创建一个新的运行的子进程。调用fork函数后,新创建的子进程几乎但不完全与父进程相同:子进程得到与父进程虚拟地址空间相同的(但是独立的)一份副本(包括代码、数据段、堆、共享库以及用户栈),子进程获得与父进程任何打开文件描述符相同的副本,这意味着当父进程调用fork时,子进程可以读写父进程中打开的任何文件。fork被调用一次,却返回两次,子进程返回0,父进程返回子进程的PID。子进程有不同于父进程的PID。

6.4 Hello的execve过程

execve函数加载并运行可执行目标文件,且带参数列表argv和环境变量列表envp。它在加载文件后,调用文件的启动代码,加载器删除子进程现有的虚拟内存段,并创建一组新的代码、数据、堆和栈段。新的栈和堆段被初始化为零,通过将虚拟地址空间中的页映射到可执行文件的页大小的片,新的代码和数据段被初始化为可执行文件中的内容。

6.5 Hello的进程执行

在获取程序进程ID(PID)之后,hello等待被内核执行,内核为hello保存一个上下文,这个上下文包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构(比如页表、进程表、文件表)等等。

Execve函数在当前进程的上下文中加载并运行一个新程序,当hello在内核中运行的时候,并行的也有其他进程,一个进程执行它的控制流的一部分的每一时间段叫做时间片,所以内核可以控制到底是否需要枪战当前进程,并重新开始一个先前被挂起的进程或者别的进程,这个过程成为调度。

在此前提下,hello程序与操作系统其他的进程通过内核的调度,切换上下文,并行地完成进程。当程序在完成一些操作的时候,例如调用一些系统函数,访问一些系统参数,当前程序需要核心权限,这时候就要让程序从用户态转变为核心态,通过信号函数的作用完成这个转换。

6.6 hello的异常与信号处理

hello执行过程中出现的异常种类可能会有:中断、陷阱、故障、终止。

乱按和空格:

这都不会影响程序输出,因为乱按只是将屏幕的输入缓存到stdin,当getchar的时候读出一个’\n’结尾的字串(作为一次输入),其他字串会当做shell命令行输入。

运行时输入Ctrl-Z

可以看到输入Ctrl-Z后,hello进程停止了,原因是shell父进程收到SIGSTP信号,信号处理函数的逻辑是打印屏幕回显、将hello进程挂起。

输入ps命令查看PID

可以看到我们的hello进程被挂起,PID为27094

输入jobs命令:

可以看到hello进程的job号为1

pstree命令是查看进程树

输入fg指令后:

程序会继续运行。

kill命令

运行时输入Ctrl-C

可以看到输入Ctrl-Chello进程终止了,原因是当按下ctrl-c之后,shell父进程收到SIGINT信号,信号处理函数的逻辑是结束hello,并回收hello进程。

6.7本章小结

在本章中,我们首先总结了进程的概念与作用,然后我们完整地分析并实践了hello程序是如何从头到尾运行的,并分析了可能出现的异常与信号处理,做出总结。

(第61分)

7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:又称相对地址,是程序运行由CPU产生的与段相关的偏移地址部分。他是描述一个程序运行段的地址。

物理地址:程序运行时加载到内存地址寄存器中的地址,内存单元的真正地址。他是在前端总线上传输的而且是唯一的。在hello程序中,他就表示了这个程序运行时的一条确切的指令在内存地址上的具体哪一块进行执行。

虚拟地址:使用虚拟寻址时,CPU通过生成一个虚拟地址来访问主存,这个虚拟地址在被送至内存前先转换成适当的物理地址。虚拟地址转化成物理地址的过程叫做地址翻译。在linux中,虚拟地址数值树等于线性地址,即hello中看到的地址加上对应段基地址的值。

线性地址:这个和虚拟地址是同一个东西,是经过段机制转化之后用于描述程序分页信息的地址。他是对程序运行区块的一个抽象映射。”

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

在x86保护模式下,段的信息(段基线性地址、长度、权限等)即段描述符占8个字节,段信息无法直接存放在段寄存器中(段寄存器只有2字节)。Intel的设计是段描述符集中存放在GDT或LDT中,而段寄存器存放的是段描述符在GDT或LDT内的索引值(index)。

一个逻辑地址由两部份组成,段标识符: 段内偏移量。

首先,给定一个完整的逻辑地址[段选择符:段内偏移地址],

1、看段选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再依据对应寄存器,得到其地址和大小。我们就有了一个数组了。

2、拿出段选择符中前13位,能够在这个数组中。查找到相应的段描写叙述符,这样。它了Base。即基地址就知道了。

3、把Base + offset,就是要转换的线性地址了。

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

线性地址即虚拟地址,用VA来表示。VA被分为虚拟页号(VPN)与虚拟页偏移量(VPO),CPU取出虚拟页号,通过页表基址寄存器(PTBR)来定位页表条目,在有效位为1时,从页表条目中取出信息物理页号(PPN),通过将物理页号与虚拟页偏移量(VPO)结合,得到由物理地址(PPN)和物理页偏移量(PPO)组合的物理地址。

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

36位VPN 被划分成四个9位的片,每个片被用作到一个页表的偏移量。CR3 寄存器包含Ll页表的物理地址。VPN 1提供到一个Ll PET的偏移量,这个PTE包含L2页表的基地址。VPN 2提供到一个L2 PTE的偏移量,以此类推。

Core i7是四级页表进行的虚拟地址转物理地址。48位的虚拟地址的前36位被分为四级VPN区。结合存放在CR3的基址寄存器,由前面多级页表的知识,可以确定最终的PPN,与VPO结合得到物理地址。

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

首先取组索引对应位,向L1cache中寻找对应组。如果存在,则比较标志位,并检查对应行的有效位是否为1。如果上述条件均满足则命中。否则按顺序对L2cache、L3cache、内存进行相同操作,直到出现命中。然后向上级cache返回直到L1cache。如果有空闲块则将目标块放置到空闲块中,否则将缓存中的某个块驱逐,将目标块放到被驱逐块的原位置。

7.6 hello进程fork时的内存映射

在shell输入命令行后,内核调用fork创建子进程,为hello程序的运行创建上下文,并分配一个与父进程不同的PID。通过fork创建的子进程拥有父进程相同的区域结构、页表等的一份副本,同时子进程也可以访问任何父进程已经打开的文件。当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同,当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面,因此,也就为每个进程保持了私有地址空间。

7.7 hello进程execve时的内存映射

execve函数调用驻留在内核区域的启动加载器代码,在当前进程中加载并运行包含在可执行目标文件hello中的程序,用hello程序有效地替代了当前程序。加载并运行hello需要以下几个步骤:

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

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

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

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

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

缺页故障是一种常见的故障,当指令引用一个虚拟地址,在MMU中查找页表时发现与该地址相对应的物理地址不在内存中,因此必须从磁盘中取出的时候就会发生故障。

缺页中断处理:缺页处理程序是系统内核中的代码,选择一个牺牲页面,如果这个牺牲页面被修改过,那么就将它交换出去,换入新的页面并更新页表。当缺页处理程序返回时,CPU重新启动引起缺页的指令,这条指令再次发送VA到MMU,这次MMU就能正常翻译VA了。

7.9动态存储分配管理

动态内存分配器维护着一个进程的虚拟内存区域,称为堆(heap) 。系统之间细节不同,但是不失通用性,假设堆是一个请求二进制零的区域,它紧接在未初始化的数据区域后开始,并向上生长(向更高的地址) 。对于每个进程,内核维护着一个变量brk, 它指向堆的顶部。分配器将堆视为一组不同大小的块(block) 的集合来维护。每个块就是一个连续的虚拟内存片(chunk),要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可用来分配。空闲块保持空闲,直到它显式地被应用所分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。

基本方法:这里指的基本方法应该是在合并块的时候使用到的方法,有最佳适配和第二次适配还有首次适配方法,首次适配就是指的是第一次遇到的就直接适配分配,第二次顾名思义就是第二次适配上的,最佳适配就是搜索完以后最佳的方案,当然这种的会在搜索速度上大有降低。

策略:这里的策略指的就是显式的链表的方式分配还是隐式的标签引脚的方式分配还是分离适配,带边界标签的隐式空闲链表分配器允许在常数时间内进行对前面块的合并。这种思想是在每个块的结尾处添加一个脚部,其中脚部就是头部的一个副本。如果每个块包括这样一个脚部,那么分配器就可以通过检查它的脚部,判断前面一个块的起始位置和状态,这个脚部总是在距当前块开始位置一个字的距离。显式空间链表就是将空闲块组织为某种形式的显式数据结构。因为根据定义,程序不需要一个空闲块的主体,所以实现这个数据结构的指针可以存放在这些空闲块的主体里面。例如,堆可以组织成一个双向空闲链表,在每个空闲块中,都包含一个前驱和后继指针,使首次适配的分配时间从块总数的线性时间减少到了空闲块数量的线性时间。为了分配一个块,必须确定请求的大小类,并且对适当的空闲链表做首次适配,查找一个合适的块。如果找到了一个,那么就(可选地)分割它,并将剩余的部分插入到适当的空闲链表中。如果找不到合适的块,那么就搜索下一个更大的大小类的空闲链表。如此重复,直到找到一个合适的块。如果空闲链表中没有合适的块,那么就向操作系统请求额外的堆内存,从这个新的堆内存中分配出一个块,将剩余部分放置在适当的大小类中。要释放一个块,我们执行合并,并将结果放置到相应的空闲链表中。

7.10本章小结

本章介绍了储存器的地址空间,讲述了虚拟地址、物理地址、线性地址、逻辑地址的概念,还有进程fork和execve时的内存映射的内容。描述了系统如何应对那些缺页异常,最后描述了malloc的内存分配管理机制(C语言为例)。

(第7 2分)

8章 hello的IO管理

8.1 Linux的IO设备管理方法

一个Linux文件就是一个m字节的序列。所有的I/O设备(例如网络、磁盘和终端)都被模型化为文件,而所有的输入和输出都被当做对相应文件的读和写来执行。这种将设备优雅地映射为文件的方式,允许Linux内核引出一个简单、低级的应用接口,称为Unix I/O。这使得所有输入和输出都能以一种统一且一致的方式来执行:

1. 打开文件。一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备。

2. Linux Shell创建的每个进程开始时都有三个打开的文件:标准输入、标准输出、标准错误。

3. 改变当前文件的位置。对于每个打开的文件,内核保持着一个文件位置k、初始为0。这个文件位置是从文件开头起始的字节偏移量。应用程序能够通过执行seek操作,显示地设置文件的当前位置为k。

4. 读写文件。一个读操作就是从文件复制n>0个字节到内存,从当前文件位置k开始,然后将k增加到k+n。写操作就是从内存复制n>0个字节到一个文件,从当前文件位置k开始,然后更新k。

5. 关闭文件。当应用完成了对文件的访问之后,它就通知内核关闭这个文件。

8.2 简述Unix IO接口及其函数

Linux提供如下 IO接口函数:

(1)read和write:最简单的读写函数;

(2)readn和writen:原子性读写操作;

(3)recvfrom和sendto:增加了目标地址和地址结构长度的参数;

(4)recv和send:允许从进程到内核传递标志;

(5)read和writev:允许指定往其中输入数据或从其中输出数据的缓冲区;

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

8.3 printf的实现分析

int

printf(const char fmt, …)

{

int i;

char

buf[256];

va_list arg

= (va_list)((char)(&fmt) + 4);

//arg是一个指针,表示的是被省略参数中的第一个参数。

i =

vsprintf(buf, fmt, arg);

//vsprintf的作用就是格式化。它接受确定输出格式的格式字符串fmt(输入)。//用格式字符串对个数变化的参数进行格式化,产生格式化输出。

write(buf,

i);// 接受buf与需要输出的参数个数,

//执行写操作,把buf中的i个元素的值写到终端

return i;

}

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

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

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

8.4 getchar的实现分析

int

getchar(void)

{

char c;

return

(read(0,&c,1)==1)?(unsigned char)c:EOF

//read函数的第一个参数是描述符fd,0代表标准输入

//第二个参数是输入内容的指针,这里是所读取字符的地址

//最后一个参数等于1表示读入字符数为1

}

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

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

8.5本章小结

hello进程需要I/O设备的信号来正确执行,而Unix通过将设备映射为文件来管理I/O设备。

printf()和getchar()函数通过调用vprintf()函数处理传入的参数,调用write()函数将数据提交给操作系统,然后操作系统通过管理硬件最后实现终端显示。

(第81分)

结论

1、首先有高级语言产生的hello.c文件

2、经过预处理后,hello.c进行了宏替换并且还将库函数.h文件等添加进入文件生成了hello.i

3、再通过编译器编译产生hello.s,变成了汇编语言。

4、再通过汇编器产生了hello.o,变成了可重定位文件。

5、通过链接器,将其他可重定位的文件写入进来变成hello可执行文件。

6、在shell中按要求输入并执行该可执行文件,shell会先fork一个子进程然后execve,加载运行hello。

7、创建了虚拟内存空间,并映射到物理内存。

8、在sleep函数时会发生异常,然后上下文切换。

9、printf函数会使用malloc函数申请堆空间,使用到动态内存管理技术。

10、printf函数会使用IO设备管理进行输出。

11、最后,进程结束被回收。Hello结束。

感想:感觉这门课虽然要结束了,但是要去学的东西还有很多很多,很多地方只是了解了大概,底层东西还是不太清楚,学海无涯,以前总是关注表面的东西,但是这常常导致编程错误,但是在学习了计算机系统之后,通篇读完这本书,包括写完这个大作业,对系统的整体等等有了更深的认识,明白了许多当时犯下的错误是因为什么,明白了做任何事不能在上面人云亦云,就像hello的一生,简单平凡,需要在底层,明白如何完成的,才知道自己该干什么不该干什么。hello的一生到此就结束了,但是我们的学习生涯才算刚刚开始,加油!

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

附件

hello.c 源文件

hello.i 预处理后的文件

hello.s 编译后的汇编文件

hello.o 汇编后的可重定位文件

hello 链接后的可执行文件

hello.elf hello的elf文件

helloout.elf hello.o的elf文件

hello1.txt objdump hello的反汇编文件

hello0.txt objdump hello.o的反汇编文件

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值