计算机系统

大作业

题 目 程序人生-Hello’s P2P

专 业 计算机科学与技术

学   号 2021113309

班   级 21w0311

学 生 白梽新    

指 导 教 师 史先俊   

计算机科学与技术学院

2022年5月

摘 要

本文主要讲述了hello程序从编写代码、预处理、编译、汇编、链接直到被回收的整个过程和原理,还介绍了hello的进程管理、存储管理相关内容,较为全面的讲述了hello程序的一生

关键词:计算机系统;hello的一生;编译;链接;虚拟内存;

目 录

第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.c的源程序由编辑器建立,通过运行预处理器将其进行预处理生成hello.i文件,再运行C编译器将其进行翻译生成汇编语言文件hello.s。运行汇编器将其翻译成一个可重定位目标文件hello.最终运行链接器程序将hello.o和系统目标文件组合起来,创建了一个可执行目标文件hello

通过shell输入./shell,shell通过fork函数创建了一个新的进程,之后调用execve映射虚拟内存,通过mmap为hello程序开创了一片空间。CPU从虚拟内存中的.text,.data节取代码和数据,调度器为进程规划时间片,有异常时触发异常处理子程序。当程序运行结束时,父进程回收hello进程和它创建的子进程,内核删除相关数据结构

1.2 环境与工具

硬件环境:X64 CPU;2GHz;4GRAM;256Disk

软件环境:Windows10 64位;Vmware 10;Ubuntu 20.04 LTS 64位

使用工具:Codeblocks;Objdump;Gdb;Hexedit

1.3 中间结果

1.hello.c 源文件

2.hello.i 预处理后的文件

3.hello.s 编译后的汇编文件

4.hello.o 可冲定位文件

5.hello 可执行文件

6.hello.elf hello的elf文件

1.4 本章小结

本章简单介绍了hellop2p020过程,列出了本次实验信息:环境、中间结果,并且简述了hello程序从c程序hello.c到可执行目标文件hello经过的历程。

第2章 预处理

2.1 预处理的概念与作用

预处理器对源程序中以#开头的命令进行处理,例如将#include命令后面的.h内容嵌入到源程序文件中。预处理器的输出结果还是一个源程序文件,以.i为扩展名。

主要作用为:处理预处理指令,删除注释以及宏替换。

2.2在Ubuntu下预处理的命令

命令:gcc -E hello.c -o hello.i

图1 预处理命令

图2 预处理生成文件

2.3 Hello的预处理结果解析

预处理器将注释消除了,对源文件中的#include语句进行预处理的结果,将头文件包含的文件插入程序文本中,从而使得预处理产生的文件比原来多了很多内容

2.4 本章小结

预处理器对.c 文件进行了处理预处理指令,删除注释以及宏替换等操作,产生hello.i 文件,并将剩下的任务交给编译器。

第3章 编译

3.1 编译的概念与作用

编译器对预处理后的源程序进行编译,生成一个汇编语言源程序文件,以.s为扩展名,例如,例如将hello.i转变为hello.s。因为汇编语言与具体的机器结构有关,从而对同一台机器而言,不管什么高级语言,编译转换后的输出结果使用的都是同一种汇编语言。

作用是经过语法分析、词法分析等一系列操作完成编译过程,并生成最终的二进制文件。

3.2 在Ubuntu下编译的命令

命令:gcc -S hello.i -o hello.s

图3 编译命令

图4 生成文件

3.3 Hello的编译结果解析

3.3.1数据

(1)常量

只有字符串常量

在.s文件中,用编码来表示该字符串常量

(2)变量

整型变量包括代表字符串数组元素个数的参数acgc,和函数内部定义的变量i

均为局部变量储存在栈中

Argc是第一个函数,由寄存器edi 传递,并且储存在栈中

变量i也被储存在栈中,并且还被赋了初值

(3)字符串数组

函数的参数为字符串数组的指针,这个地址先被保存在寄存器中,再被转存到栈中

当字符串数组被输出的时候,内容先被传递给rsi和rdi,再给printf函数,实现对内容的输出

图5 字符串输出

3.3.2赋值

.c文件中只有对局部变量i的赋值,在.s文件中,通过movl指令将0赋给i,i占四个字节,所以采用l后缀

3.3.3算数操作

每次循环i++,.s文件中用addl指令实现此操作

3.3.4关系操作

(1)“!=”

