计算机系统大作业-程序人生

本文详细介绍了从C程序hello.c的源码开始,经过预处理、编译、汇编、链接成为可执行文件hello的过程,并探讨了进程管理和内存管理,包括fork、execve、进程调度、异常处理、存储器地址空间的转换等,揭示了计算机系统的运行逻辑和管理机制。
摘要由CSDN通过智能技术生成

计算机系统

大作业

题     目  程序人生-Hello’s P2P 

专       业  网络空间安全           

学     号  2021111156             

班     级  2103901                 

学       生  刘之弘               

指 导 教 师  吴锐                   

计算机科学与技术学院

2023年5月

摘  要

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

关键词:计算机系统;关键词2;……;                           

    本文从hello的一生(P2P,020)入手,深入介绍了一个C程序从源码到编译为可执行程序再到作为进程完整执行的全流程,并以此为主线完整介绍了计算机系统的知识体系。

在第2、3、4、5章,我们对源代码hello.c依次进行预处理、编译、汇编,链接,深入介绍这些过程的目的、意义及细节,同时用代码实例进行分析。

在第6、7章,我们执行hello并探讨程序运行过程中的进程管理和内存管理,由此窥及整个计算机系统的运行逻辑。

在跟随hello程序走完它一生的过程中,我们也会体会到计算机科学的精妙与优雅:既有顶层设计的洞见构想,也有底层执行的精细设计。

目  录

第1章 概述................................................................................................................ - 4 -

1.1 Hello简介......................................................................................................... - 4 -

1.2 环境与工具........................................................................................................ - 4 -

1.3 中间结果............................................................................................................ - 4 -

1.4 本章小结............................................................................................................ - 4 -

第2章 预处理............................................................................................................ - 6 -

2.1 预处理的概念与作用........................................................................................ - 6 -

2.2在Ubuntu下预处理的命令............................................................................. - 6 -

2.3 Hello的预处理结果解析................................................................................. - 7 -

2.4 本章小结............................................................................................................ - 7 -

第3章 编译................................................................................................................ - 8 -

3.1 编译的概念与作用............................................................................................ - 8 -

3.2 在Ubuntu下编译的命令................................................................................ - 8 -

3.3 Hello的编译结果解析..................................................................................... - 9 -

3.4 本章小结.......................................................................................................... - 12 -

第4章 汇编.............................................................................................................. - 13 -

4.1 汇编的概念与作用.......................................................................................... - 13 -

4.2 在Ubuntu下汇编的命令.............................................................................. - 13 -

4.3 可重定位目标elf格式.................................................................................. - 14 -

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

4.5 本章小结.......................................................................................................... - 17 -

第5章 链接.............................................................................................................. - 18 -

5.1 链接的概念与作用.......................................................................................... - 18 -

5.2 在Ubuntu下链接的命令.............................................................................. - 18 -

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

5.4 hello的虚拟地址空间................................................................................... - 20 -

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

5.6 hello的执行流程........................................................................................... - 21 -

5.7 Hello的动态链接分析................................................................................... - 22 -

5.8 本章小结.......................................................................................................... - 22 -

第6章 hello进程管理....................................................................................... - 23 -

6.1 进程的概念与作用.......................................................................................... - 23 -

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

6.3 Hello的fork进程创建过程......................................................................... - 25 -

6.4 Hello的execve过程..................................................................................... - 25 -

6.5 Hello的进程执行........................................................................................... - 25 -

6.6 hello的异常与信号处理............................................................................... - 26 -

6.7本章小结.......................................................................................................... - 29 -

第7章 hello的存储管理................................................................................... - 30 -

7.1 hello的存储器地址空间............................................................................... - 30 -

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

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

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

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

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

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

7.8 缺页故障与缺页中断处理.............................................................................. - 33 -

7.9动态存储分配管理.......................................................................................... - 34 -

7.10本章小结........................................................................................................ - 34 -

第8章 hello的IO管理........................................................... 错误!未定义书签。

8.1 Linux的IO设备管理方法.................................................... 错误!未定义书签。

8.2 简述Unix IO接口及其函数................................................. 错误!未定义书签。

8.3 printf的实现分析................................................................. 错误!未定义书签。

8.4 getchar的实现分析............................................................. 错误!未定义书签。

