计算机系统

大作业

题 目 程序人生-Hello’s P2P

专 业 计算机科学与技术

学   号 2021110802

班   级 21w0312

学 生 黄键树   

指 导 教 师 史先俊   

计算机科学与技术学院

2022年11月

摘 要

本文遍历了hello.c程序在Linux系统下的生命周期,通过gcc、objdump、edb等工具对其代码进行预处理、编译、汇编、链接与反汇编并进行分析,同时对hello的进程运行、内存管理等过程的进行探索,以更深入理解Linux系统下的存储层次结构、异常控制流、虚拟内存等相关内容,更加深入地理解计算机系统课程的知识。

关键词:计算机系统;编译;汇编;异常控制流;虚拟内存

目 录

第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简介

P2P过程:用户在shell中输入编译的命令后,gcc编译器驱动程序读取源程序文件hello.c,hello.c(源程序,文本)经过预处理器cpp处理后生成hello.i(源程序,文本),hello.i经过编译器处理变成hello.s(汇编语言程序,文本),hello.s经过汇编器as变成hello.o(可重定位目标程序,二进制),hello.o再通过链接器ld与其他c语言库进行链接,最后变成hello(可执行目标程序,二进制)。用户通过shell输入./hello后,shell执行一系列指令将hello目标文件中的代码和数据从磁盘复制到主存,然后调用fork函数为hello创建一个新的子进程,再通过execve执行这个进程, 然后使用mmap函数创建新的内存区域,并创建一组新的代码、数据、堆和栈。新的堆和栈被初始化为空,通过将虚拟地址空间中的页映射到可执行文件的页大小的片,新的代码和数据段被初始化为可执行文件的内容。最后,加载器跳转到_start地址,它最终会调用程序的main函数,然后程序从内存读取指令,取指译码执行,至此hello变成一个process,在hardware上驰骋。

020过程:hello程序执行结束以后,其进程会处于终止的状态,父进程会将其回收,释放其运行过程中占用的内存空间,使hello变成执行之前的状态,也就是又变回zero了。

1.2 环境与工具

硬件环境:intel i5 CPU

软件环境:ubuntu 64位

开发与调试工具:gedit、gcc、readelf、objdump、edb

1.3 中间结果

hello.c:c语言源程序

hello.i:hello.c预处理生成的文本文件

hello.s:hello.i经过编译器翻译成的汇编语言文本文件

hello.o:hello.s经汇编器翻译成机器语言指令并打包成的二进制可重定位目标文件

hello:经过hello.o链接生成的二进制可执行目标文件

1.4 本章小结

叙述了hello程序P2P和020的过程,介绍了研究所用到的硬件与软件环境、开发与调试工具,列出了生成的中间结果文件及其作用。

第2章 预处理

2.1 预处理的概念与作用

预处理会展开以#起始的行,试图解释为预处理指令(preprocessing directive),其中ISO C/C++要求支持的包括#if/#ifdef/#ifndef/#else/#elif/#endif(条件编译)、#define(宏定义)、#include(源文件包含)、#line(行控制)、#error(错误指令)、#pragma(和实现相关的杂注)以及单独的#(空指令)。主要流程如下:

1、将源文件中用#include形式声明的文件复制到新的程序中。

2、用实际值替换用#define定义的字符串。

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

2.2在Ubuntu下预处理的命令

gcc -E hello.c -o hello.i

图1 进行预处理

2.3 Hello的预处理结果解析

可以看到,hello.i中在原本hello.c的基础上对stdio.h、unistd.h、stdlib.h三个库进行了展开,将里面的函数复制了进去,并对原程序的缩进格式进行了一定的修改。

图2 部分库函数原型

图3 hello源代码

2.4 本章小结

.c文件中包含有头文件、宏定义以及一些其他操作都需要通过预处理来实现,预处理可以使得程序在后续的操作中不受阻碍,是可执行程序生成的流程中非常重要的步骤。

第3章 编译

3.1 编译的概念与作用

编译是利用编译器将预处理文件生成汇编程序的过程。主要包含6个阶段:词法分析、语法分析、语义分析、中间代码生成、代码优化、目标代码生成。