Argc!=4的判断是用cmpl和je指令完成的,用cmpl进行比较,je判断是否相等,如果相等就跳转到L2

(2)“<”

通过cmpl和jle实现,通过cmpl进行比较,再用jle判断,若i<=7就跳到L4,否则执行其他指令

3.3.5数组/指针/结构操作

字符串数组argv[],首地址被存在栈中,通过首地址+偏移量的方式实现对数组的访问

3.3.6控制转移

(1)if

程序通过if判断argc是否等于4,若相等则跳到L2,执行if内的语句,否则就跳过if语句中的内容

(2)for

程序中i的初值为0,若i小于等于7,就跳转到L4,执行for循环内的语句,否则不进行跳转,执行for循环外之后的语句

3.3.7函数操作

(1)参数传递

函数参数用寄存器来传递,比如传递给printf 的参数,三个参数传递的都是地址

(2)函数调用

函数调用通过call完成,参数由寄存器传递

(3)函数返回

函数返回值存放在rax中,如图将返回值存放在edi中传递给sleep函数

3.4 本章小结

这一章主要介绍了编译的概念作用,以及数据、算数操作、控制转移、函数操作等具体是如何被编译的

第4章 汇编

4.1 汇编的概念与作用

汇编是指把汇编语言代码转换为机器语言表示的过程

作用:将hello.s翻译成机器语言指令,把这些指令打包成一种壳虫定位目标程序的格式并把结果保存在文件hello.o中。

4.2 在Ubuntu下汇编的命令

Gcc -c hello.s -o hello.o

图6 汇编命令

图7 汇编生成文件

4.3 可重定位目标elf格式

4.3.1ELF

一个16字节序列(描述字大小和字节顺序),包括ELF头大小、目标文件类型、机器类型、节头部表文件、偏移、条目大小和数量

图8 ELF头

4.3.2节头部表

包含了不同节的位置和大小

各节包含的内容为:

.text:机器码

.rodata:只读数据、格式串、跳转表等

.data:已初始化的全局、静态变量。

.bss:未初始化或初值为0的全局和静态C变量。

.symtab:符号表,存放函数和全局变量信息

.rel.text:链接时需要修改的位置

.rel.data:全局变量的重定位信息。

.bebug:一个调试符号表

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

.strtab:字符串表

图9 节头部表

4.3.3重定位节

连接器将所有相同类型的节合并为聚合节,并为其分配运行时的内存地址

图10 重定位节

4.3.4符号表

每个可重定位目标模块都有一个符号表,包含定义和引用的符号的信息,符号表是由汇编器构造的

图11 符号表

4.4 Hello.o的结果解析

通过命令objdump -d hello.o对其进行反汇编,结果为

图12 反汇编结果

与hello.s进行对比分析,发现在.s中,操作数是十进制的,而.o的反汇编中,操作数是十六进制的,对于条件分支而言,以if为例,.s中程序被分成了不同的段,需要跳转时说明要跳转到哪个段,而.o中是不分段的,它通过main入口的值加上偏移量去实现跳转;对于函数调用,在.s中使用call加函数名,在.o中,通过call加偏移地址,对于一些不确定地址的函数调用,将call指令后的相对地址设置为全0,然后在.rela.text节中为其添加重定位条目,等待静态链接的进一步确定。

4.5 本章小结

这一章对 hello.s 进行了汇编操作,生成可重定位文件 hello.o。介绍了汇编的概念与作用,以及可重定位目标文件的elf格式,列举了gejie每个节包含的信息对汇编代码和机器代码的差别进行了分析。

第5章 链接

5.1 链接的概念与作用

链接是将各种代码和数据片段收集组合成一个单一文件的过程。这个文件可以直接被加载到内存中执行。

作用:使得编译分离,我们不用将一个大的应用程序组织为一个巨大的源文件,而是可以通过分解为更小的、更好管理的模块,单独编译这些模块。

5.2 在Ubuntu下链接的命令

ld -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 /usr/lib/gcc/x86_64-linux-gnu/9/crtbegin.o hello.o -lc /usr/lib/gcc/x86_64-linux-gnu/9/crtend.o /usr/lib/x86_64-linux-gnu/crtn.o -z relro -o hello

图13 链接命令

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

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

图14 节头部表

代码段号15,数据段号为24,bss段号为25

5.4 hello的虚拟地址空间