8.5本章小结................................................................................. 错误!未定义书签。

结论............................................................................................................................ - 35 -

附件............................................................................................................................ - 36 -

参考文献.................................................................................................................... - 37 -

第1章 概述

1.1 Hello简介

P2P

hello的生命是从一个源程序(hello.c)开始的。这样的程序可以被直接编写出来,但不能直接执行。因此从Programme到Progress,首先需要经过预处理(Preprocessing)、编译(Compilation)、汇编(Assembly)、链接(Linking)四个阶段。链接后我们会得到可执行文件hello,用fork和exceve将其载入到内存并运行,才是真的变成了一个进程(Progress)。

020

作为进程的hello,在最开始一无所有。操作系统会为他分配页面,将它装入内存;随后CPU从入口开始执行它的代码,它也会接受外部的输入,输出信息;它会遇到各种异常,发生中断和上下文切换;最终退出变为僵尸进程,为系统回收,结束他的一生,不留下一片云彩。

1.2 环境与工具

Linux iZ2ze990f69tlt5qfzu7miZ 3.10.0-1160.83.1.el7.x86_64 #1 SMP Wed Jan 25 16:41:43 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7

gcc 版本 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)

1.3 中间结果

hello                      可执行文件

hello.c                   源文件

hello.i                    预处理后的生成文件

hello.s                   编译后得到的汇编文件

hello1.disasm        对hello.o的反汇编结果

hello.disasm          对hello的反汇编结果

hello.o                   可重定位文件

1.4 本章小结

       本章简单介绍了hello.c编译和执行的大致过程,由此粗略引入计算机系统的知识体系。

第2章 预处理

2.1 预处理的概念与作用

预处理的过程

  1. 预处理器(cpp)将所有的#define删除,并且展开所有的宏定义。
  2. 处理所有的条件预编译指令,比如#if、#ifdef、#elif、#else、#endif等。
  3. 处理#include预编译指令,将被包含的文件直接插入到预编译指令的位置。
  4. 删除所有的注释。
  5. 添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号。
  6. 保留所有的#pragma编译器指令,因为编译器需要使用它们。
  7. 使用gcc -E hello.c -o hello.i命令来进行预处理, 预处理得到的另一个程序通常是以.i作为文件扩展名。

预处理的意义

预处理一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。典型地,由预处理器(preprocessor)对程序源代码文本进行处理,得到的结果再由编译器核心进一步编译。

这样能够做方便程序员编写代码,提高代码的复用性和可维护性。

2.2在Ubuntu下预处理的命令

gcc -E hello.c -o hello.i

 

2.3 Hello的预处理结果解析

       预处理结果(部分):

 

2.4 本章小结

纵览整个hello.i,可以发现前面99%的内容都是被include的库文件,最后是原本的hello.c代码。hello.c较简单,代码部分没有发生变化。

第3章 编译

3.1 编译的概念与作用

编译的概念

编译器将原始程序(source program)作为输入,翻译产生使用目标语言(target language)的等价程序。在这里即是用上一个阶段预处理后的hello.i作为输入,将其翻译成汇编语言。在这个过程中还可能发现原程序的语法错误。

编译的作用:

  1. 词法分析:扫描器(Scanner)将源代的字符序列分割成一系列的记号(Token)。lex工具可实现词法扫描。
  2. 语法分析:语法分析器将记号(Token)产生语法树(Syntax Tree)。yacc工具可实现语法分析(yacc: Yet Another Compiler Compiler)。
  3. 语义分析:静态语义(在编译器可以确定的语义)、动态语义(只能在运行期才能确定的语义)。
  4. 源代码优化:源代码优化器(Source Code Optimizer),将整个语法书转化为中间代码(Intermediate Code)(中间代码是与目标机器和运行环境无关的)。中间代码使得编译器被分为前端和后端。编译器前端负责产生机器无关的中间代码;编译器后端将中间代码转化为目标机器代码。
  5. 目标代码生成:代码生成器(Code Generator).
  6. 目标代码优化:目标代码优化器(Target Code Optimizer)。

3.2 在Ubuntu下编译的命令

gcc -S hello.i -o hello.s

 

 

3.3 Hello的编译结果解析

 

 

 