编译的作用是将文本文件hello.i翻译成文本文件hello.s,并在出现语法错误时给出信息。

图4 编译流程

3.2 在Ubuntu下编译的命令

gcc -S hello.i -o hello.s

图5 进行编译

3.3 Hello的编译结果解析

图6 hello.s

3.3.1数据

程序没有全局变量,main函数的数据编译如下,要打印的字符串被编译成.string “Hello %s %s\n”,main函数被编译为全局,类型为function。

图7 数据

3.3.2赋值

赋值语句i=0编译如下:

图8 i=0的赋值

3.3.3算数操作

算数表达式i++编译如下:

图9 i++汇编语言

3.3.4关系操作

关系操作有两个,if(argc!=4)编译如下:

图10 if(argc!=4)汇编

i<9编译如下:

图11 i<9汇编

3.3.5数组操作

printf执行时其中的argv[1],argv[2]编译如下:

图12 数组

3.3.6控制转移

if(argc!=4){…}编译如下:

图13 控制

若argc和4相等,就跳转至.L2处,否则继续执行接下来的语句。

for(i=0;i<9;i++){…}编译如下:

图14 for循环体的汇编

程序是从.L3处开始执行,先把i和8进行比较,若i小于等于8(即i小于9)就跳转至.L4处执行for循环体里面的语句,最后i++又开始新一轮的比较。

3.3.7函数操作

printf(“Hello %s %s\n”,argv[1],argv[2]);编译如下:

图15 printf的汇编

sleep(atoi(argv[3]));编译如下:

图16 sleep调用atoi的汇编

先进行计算取得argv[3],然后调用atoi函数,再调用sleep函数。

3.4 本章小结

本章进行了hello.i的编译,将其转换为hello.s汇编语言文件,并将其与原C程序代码进行比较,完成了对汇编代码的解析。

第4章 汇编

4.1 汇编的概念与作用

汇编是指把汇编语言翻译成机器语言的过程。

汇编的作用是将hello.s汇编语言文件翻译成机器语言指令,并把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在二进制目标文件hello.o中。hello.o包含hello程序执行的机器指令。

4.2 在Ubuntu下汇编的命令

gcc -c hello.s -o hello.o

图17 进行汇编

4.3 可重定位目标elf格式

ELF头:

图18 ELF头

节头:

图19 节头

重定位节:

图20 重定位节

可以看到.rodata、puts、exit、printf、atoi、sleep、getchar符号的偏移。链接器会依据重定位节的信息对可重定位目标文件进行链接得到可执行文件。

.symtab节:

图21 .symtab节

4.4 Hello.o的结果解析

使用objdump命令得到hello.o的反汇编如下:

图22 hello.o的反汇编-1

图23 hello.o的反汇编-2

hello.o的反汇编相比于hello.s,数字在hello.s中都是十进制,而在hello.o中都是十六进制。跳转语句原来对应的符号都变成了相对偏移地址。函数调用时原来的函数名也被替换成了函数的相对偏移地址。

机器语言是二进制的机器指令序列集合,每一条机器指令由操作码和操作数组成,是纯粹的二进制数据表示的语言,是电脑可以真正识别的语言。汇编语言是以人们比较熟悉的词句直接表述CPU动作的语言,是最接近CPU运行原理的较为通俗的比较容易理解的语言。机器语言与汇编语言具有一一对应的映射关系,一条机器语言对应一条汇编语言语句。

4.5 本章小结

本章进行了hello.s的汇编,将其转换为二进制可重定位目标程序文件hello.o,通过readelf读取其elf信息与重定位信息,得到其符号表的相关信息,并通过objdump反汇编目标文件,从中得到机器代码,并将机器代码与汇编代码进行对照,发现机器语言与汇编语言存在一一映射关系。

第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

图24 用ld命令进行链接

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

ELF头:

图25 ELF头

节头:

图26 节头

程序头:

图27 程序头

重定位节:

图28 重定位节

符号表:

图29 符号表

5.4 hello的虚拟地址空间

图30 用edb查看hello的虚拟地址

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

5.5 链接的重定位过程分析

使用objdump命令得到hello的反汇编如下:

图31 hello的反汇编