使用edb加载hello,详细信息如下:

图15 edb结果

观察Data Dump窗口,发现虚拟地址从0x401000开始到0x401ff0结束。根据的节头部表,可以通过edb找到各个节的信息

图16 虚拟地址空间

5.5 链接的重定位过程分析

通过objdump -d -r hello ,分析hello与hello.o的不同

5.5.1链接过程

在hello.o中,访问的地址是偏移地址,但是在hello中,访问的地址是虚拟地址,main 函数在地址0x4011d6开始

图17 地址

在hello中,左右要重定位的项目都进行了重定位,对函数的调用变为虚拟地址,对字符串的访问也是

新增加了.init、.plt节

图18 新增节

把程序中的函数也链接进了文件中

图19 新增函数

链接分为符号解析和重定位,符号解析将每个符号引用与一个符号定义关联起来,重定位将符号定义与一个内存位置关联起来,然后修改所有对这些符号的引用

5.5.2重定位

重定位分为两个步骤

  1. 重定位节和符号定义:链接器将所有相同类型的节合并为聚合节,并为其分配运行时的内存地址
  2. 重定位节中的符号引用:链接器修改代码节和数据节中对符号的引用,使他们指向正确运行时的地址

5.6 hello的执行流程

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

图20 edb执行结果

ld-2.31.so!dl_start 0x00007f807a1180b3

ld-2.31.so!dl_init 0x00007f5a70281c10

Hello!_start 0x000000000040111e

libc-2.31.so!_libc_start-main 0x00007fd1c3e7a550

Hello!main 0x00000000004010d0

Hello!printf@plt 0x00000000004010a

hello!atoi@plt 0x0000000004010c0

Hello!sleep@plt 0x0004010e0

hello!getchar@plt 0x00000000004010b0

libc-2.31.so!exit 0x00007d1c3e6f460

5.7 Hello的动态链接分析

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

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

5.8 本章小结

这一章介绍了链接的作用与概念,除此之外,分析了可执行文件hello的格式,分析了hello的虚拟地址空间,分析了链接的重定位过程。

第6章 hello进程管理

6.1 进程的概念与作用

进程是计算机科学中最深刻最成功的概念之一,他的经典定义就是一个执行程序中的实例,系统中的每个程序都运行在某个进程的上下文中。

作用:进程提供给应用程序的关键抽象

(1)一个独立的逻辑控制流(2)一个私有的地址空间

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

作用:实际上Shell是一个命令解释器,它解释由用户输入的命令并且把它们送到内核。不仅如此,Shell有自己的编程语言用于对命令的编辑,它允许用户编写由shell命令组成的程序。Shell编程语言具有普通编程语言的很多特点, ,比如它也有循环结构和分支控制结构等,用这种编程语言编写的Shell程序与其他应用程序具有同样的效果