3.3.1 常量

       包括printf的模板字符串和整数4。

       对于前者,编译器将起放在.rodata中:

      

 

       对于后者,直接作为立即数写在汇编代码中。

      

 

3.3.3 全局变量

       直接标明,如main函数

3.3.3 局部变量

       可能在寄存器里,也可能在栈中。这里以i为例,i被存放在了栈中。

       int i;

       for (i=0;i<8;i++)

      

      

 

3.3.5 赋值

       赋值运算在“局部变量”中出现过,不再赘述。

3.3.8 算术操作

       算术操作在“局部变量”中出现过,不再赘述。

3.3.10 关系操作

       关系操作在“局部变量”中出现过,不再赘述。

3.3.11 数组、指针

       数组或指针在hello中以argv和指向string常量的指针的形式出现。argv在“函数”中叙述,本节举例说明后一种。

  

这里movl $.LC0, %edi将前面提过的字符串常量了存入%edi中,具体来说是以指针的形式,指向标签.LC0。随后,%edi作为参数,被函数puts使用,输出到stdout中。

3.3.12 控制转移

       控制转移在前面多次出现过,例如“局部变量”中的for循环里,如果i<8,就继续跳转到循环体,如果该条件不成立就结束循环,继续执行后面的代码。

3.3.13 函数

      

main函数被调用时接收了两个参数,argc位于%edi中,argv位与%rsi中。可以看到编译器首先将它们存在了栈中,随后比较argc与立即数4,用比较结果进行条件跳转。这对应hello.c中“如果argc不为4就输出提示并退出程序”的逻辑。argv中的各个指针对应的参数后续也被放在寄存器中,传给printf使用。

 

3.4 本章小结

编译器将C语言代码翻译成了汇编语言,根据优化等级不同也伴随着不同程度的逻辑改动。本章对各种数据与操作的细节作了举例分析。

第4章 汇编

4.1 汇编的概念与作用

       机器语言可以被机器直接执行,但难以辨别和记忆,且可移植性不好。于是便产生了汇编语言,程序员通过汇编代码编写程序,通过汇编编译器,最终还是转化为机器语言,最终让计算机执行。

       汇编就是将汇编语言转换成机器语言的过程。

4.2 在Ubuntu下汇编的命令

       gcc -c hello.s -o hello.o

 

 

 

4.3 可重定位目标elf格式

ELF文件的基本格式

 

 

ELF头给出了一些基本信息,如版本、入口点地址、头大小等。

  

.symtab定义了程序中的符号表。符号表中存放了地址需要解析的符号信息,包括main函数、调用的puts和printf、exit等函数。

       .rela.text一节存放了.text中的重定位信息,在链接时这一节会起作用。偏移量指示了需要被重定位的符号相对于.text节起始位置的距离。类型有两种,PC相对(PC32)和绝对定位(32)。这些信息会被用于计算最终的指令操作数。。

4.4 Hello.o的结果解析

使用 objdump -d -r hello.o > hello.disasm 对hello.o进行反汇编

 

 

 

反汇编得到的代码与hello.s中的汇编代码基本一致,区别在反汇编代码中不再有跳转标签,而是使用地址跳转、寻址。目前(链接之前)代码中的跳转地址都是全0,但包含在.rel.text中的相对寻址信息也被一并显示在下方了。

4.5 本章小结

本章对汇编和反汇编的结果进行了一个概览,重点集中在汇编时的相对寻址部分。
第5章 链接

5.1 链接的概念与作用

汇编后得到的可重定位文件只包含hello.c本身的信息,想要获得可执行程序还需要能够调用其中include的库函数的信息。链接器解析多个原文件中的符号并进行重定位,最终得到一个拼接起来的可执行程序。

5.2 在Ubuntu下链接的命令

使用ld的链接命令

 

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

 

       与hello.o不同,hello的地址不再从0开始,而是从0x400000开始。

5.4 hello的虚拟地址空间

 

发现地址空间确实是从0x400000开始的。这样做是为了保证空指针可以触发访问缺失页的异常SIGSEGV,而不是访问了不该访问的资源从而导致了奇奇怪怪的问题。0x400000也就是4MB,是x86-64虚拟内存最大的页大小,也就是说内存从这里开始,能够保证刚好空出来一个页。

 

5.5 链接的重定位过程分析

objdump -d -r hello 对hello反汇编

(部分结果)

 