相比hello.o,hello中增加了hello.c源文件所用到的库函数,如printf、getchar、atoi等的具体实现,hello中添加了节,如.init、.fini等,hello.o中的相对偏移地址到了hello中变成了虚拟内存地址,hello.o中跳转以及函数调用的地址在hello中都被更换成了虚拟内存地址。

链接器在完成符号解析以后,就把代码中的每个符号引用和一个符号定义关联起来。此时,链接器就知道它的输入目标模块中的代码节和数据节的确切大小。然后就开始重定位,将合并输入模块,并为每个符号分配运行时的地址。链接器将所有输入到hello中相同类型的节合并为同一类型的新的节,然后链接器将运行时内存地址赋给新的节,赋给输入模块定义的每个节,以及赋给输入模块定义的每一个符号。当这一步完成时,程序中的每条指令和全局变量都有唯一的运行时内存地址了。然后是重定位节中的符号引用,链接器会修改hello中的代码节和数据节中对每一个符号的引用,使得他们指向正确的运行地址。

5.6 hello的执行流程

用edb查看hello的执行流程如下:

图32 用edb查看hello的执行流程

5.7 Hello的动态链接分析

在调用共享库函数时,编译器没有办法预测这个函数的运行时地址,因为定义它的共享模块在运行时可以加载到任意位置。正常的方法是为该引用生成一条重定位记录,然后动态链接器在程序加载的时候再解析它。GNU编译系统使用延迟绑定将过程地址的绑定推迟到第一次调用该过程时。延迟绑定是通过GOT(全局偏移量表)和PLT(过程链接表)实现的。GOT是数据段的一部分,而PLT是代码段的一部分。

在hello的elf中找到got表的位置为0x404000,可以看到在dl_init前后,其got[1]和got[2]的内容发生了变化。

图33 hello的elf中got表的位置

图34 dl_init前

图35 dl_init后

5.8 本章小结

本章分析了可执行文件hello的ELF格式及其虚拟地址空间,分析了重定位过程、加载以及运行时函数调用顺序以及动态链接过程,深入理解了链接和重定位的过程。

第6章 hello进程管理

6.1 进程的概念与作用

进程的经典定义就是一个执行中的程序的实例。

系统中的每个程序都运行在某个进程的上下文中。上下文是由程序正确运行所需的状态组成的。这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量、以及打开文件描述符的集合。

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

作用:shell是一种交互型的应用级程序。它能够接收用户命令,然后调用相应的应用程序,代表用户运行其他程序。

处理流程:shell执行一系列的读/求值步骤,然后终止。读步骤读取来自用户的一个命令行。求值步骤解析命令行,运行对应的程序。

6.3 Hello的fork进程创建过程

当在终端中输入./hello 学号 姓名 秒数的指令后,终端对输入的命令行进行解析,发现这不是一个内置命令,所以终端会调用fork函数创建一个新的运行的子进程。新创建的子进程几乎但不完全与父进程相同。子进程得到与父进程用户级虚拟地址空间相同的(但是独立的)一份副本,包括代码和数据段、堆、共享库以及用户栈。子进程还获得与父进程任何打开文件描述符相同的副本,这就意味着当父进程调用fork时。子进程可以读写父进程中打开的任何文件。父进程和新创建的子进程最大的区别在于他们有不同的pid。fork后调用一次返回两次,在父进程中fork会返回子进程的pid,在子进程中fork会返回0;父进程与子进程是并发运行的独立进程。

6.4 Hello的execve过程

创建进程后,在子进程中通过判断pid即fork函数的返回值,判断处于子进程,则会通过execve函数在当前进程的上下文中加载并运行一个新程序。execve加载并运行可执行目标文件,且带参数列表argv和环境变量列表envp。只有当出现错误时,execve才会返回到调用程序。

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

6.5 Hello的进程执行

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

一个进程执行它的控制流的一部分的每一时间段叫做时间片。因此,多任务也叫做时间分片。hello程序执行过程中同样存储时间分片,与操作系统的其他进行并发运行。