处理流程:shell首先检查命令是否是内部命令,若不是再检查是否是一个应用程序(这里的应用程序可以是Linux本身的实用程序,如lsrm,也可以是购买的商业程序,如xv,或者是自由软件,如emacs. 然后shell在搜索路径里寻找这些应用程序(搜索路径就是一个能找到可执行程序的目录列表)。如果键入的命令不是一个内部命令并且在路径里没有找到这个可执行文件,将会显示一条错误信息。如果能够成功找到命令,该内部命令或应用程序将被分解为系统调用并传给Linux内核。

6.3 Hello的fork进程创建过程

终端对输入的命令行进行解析,调用fork函数来创建一个子进程来执行命令。新创建的子进程除了进程ID不同外几乎与父进程相同。子进程得到与父进程用户级虚拟地址空间相同的但是独立的一份副本,包括代码和数据段、堆、共享库以及用户栈。子进程还获得与父进程任何打开文件描述符相同的副本,因此当父进程调用fork时,子进程可以读写父进程中打开的任何文件。

6.4 Hello的execve过程

execve函数加载并运行可执行目标文件hello,且带参数列表argv和环境变量列表envp。删除已存在的用户区域。映射私有区域,为hello的代码、数据、bss和栈区域创建新的区域结构。映射共享区域。设置程序计数器(PC)。 execve做的最后一件事情就是设置当前进程上下文中的程序计数器,使之指向代码区域的人口点

6.5 Hello的进程执行

6.5.1上下文信息

内核重启一个被抢占的进程所需要的状态,包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器等

6.5.2 进程时间片

一个进程执行它的控制流的一部分的每一时间段叫做时间片

6.5.3用户模式与内核模式

处理器通过某个控制寄存器的模式位判定,设置了模式位,进程就运行在内核模式,否则就运行在用户模式,运行应用程序代码的进程初始时是在用户模式中的,进程从用户模式变为内核模式的唯一方法是通过中断、故障或陷阱调用这样的异常

6.5.4 进程调度过程

在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占了的进程。这种决策就叫做调度,是由内核中称为调度器的代码处理的。当内核选择一个新的进程运行时,内核调度了这个进程。在内核调度了一个新的进程运行后,它就会抢占当前进程,并使用一种称为上下文切换的机制来将控制转移到新的进程

6.6 hello的异常与信号处理

  1. 乱按,回车

图21 乱按

可见乱按加回车不会影响程序的正常执行,乱按输入的内容会被当做无效的命令

  1. crtl+z

图22 ctrl+z

在输出过程中按下CTRL+Z,程序中止并退出,此时调用ps指令查看后台进程,发现程序并未终止,于是通过fg命令继续执行该进程,发现程序继续执行

  1. crtl+c

图23 ps

在输出过程中按下crtl+c,程序终止,此时调用ps指令查看后台进程,未发现hello程序,说明程序已终止

6.7本章小结

这一章主要介绍了进程的概念与作用,描述了shell的作用与处理流程,描述了hello的fork进程创建过程以及execve过程,描述了进程上下文信息、用户模式内核模式、进程调度过程等,最后结合实践说明了hello的异常以及信号处理过程。

第7章 hello的存储管理

7.1 hello的存储器地址空间

物理地址:用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相相应。是指出目前CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果地址。如果启用了分页机制,那么线性地址会使用页目录和页表中的项变换成物理地址。没有启用分页机制,那么线性地址就直接成为物理地址了。

线性地址:是逻辑地址到物理地址变换之间的中间层。程式代码会产生逻辑地址,或说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。如果启用了分页机制,那么线性地址能再经变换以产生一个物理地址。若没有启用分页机制,那么线性地址直接就是物理地址。

虚拟地址:是Windows程序时运行在386保护模式下,这样程序访问存储器所使用的逻辑地址称为虚拟地址,与实地址模式下的分段地址类似,虚拟地址也可以写为“段:偏移量”的形式,这里的段是指段选择器。Hello反汇编的地址即为虚拟地址。

逻辑地址:在有地址变换功能的计算机中,访内指令给出的地址 (操作数) 叫逻辑地址,也叫相对地址。逻辑地址用来指定一个操作数或指令,它由选择符和偏移量组成。是由一个段标识符加上一个指定段内相对地址的偏移量,表示为 [段地址:偏移地址]。在hello.o及hello.o的ELF格式文件中可以看到偏移量及相对偏移地址

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

一个逻辑地址由两部份组成,段标识符: 段内偏移量。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号。后面3位包含一些硬件细节.

逻辑地址到线性地址的转换过程:linux是通过分段机制,将逻辑地址转化为线性地址。通过数据段寄存器ds,可以找到此进程数据段所在的段描述符,再通过段描述符找到相应的线性地址。寄存器ds中保存了16位的段选择符,段选择符格式如下: TI:指明段描述符是在全局描述符表(GDT, TI=0)中或局部描述符表(LDT, TI=1)中 index:指定该段在GDT或LDT中的位置。看段选择符的T1=0还是1,声明当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地址和大小。使用段选择符中的前13位,在这个数组中查找到对应的段描述符,即可得到它的基地址,基地址Base + offset即要转换的线性地址。

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

CPU通过将逻辑地址转换为虚拟地址来访问主存,这个虚拟地址在访问主存前必须先转换成适当的物理地址。CPU芯片上叫做内存管理单元(MMU)的专用硬件,利用存放在主存中的查询表来动态翻译虚拟地址。然后CPU会通过这个物理地址来访问物理内存。

页表结构:在物理内存中存放着一个叫做页表的数据结构,页表将虚拟页映射到物理页,每次地址翻译硬件将一个虚拟地址转换为物理地址时,都会读取页表。

页表就是一个页表条目的数组,虚拟地址空间中的每个页在页表中的一个固定偏移量处都有一个PTE。PTE是由一个有效位和一个n个字段组成的。有效位表明了该虚拟页当前是否被缓存在DRAM中。如果设置了有效位,那么地址字段就表示DRAM中相应的物理页的起始位置。

MMU利用VPN来在虚拟页表中选择合适的PTE,当找到合适的PTE之后,PTE中的物理页号和VPO就会组合形成物理地址。其中VPO与PPO相同,因为虚拟页大小和物理页大小相同,所需要的偏移量位数也就相同。此时,物理地址就通过物理页号先找到对应的物理页,然后再根据物理页偏移找到具体的字节。

图24 页式管理

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

图25 多级页表

TLB是一个小的、虚拟寻址的缓存,其中每一行都保存着一个由单个PTE组成的块。TLB命中时具体过程如下:

(1)CPU产生一个虚拟地址(2)MMU从TLB中取出相应的PTE。

(3)MMU将这个虚拟地址翻译成一个物理地址,并将它发送到高速缓存/主存。

(4)高速缓存/主存将所请求的数据字返回给CPU

当TLB不命中时,MMU必须从L1缓存中取出相应的页表条目,新取的页表条目存放在TLB中,可能覆盖一个以及存在的条目。

在四级页表层次结构的地址翻译中,虚拟地址被划分为4个VPN和1个VPO。第i个VPN是一个到第i级页表的索引,第j级页表中的每个PTE都指向第j+1级某个页表的基址,第四级页表中的每个PTE包含某个物理页面的PPN,或者一个磁盘块的地址。为了构造物理地址,在能够确定PPN之前,MMU必须访问四个PTE。

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

酷睿i7 MMU使用四级页表来将虚拟地址翻译成物理地址,得到了物理地址PA。现在分析三级cache支持下的物理内存访问。L1 Cache是8路64组相联。块大小为64B。因此CO和CI都是6位,CT是40位。根据物理地址(PA),首先使用CI组索引,每组8路,分别匹配标记CT。如果匹配成功且块的有效位是1,则命中,根据块偏移CO返回数据. 如果没有匹配成功或者匹配成功但是标志位是1,则不命中,向下一级缓存中取出被请求的块,然后将新的块存储在组索引指示的组中的一个高速缓存行中。一般而言,如果映射到的组内有空闲块,则直接放置,否则必须驱逐出一个现存的块,一般采用最近最少被使用策略LRU进行替换.

7.6 hello进程fork时的内存映射

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

7.7 hello进程execve时的内存映射

exceve函数加载和执行程序Hello,需要以下几个步骤:

1.删除已存在的用户区域。

2.映射私有区域。为Hello的代码、数据、bss和栈区域创建新的区域结构,所有这些区域都是私有的、写时复制的。

3.映射共享区域。比如Hello程序与标准C库libc.so链接,这些对象都是动态链接到Hello的,然后再用户虚拟地址空间中的共享区域内。

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

图26 映射

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

缺页故障:当指令引用一个虚拟地址,在MMU中查找页表时发现与该地址相对应的物理地址不在内存中发生了故障,需要调用异常处理子程序从磁盘中取出。

图27 缺页处理

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

7.9动态存储分配管理

7.10本章小结

本章主要介绍了hello的存储器地址空间、intel的段式管理、hello 的页式管理,在intel Core7环境下介绍了VA 到PA的翻译、物理内存访问,还介绍hello进程fork时的内存映射、execve时的内存映射、缺页故障与缺页中断处理。

结论

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

1.编写hello.c的代码

2.预处理器处理hello.c,得到hello.i;

3.编译器使hello.i成为汇编文件hello.s;

4.汇编器将hello.s变为可重定位目标文件hello.o;

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

6.运行hello,在shell中输入./hello 2021113309 白梽新

7.shell调用fork创建子进程

8.shell调用execve函数,映射到虚拟内存中

9.进入程序,载入物理内存,进入main函数

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

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

12.信号处理:运行途中键入不同指令,做出不同反应。

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

感悟:hello只是一个很简单的程序,代码也只有二十多行,但这个程序的一生却需要运用到这么多知识,计算机真的是一个神奇而又伟大的创造,科学家前辈们真的都很了不起

附件

1.hello.c 源文件

2.hello.i 预处理后的文件

3.hello.s 编译后的汇编文件

4.hello.o 可冲定位文件

5.hello 可执行文件

6.hello.elf hello的elf文件

参考文献

[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.