首先是比对hello.o的反汇编结果长了很多,因为链接进了库文件。

这次的反汇编结果中增加了.init和.fini段的代码,这两段代码主要是编译器增加的代码段。

.text段增加了_start入口和一些其他函数,_start入口在C语言运行时被动态库添加,是程序真正的入口点。

增加了.plt(过程链接表)段,这些段通过访问GOT来获得动态库中目标函数的地址。

5.6 hello的执行流程

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

_start函数:0x004010f0

main:    0x004005b0

printf:    0x00400540

atoi:       0x00400580

sleep:     0x004005a0

getchar: 0x00400560

5.7 Hello的动态链接分析

dl_init前

 

dl_init后

 

可以发现有部分内存空间发生变化,这是动态链接延迟绑定的过程中,GOT表中的地址逐渐被填充。GOT是数据段的一部分,而PLT是代码段的一部分。编译器在数据段的开头创建了全局偏移表(GOT),在加载时,动态链接器重新定位GOT中的每个条目,使其包含目标的正确绝对地址。被可执行程序调用的每个库函数都有自己的PTL条目,每个条目负责调用一个特定的函数。

5.8 本章小结

       本章分析了链接的过程,探讨了起始地址设置的逻辑、链接前后的变化、动态链接的过程。

6章 hello进程管理

6.1 进程的概念与作用

狭义定义:进程是正在运行的程序的实例

       广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

进程是由进程控制块、程序段、数据段三部分组成

从理论角度看,是对正在运行的程序过程的抽象;

从实现角度看,是一种数据结构,目的在于清晰地刻画动态系统的内在规律,有效管理和调度进入计算机系统主存储器运行的程序。

 

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

Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。

 

1.  将命令行分成由 元字符(meta character) 分隔的 记号(token)

元字符包括 SPACE, TAB, NEWLINE, ; , (, ), <, >, |, &
记号的类型包括单词,关键字,I/O重定向符和分号。