在执行过程中,内核可以决定抢占当前进程,并重新开始一个先前被抢占的进程,这个过程称为调度。在此基础上,hello程序与操作系统其他进程通过操作系统的调度,切换上下文,拥有各自的时间片从而实现并发运行。程序在涉及到一些操作时,例如调用一些系统函数,内核需要将当前状态从用户态切换到核心态,执行结束后再及时改用户态,从而保证系统的安全与稳定。

6.6 hello的异常与信号处理

hello执行过程中可能会出现的异常有:中断、陷阱、故障和终止,可能产生的信号有SIGINT、SIGQUIT、SIGKILL、SIGTERM、SIGALRM、SIGCHLD、SIGSTOP等。处理方式可能是将程序挂起等待下一个信号来临,或终止程序。

乱按:

图36 乱按

可以看到,乱按并不会影响程序的正常执行。

按回车:

图37 回车

按回车也不会影响程序的执行,但是按下的回车会在程序结束后输入命令行继续执行。

按Ctrl+Z:

图38 Ctrl+Z

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

图39 用ps查看进程并用fg继续执行

按Ctrl+C:

图40 Ctrl+C

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

图41 用ps查看进程

6.7本章小结

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

第7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址空间的格式为“段地址:偏移地址”,例如“23:8048000”,可以转换为物理地址:逻辑地址CS:EA = 物理地址CS × 16 + EA。保护模式下以段描述符作为下标,通过在GDT/LDT表获得段地址,段地址加偏移地址得到线性地址。

线性地址空间是指一个非负整数地址的有序集合,例如{0,1,2,3……}。在采用虚拟内存的系统中,CPU从一个有N = 2n个地址的地址空间中生成虚拟地址,这个地址空间称为虚拟地址空间。

而对应于物理内存中M个字节的地址空间{0, 1, 2, 3, …, M-1}则称为物理地址空间。

Intel处理器采用段页式存储管理,前者将逻辑地址转换为线性地址从而得到虚拟地址,后者将虚拟地址转换为物理地址。逻辑地址中的偏移地址需要经过段地址到线性地址的转换变为虚拟地址,然后通过MMU转换为物理地址,才能找到对应物理内存。

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

将程序按内容或过程函数关系分成段,例如程序段、数据段……以段为单位分配内存,通过地址映射机制,将段式虚拟地址转换成实际内存物理地址。段的长度由相应的逻辑信息组的长度决定,因而各段长度不等。逻辑地址得到段号、段内地址,再根据段号和段表求出基址,再由基址+段内地址即可得物理地址。

图42 段式管理

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

页式管理,是将虚拟地址空间划分为一个一个固定大小的块(称作虚页),同一时候也让实际地址空间也划分为一个一个相同大小的页(称作实页)。

图43 页式管理

上面是页式管理中由虚拟地址寻到实际主存地址的步骤。先找到页表基址寄存器,获得相应页表的基地址,然后用基址与虚页号做一次加法找到相应的实页号,由实页号和页内位移组成实际主存地址。

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

每次CPU产生一个虚拟地址,MMU就必须查阅相应的PTE,这显然造成了巨大的时间开销,为了消除这样的开销,MMU中存在一个关于PTE的小的缓存,称为翻译后备缓冲器(TLB)。TLB通过虚拟地址VPN部分进行索引,分为索引(TLBI)与标记(TLBT)两个部分。这样,MMU在读取PTE时会直接通过TLB,如果不命中再从内存中将PTE复制到TLB。

在以上机制的基础上,如果所使用的仅仅是虚拟地址空间中很小的一部分,那么仍然需要一个与使用较多空间相同的页表,造成了内存的浪费。所以虚拟地址到物理地址的转换过程中还存在多级页表的机制:上一级的页表映射到下一级也表,直到页表映射到虚拟内存,如果下一级内容都未分配,那么页表项则为空,不映射到下一级,也不存在下一级页表,当分配时再创建相应页表,从而节约内存空间。

图44 多级页表下VA到PA的变换

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

首先获取物理地址VA,使用物理地址的CI进行组索引,分别对每路与缓存标记CT进行标志位匹配。若匹配成功且块的标志位为1则命中。然后根据块偏移取出数据并返回。若未到相匹配的块或者标志位为0,则缓存不命中。一级cache向下逐级寻找查询数据。然后向上逐级写入cache。在更新cache的时候,需要判断是否有空闲块。如果有空闲块(即有效位为0)则写入;如果不存在则驱逐一个最近最少使用的块重新写入。

7.6 hello进程fork时的内存映射

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

当fork在新进程中返回时,新进程现在的虚拟内存刚好的和调用fork时的虚拟内存相同。而当这两个进程中任何一个进行写操作时,就会触发一个保护故障。当故障处理程序注意到保护异常是由于进程试图写私有的写时复制区域中的一个页面而引起的,它就会在物理内存中创建这个页面的一个新副本,更新页表条目指向这个新的副本,然后恢复这个页面的可写权限。当故障处理程序返回时,CPU重新执行这个写操作,现在在新创建的页面上这个写操作就可以正常执行了。

7.7 hello进程execve时的内存映射

execve函数在当前进程中加载并运行包含在可执行目标文件hello中的程序,用hello程序有效地替代了当前程序。加载并运行hello需要以下几个步骤:

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

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

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

4、设置程序计数器(PC)。execve做的最后一件事情就是设置当前进程上下 文中的程序计数器,使之指向代码区域的入口点。下一次调度这个进程时,它将从这个入口点开始执行。Linux将根据需要换入代码和数据页面。

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

缺页故障是指当指令引用一个虚拟地址,在MMU中查找页表时发现与该地址相对应的物理地址不在内存中就会发生故障。

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

7.9动态存储分配管理

动态存储分配管理由动态内存分配器完成。动态内存分配器维护着一个进程的虚拟内存区域,称为堆。堆是一个请求二进制零的区域,它紧接在未初始化的数据区后开始,并向上生长(向更高的地址)。分配器将堆视为一组不同大小的块的集合来维护。

每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可以用来分配,空闲块保持空闲,直到它显示地被应用程序所分配。

一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。动态内存分配器从堆中获得空间,将对应的块标记为已分配,回收时将堆标记为未分配。而分配和回收的过程中,往往涉及到分割、合并等操作。

动态内存分配器的目标是在对齐块的基础上,尽可能地提高吞吐率及空间占用率,即减少因为内存分配造成的碎片。其实现常见的数据结构有隐式空闲链表、显式空闲链表、分离空闲链表,常见的放置策略有首次适配、下一次适配和最佳适配。

7.10本章小结

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

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

8.2 简述Unix IO接口及其函数

8.3 printf的实现分析

8.4 getchar的实现分析

8.5本章小结

结论

hello一生的经历如下:

编写:由编辑器编写出hello.c

预处理:将hello.c调用的库合并到hello.i中

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

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

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

开始运行:在shell中输入命令行./hello 学号 姓名 秒数

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

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

执行指令:CPU为其分配时间片,在一个时间片中,hello享有CPU资源,执行指令

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

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

异常控制流:如果运行途中键入ctr-c或ctr-z或进行其他操作,则调用shell的信号处理函数进行相应处理

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

至此,hello结束了其坎坷的一生。

经过本课程的学习,大大提高了我对计算机的兴趣,我粗浅的认识到计算机是由软件和硬件共同组成的,他们共同协作运行程序,计算机内部表示最基础的单位是一堆bit,在不同的上下文中,一组组的bit发挥不同的功能,一组组被翻译过的bit存储在硬盘中,在系统运行程序的时候,经过翻译的文件就会被读取到内存中,然后被被读取到CPU的高速缓存中,寄存器直接从高速缓存中读取数据,进行处理指令,从而实现程序的运行。

一个程序的运行,说起来可以像上面那样简单,实际上在底层的实现是相当的复杂与精妙,我不禁感慨前人的各种天才构想,对计算机科学家们发出由衷的敬佩,感谢hello这短暂而又精彩的一生,感谢历史上一切为计算机的发展做出贡献的人们!

附件

hello.c:c语言源程序

hello.i:hello.c预处理生成的文本文件

hello.s:hello.i经过编译器翻译成的汇编语言文本文件

hello.o:hello.s经汇编器翻译成机器语言指令并打包成的二进制可重定位目标文件

hello:经过hello.o链接生成的二进制可执行目标文件

参考文献

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