2. 检测每个命令的第一个记号,看是否为不带引号或反斜线的关键字。如果是一个 开放的关键字,如if和其他控制结构起始字符串,function{(,则命令实际上为一复合命令。shell在内部对复合命令进行处理,读取下一个命 令,并重复这一过程。如果关键字不是复合命令起始字符串,而是如then等一个控制结构中间出现的关键字,则给出语法错误信号。

3. 依据别名列表检查每个命令的第一个关键字。如果找到相应匹配,则替换其别名定义,并退回第一步;否则进入第4步。

4. 执行大括号扩展,例如a{b,c}变成ab ac

5. 如果~位于单词开头,用$HOME替换~。使用usr的主目录替换~user

6. 对任何以符号$开头的表达式执行参数(变量)替换

7. 对形如$(string)或者`string` 的表达式进行命令替换
这里是嵌套的命令行处理。

8. 计算形式为$((string))的算术表达式

9. 把行的参数替换,命令替换和算术替换 的结果部分再次分成单词,这次它使用$IFS中的字符做分割符而不是步骤1的元字符集。

10. 对出现*, ?, [ ]对执行路径名扩展,也称为通配符扩展

11.  按命令优先级表(跳过别名),进行命令查寻。先作为一个特殊的内建命令,接着是作为函数,然后作为一般的内建命令,最后作为查找$PATH找到的第一个文件。

12. 设置完I/O重定向和其他操作后执行该命令。

6.3 Hello的fork进程创建过程

当fork函数被当前hello进程调用时,内核为新hello进程创建各种数据结构,并分配该进程一个唯一的pid。为子进程分配虚拟内存时,会赋予其一个和父进程共享的虚拟内存。其属性被标记为只读和私有的写时复制。

6.4 Hello的execve过程

  1. 删除已经存在的用户区域。删除当前进程虚拟地址的用户部分中的内容。
  2. 映射私有区域,要为hello的代码,数据,bss和栈区创建新的结构。这些结构都是私有/写时复制的。其中,代码和数据来自a.out文件的.text和.data区域。Bss,栈,堆则请求二进制零区域。
  3. 映射共享区域。在这个例子中,hello中需要创建一个共享到标准C库的共享区域。当hello调用到标准C库中的对象时。延时动态链接器需要进行到libc.so的动态链接。
  4. 设置程序计数器,一切都载入完毕以后,PC需要指向_start入口。

6.5 Hello的进程执行

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

上下文:

       上下文就是内核重新启动一个被抢占的进程所需的状态。它由一些对象的值组成,包括通用的目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构(进程表和文件表)。

时间片:

       时间片由操作系统内核的调度程序分配给每个进程。首先,内核会给每个进程分配相等的初始时间片,然后每个进程轮番地执行相应的时间,当所有进程都处于时间片耗尽的状态时,内核会重新为每个进程计算并分配时间片,如此往复。

进程调度:

就是先把前一个任务的 CPU 上下文(也就是 CPU 寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。

而这些保存下来的上下文,会存储在系统内核中,并在任务重新调度执行时再次加载进来。这样就能保证任务原来的状态不受影响,让任务看起来还是连续运行。

从进程A切换到进程B时,进程A初始运行在用户态中,直到它因为某些原因陷入内核。内核此时执行进程A到进程B的上下文切换,在切换的第一部分中,内核代表进程A在内核态下执行指令,然后在某一时刻,它代表进程B在用户模式下执行指令。

6.6 hello的异常与信号处理

异常有4类:

中断:是来自外部I/O设备的信号,如鼠标键盘等。

故障:由错误情况引起,如缺页异常,可能被修复,也可能无法恢复并终止进程。

终止:不可恢复的错误,如内存数据损坏,会强行终止进程。

陷阱:是有意的,通过指令产生的异常,通常以系统调用的方式出现。

Ctrl-Z(发送SIGSTP)       >     ps(查看进程) >     jobs(查看任务)

 

pstree

 

fg   (发送SIGCONT,继续执行了后面的内容)

 

后台运行+kill(发送了SIGKILL信号,强制杀死进程)

 

Ctrl+C(发送SIGINT信号,终止进程)

 

6.7本章小结

本章分析了进程执行和切换的过程,并用命令行实例实践了通过信号对进程进行控制。

7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:

在有地址变换功能的计算机中,访问指令给出的地址 (操作数) 叫逻辑地址,也叫相对地址。要经过寻址方式的计算或变换才得到内存储器中的物理地址。逻辑地址由两个16位的地址分量构成,一个为段基值,另一个为偏移量。两个分量均为无符号数编码。

线性地址:

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

虚拟地址:

在保护模式下,程序在虚拟内存中运行。虚拟内存被组织成一个存储在磁盘上的N个连续的字节大小的单元阵列。每个字节都有一个唯一的虚拟地址,作为数组的索引。虚拟地址由VPO(虚拟页偏移)、VPN(虚拟页号)、TLBI(TLB索引)和TLBT(TLB标签)组成。

物理地址:

物理内存的真实的地址,即可以通过地址线进行正确访问的地址。

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

段式管理分为实模式和和保护模式:

实模式:逻辑地址满足CS:EA=EA+16*CS;

保护模式:辑地址包括一个段标识符和一个段偏移。使用段标识符作为下标,进入索引的段描述符表,如果T1=0,则索引全局段描述符表(GDT),如果T1=1,则索引局部段描述符表(LDT)。段描述符表中的段地址(基数字段)被添加到段偏移量中,这就是线性地址。

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

一个虚拟地址由一个虚拟页号(VPN)和一个虚拟页偏移量(VPO)组成,页表由有效位和一个物理页号组成。VPN作为页表的索引,用于在页表中找到相应的PTE,PTE指示了物理地址(PA)。

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

多级页表仍然是通过VPN寻找PTE,区别在于前三级页表指向下一级页表的物理基地址,第四级页表指向物理地址。

 

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

使用物理地址依次读取一级、二级、三级Cache,如果上一级命中就直接返回结果,如果没有命中继续向下读。如果三级Cache都没有命中,就需要读取主存,同样是使用物理地址。

 

 

7.6 hello进程fork时的内存映射

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

当 fork 在新进程中返回时,新进程现在的虚拟内存刚好和调用 fork 时存在的虚拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面,因此,也就为每个进程保持了私有地址空间的概念。

 

7.7 hello进程execve时的内存映射

加载并运行 hello 需要以下几个步骤:

      1. 删除已存在的用户区域(结构)。
      2. 映射私有区域。为新程序的代码、数据、bss 和栈区域创建新的区域结构,都是私有、写时复制的。bss 区域是请求二进制零的,映射到匿名文件,其大小包含在 hello 中的。栈和堆区域也是请求二进制零的,初始长度为零。
      3. 映射共享区域。共享库中的对象需要动态链接到这个程序,然后再映射到用户虚拟地址空间中的共享区域内。
      4. 设置程序计数器。执行的最后一件事情就是设置当前进程上下文中的程序计数器,使之指向代码区域的入口点。

Linux 进程可以使用 mmap 函数来创建新的虚拟内存区域,并将对象映射到这些区域中。

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

内核的缺页处理程序:

  1. 虚拟地址 A 合法吗?(A 在某个区域结构定义的区域内吗?)

缺页处理程序需要搜索区域结构的链表,把 A 和每个区域结构中的 vm_start 和 vm_end 做比较,如果不合法就触发一个段错误,从而终止这个进程(如图中的「1」)。

因为一个进程可以创建任意数量的新虚拟内存区域(使用之后描述的 mmap 函数),所以顺序搜索花销很大。因此在实际中,Linux 使用某些这里没有显示出来的字段,在链表中构建了一棵树,并在这棵树上进行查找。

  1. 试图进行的内存访问合法吗?(进程是有读写这个区域内页面的权限?)

如果试图进行的访问是不合法的,那么缺页处理程序会触发一个保护异常,从而终止这个进程,如图中的「2」。

  1. 此时,内核知道了这个缺页是由于对合法的虚拟地址进行合法的操作造成,它将选择一个牺牲页面,如果其被修改过,那么就将它交换出去,换入新的页面并更新页表。当缺页处理程序返回时,CPU 重新启动引起缺页的指令,这条指令将再次发送 A 到 MMU。

7.9动态存储分配管理

动态内存分配器维护着一个进程的虚拟内存区域,称为堆(heap)。系统之间细节不同,但不失通用性。假设堆是一个请求二进制零的区域,它紧接在未初始化的数据区域后开始,并向上生长(向更高地址)。对于每个进程,内核维护者一个变量 brk(读「break」),它指向堆的顶部。

分配器将堆视为一组不同大小的块(block)的集合来维护。每个块就是一个连续的虚拟内存片(chunk),要么是已分配的,要么是空闲的。内存释放要么是应用程序显式执行,要么是内存分配器自身隐式执行。

分配器有两种基本风格:

  1. 显式分配器(explicit allocator):C 程序通过调用 malloc 函数来分配一个块,并通过调用 free 函数来释放一个块(类似于 C++ 的 new 和 delete 关键字)。
  2. 隐式分配器(implicit allocator):也叫做垃圾收集器(garbage collection),诸如 Lisp、ML 以及 Java 之类的高级语言就依赖垃圾收集来释放已分配的块。

分配器的两个性能目标:最大化吞吐率和最大化内存利用率。

7.10本章小结

本章对进程执行过程中的内存访问与使用过程进行了一个全方位的梳理。Cache解决CPU与主存的速度差异问题,虚拟内存解决主存与磁盘的存储容量问题,与此同时操作系统对于内存的分配和使用也有许多细节,以上这些共同造就了现代计算机优秀的运行速度。

结论

hello虽小,五脏俱全;它所经历的一生,也是所有程序/进程会经历的一生。管中窥豹,见微知著,相信对hello的学习能够为我们其他编程实践提供帮助,甚至对改变更大世界的伟大工作具有价值。

  1. 预处理,做简单的文本处理,编译控制,宏,头文件的包含。
  2. 编译,通过编译器,将源代码转化成等价的汇编语言描述。
  3. 汇编,将汇编语言翻译成机器码(实际上是目标文件)。
  4. 链接,处理外部符号的引用,将多个可重定位目标文件连接成一个可执行目标文件。
  5. 加载,在shell中键入命令,shell通过fork创建子进程,子进程通过execve把hello加载进内存。
  6. 运行,hello接受内核的调度,在自己的时间片上运行。
  7. 结束,被系统终止,清理资源,由父进程回收。

附件

hello                      可执行文件

hello.c                   源文件

hello.i                    预处理后的生成文件

hello.s                   编译后得到的汇编文件

hello1.disasm        对hello.o的反汇编结果

hello.disasm          对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.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值