摘 要
本文从计算机系统的角度介绍了hello的一生。
hello的一生经历了许多成长。从hello.c文件,到预处理生成hello.i文件、编译生成hello.s汇编文件、汇编生成hello.o可重定位目标文件、链接生成hello可执行文件。在shell中,启动之后,shell为可执行文件hello创建一个子进程fork。于是hello就从program变成了progress。之后,shell通过execve函数加载hello进入内存,映射虚拟内存。进入程序之后由虚拟内存转换成物理内存。之后进入main函数执行代码,CPU在执行hello时,可能会发出系统中断等信号,将控制权暂时交给其他程序。hello程序执行中,会调用函数向屏幕输出。当程序结束后,shell父进程负责回收子进程,内核删除相关数据。至此hello程序过完了完整的一生。
关键词:计算机系统;预处理;汇编;链接;进程管理;存储;内存
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
目 录
第1章 概述
1.1 Hello简介
P2P: From Program to Process 程序的周期从c语言开始,到最后被机器执行可以分为四个过程:预处理,编译,汇编,链接。最终成为可执行目标程序hello。在shell中键入启动命令后,shell为其fork产生子进程的过程。
020: 这时这个fork的进程的上下文已经变成hello的信息。CPU会一步一步执行此程序,按照取指、译码、执行、访存、写回、更新PC等微操作,逐步执行目标文件中的程序。同时,CPU使用流水线、进程切换等工作方式实现多进程作业。OS的进程管理调用fork函数产生子进程(PROCESS),调用了execve函数,并进行虚拟内存映射(MMP),并为运行的hello分配时间片以执行取指译码流水线等操作;OS的储存管理以及MMU解决虚拟地址到物理地址的转换,cache、TLB、页表等加速访问过程,IO管理与信号处理综合软硬件对信号等进行处理;当程序运行结束后,shell父进程负责回收hello进程,内核释放内存并且删除相关数据结构,hello结束了完整的一生。
1.2 环境与工具
硬件环境:CPU为AMD Ryzen 7 5800H;
软件环境Windows11 64位; linux 4.15.0-142-generic #146~16.04.1-Ubuntu SMP Tue Apr 13 09:27:15 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
工具:gcc,gdb,edb,Winhex,vim,ld,readelf,objdump,ldd等
1.3 中间结果
- Hello.i Hello.c预处理后的文本文件
- Hello.s 编译后得到的文件
- Hello.o 汇编后得到的可重定位目标文件
- Hello 链接之后的可执行目标文件
- Hello.out Hello反汇编之后的可重定位文件
- Elf.txt Hello.o的elf格式文件
7、Hello.txt Hello的反汇编文件
1.4 本章小结
简要介绍了hello程序在系统中的生命流程,列出了本次大作业涉及的软硬件环境、开发调试工具以及中间文件。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
概念:预处理是在编译之前进行的处理,一般指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程,预处理中会展开以#起始的行,试图解释为预处理指令(PREPROCESSING DIRECTIVE)。
作用:在集成开发环境中,编译,链接是同时完成的。其实,C语言编译器在对源代码编译之前,还需要进一步的处理:预编译。
所以,完整的步骤是:预编译->编译->链接预编译的主要作用如下:
1.将源文件中以”include”格式包含的文件复制到编译的源文件中。2.用实际值替换用“#define”定义的字符串。3.根据“#if”后面的条件决定需要编译的代码。
预处理是C语言的一个重要功能,由预处理程序完成。当对一个源文件进行编译时,系统将自动调用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。
预处理的行为是由指令控制的。这些指令是由#字符开头的一些命令。
#define指令定义了一个宏---用来代表其他东西的一个命令,通常是某一个类型的常量。预处理会通过将宏的名字和它的定义存储在一起来响应#define指令。当这个宏在后面的程序中使用到时,预处理器”扩展”了宏,将宏替换为它所定义的值。
指示预处理器打开一个名字为stdio.h的文件,并将它的内容加到当前的程序中。
预处理器的输入是一个C语言程序,程序可能包含指令。预处理器会执行这些指令,并在处理过程中删除这些指令。预处理器的输出是另外一个程序:原程序的一个编辑后的版本,不再包含指令。预处理器的输出被直接交给编译器,编译器检查程序是否有错误,并经程序翻译为目标代码。
2.2在Ubuntu下预处理的命令
在Ubuntu下用gcc预处理hello.c程序的命令是:gcc hello.c -E -o hello.i其中-E选项的作用是使编译过程在预处理后停止。
2.3 Hello的预处理结果解析
可以看到,预处理后的文件是一个可阅读的文本文件,相比于源c程序,预处理后的文件多了很多内容,这些内容就是我们上面所提到过的库文件和宏展开等。也可以观察到预处理只是基于文本的改动,并没有再进行深入的处理。
找到main函数,通过比较发现两边函数几乎一样,唯一的区别是.i文件中将所有#include的内容和注释部分的内容删除掉了,这与前面分析预处理的作用相一致。继续寻找发现.i文件将include内容进行了引用,如stdio.h。
2.4 本章小结
本章介绍了预处理的定义与作用。并且实践了hello.i的生成,亲身体会了#include语句是如何将系统头文件插入程序文本中的。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
编译的概念:
利用编译程序从源程序(hello.i)产生目标程序(hello.s)的过程,这个过程中hello从以高级语言编写的文本文件变成了用汇编语言或机器语言表示的文件。是一个由面向人的高级语言转换成面向机器的低级语言的过程。
编译的作用:
通过语法分析和词法分析,在确认所有的指令都符合语法规则后,将预处理程序翻译成等价的中间代码表示或汇编代码
3.2 在Ubuntu下编译的命令
Ubuntu下的编译命令: gcc -S hello.i -o hello.s
3.3 Hello的编译结果解析
Hello编译结果如下:
3.1.1 数据
立即数4、0、8等,会直接体现在代码中,如argc!=4这句语句中的4,直接体现在编译后文件中24行cmpl $4, -20(%rbp)中。
字符串会被存储进只读数据段.LC0中。
. globl main 定义为全局变量
.type main, @function 将其设置为函数
Main函数中有一个局部循环变量i,我们可以在.L2中找到movl $0, -4(%rbp),这就是i=0语句,可以看到它被存放在堆栈-4(%rbp)中。
3.1.2 赋值
汇编程序中赋值一般通过mov指令实现,指令的后缀取决于操作数据的字节大小,movb:一个字节;movw:两个字节;movl:四个字节;movq:八个字节。
源代码中有i= 0赋值操作,体现在movl $0, -4(%rbp)。
3.1.3 算数操作
程序中出现的算术操作为:
leal 4(%esp), %ecx对应i++操作
addl $1, -12(%ebp)取地址运算
3.1.4 关系操作
通过cmpl指令比较立即数和变量,并通过设置标志位记录比较结果。
3.1.5 控制转移
S文件中有关跳转的指令均为控制转移,如:条件跳转、无条件跳转、循环中的条件跳转。
3.1.6 函数操作
参数传递:64位前6个参数在寄存器中,按顺序为%rdi ,%rsi,%rdx,%rcx,%r8,%r9。如main函数,%edi中为argc的值,%esi中为argv数组的头地址
函数调用:利用call指令,如:Call puts Call exit Call sleep把调用指令的下一条指令的地址压入栈中
参数返回:利用ret指令
3.4 本章小结
本章主要介绍了编译器是如何处理 C 语言的各个数据类型以及各类操作。展示了编译后文件内容和编译时源代码与汇编语言的对应规则和编译行为。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
汇编的概念:汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件hello.o中。
汇编的作用:将汇编语言翻译成机器语言。
4.2 在Ubuntu下汇编的命令
Ubuntu下用gcc汇编的命令:gcc -c -m64 -no-pie -fno-PIC hello.s -o hello.o
4.3 可重定位目标elf格式
ELF文件由4部分组成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。实际上,一个文件中不一定包含全部内容,而且它们的位置也未必如同所示这样安排,只有ELF头的位置是固定的,其余各部分的位置、大小等信息由ELF头中的各项值来决定。
使用readelf -a命令读取hello.o的ELF格式。
4.3.1 ELF头
以 16B 的序列 Magic 开始,Magic 描述了生成该文件的系统的字的大小和字节顺序,ELF 头剩下的部分包含帮助链接器语法分析和解释目标文件的信息,其中包括 ELF 头的大小、目标文件的类型、机器类型、字节头部表(section header table)的文件偏移,以及节头部表中条目的大小和数量等信息。
4.3.2 节头部表
节头部表,包含了文件中出现的各个节的语义,包括节的类型、位置和大小等信息。
4.4.3 重定位节
.rel.text 代码的重定位条目,告诉链接器在将目标文件合并成可执行文件时如何修改这个应用。可以看到重定位节包含以下信息:偏移量、信息、类型、符号值、符号名称和加数。Hello.s中,重定位节.rela.text一共描述了8个重定位条目,重定位节.rela/eh_frame描述了1个重定位条目。
4.4.4 符号表
符号表中存放定义和引用全局变量的信息。Value项为地址偏移量,size项为目标大小,type项为目标数据类型,bind项表示该符号为局部符号还是全局符号,name项为符号名。
4.4 Hello.o的结果解析
objdump -d -r hello.o反汇编hello.o的结果为:
与第3章的 hello.s进行对照后发现,汇编程序较反汇编程序多了只读代码段等预处理内容,而反汇编程序相比于汇编程序多了机器码,且二者具体汇编语言有细微不同,但实际上这些不同都只是表现形式不同,是无关紧要的,实际二者代码逻辑上是一样的。例如:
分支跳转:(左为main.s,右为反汇编)je 29 <main+0x29>反汇编不使用段名称这样的助记符,直接使用地址。
函数调用:(左为main.s,右为反汇编)callq 29 <main+0x29>在.s 文件中,函数调用之后直接跟着函数名称,在反汇编程中,call 的目标地址是当前下一条指令。
4.5 本章小结
在这一章中,我们了解并介绍了汇编的概念和作用;熟悉了在Ubuntu下反汇编的指令;了解了elf文件各个部分的作用如ELF头、节头部表、符号表和可重定位节等;对比了hello.o和hello.s之间的差距,分析了汇编语言和机器语言的对应关系
(第4章1分)
第5章 链接
5.1 链接的概念与作用
链接的概念:链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。在现代系统中,链接是由叫做链接器的程序自动执行的。
链接的作用:将原本分散的数据片段链接起来变成一个完整的可执行文件。
5.2 在Ubuntu下链接的命令
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.ohello.o/usr/lib/x86_64-linux-gnu/libc.so /usr/lib/x86_64-linux-gnu/crtn.o进行链接
5.3 可执行目标文件hello的格式
1、命令:readelf -a hello > Elf.txt
- ELF头
ELF头以16字节序列magic数开始,用于确认文件类型。ELF头剩下的部分包括ELF头的大小、目标文件的类型、机器类型、节头部表的文件偏移,以及节头部表中条目的大小和数量。
- 节头
节头包括各个节的类型、位置、偏移量和大小等信息,与 hello_o.elf相比,其在链接之后的内容更加丰富详细。
- 程序头
在hello程序头中,描述了12个不同的程序段,并且给出了段的偏移地址、虚拟内存地址和物理内存地址。
- 段节
- 动态链接部分
- 重定位节
8、符号表
5.4 hello的虚拟地址空间
用edb打开hello后,可以在datadump中看到虚拟地址是从401000开始的。(如下图)
在之前的EFL头文件中,我们可以看到起始地址为401000,标志着程序的开始,由此我们可以知道是吻合的。
检查程序头表中所有的节,可以发现均是符合的。
5.5 链接的重定位过程分析
命令:objdump -d -r hello > Hello.txt
下图只展示hello.txt的部分:
得到的hello.txt与之前的hello.o相比有着不少区别。原本在hello.o中因为没有进行链接,相对地址都是0.而在hello.out中则是确定的虚拟地址。在hello.o中call后面跟的是地址偏移量,比如<main + 0x2f>,而hello.txt中是直接寻址方式,直接给出虚拟地址。此外,hello.txt中多了一些hello.o没有的东西,比如<_init>,<.plt>。
5.6 hello的执行流程
0x401000 _init
0x401030 puts@plt
0x401040 printf@plt
0x401050 getchar@plt
0x401060 atoi@plt
0x401070 exit@plt
0x401080 sleep@plt
0x401090 _start
0x4010c0 _dl_relocate_static_pie
0x401177 main
0x401210 _libc_csu_init
0x401280 _libc_csu_fini
0x401288 _fini
5.7 Hello的动态链接分析
在程序中动态链接是通过延迟绑定来实现的,延迟绑定的实现依赖全局偏移量表GOT和过程连接表PLT实现。GOT是数据段的一部分,PLT是代码段的一部分。
Init之前:
Init之后:
可以看到,未调用dl_init前,该地址处的内容都是0.而调用之后,动态链接器会重定位GOT中的每个条目,使其出现变化。
5.8 本章小结
本章了解了链接的概念及作用,分析可执行文件hello的ELF格式及其虚拟地址空间,同时通过实例分析了重定位过程、加载以及运行时函数调用顺序以及动态链接过程,深入理解链接和重定位的过程。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
概念:进程是一个执行中的程序的实例。
作用:在现代系统上运行一个程序时,我们会得到一个假象,好像我们的程序是系统中唯一运行的程序一样。我们的程序好像独占处理器和内存。处理器好像无间断地一条接一条执行我们程序中的指令,我们程序中的代码和数据好像是系统内存中唯一的对象。这些假象是通过进程的概念提供的。进程提供给应用程序的关键抽象:1)一个独立的逻辑控制流,提供一个程序独占处理器的假象;2)一个私有的地址空间,提供一个程序独占地使用内存系统的假象。
6.2 简述壳Shell-bash的作用与处理流程
Shell是用户级的应用程序,代表用户控制操作系统中的任务。处理流程如下:1、在shell命令行中输入命令:./hello
2、shell命令行解释器构造argv和envp;
3、调用fork()函数创建子进程,其地址空间与shell父进程完全相同,包括只读代码段、读写数据段、堆及用户栈等
4、调用execve()函数在当前进程(新创建的子进程)的上下文中加载并运行hello程序。将hello中的.text节、.data节、.bss节等内容加载到当前进程的虚拟地址空间
5、调用hello程序的main()函数,hello程序开始在一个进程的上下文中运行。
6.3 Hello的fork进程创建过程
在shell接受到./hello这个命令行后,它会对其进行解析,发现是加载并运行一个可执行文件的命令,于是它会先创建一个对应./hello的作业,再用fork()创建一个子进程,这个子进程与父进程几乎与父进程完全相同,它们有着相同的代码段、数据段、堆、共享库以及栈段,但它们的pid与fork的返回值是不同的,因此可以进行区分。然后,父进程(即shell主进程)会将新创建的子进程用setpgid()放在一个新的进程组中,这样这个进程组就对应./hello这个作业,shell可以通过向进程组中的所有进程发信号的方式管理作业。
6.4 Hello的execve过程
execve函数在当前进程的上下文中加载并运行可执行目标文件hello,且带参数列表argv和环境变量列表envp。只有当出现错误时,例如找不到hello文件,execve才会返回到调用程序。所以与fork一次调用返回两次不同,execve调用一次并从不返回。
6.5 Hello的进程执行
结合进程上下文信息、进程时间片,阐述进程调度的过程,用户态与核心态转换等等。进程调度:即使在系统中通常有许多其他程序在运行,进程也可以向每个程序提供一种假象,好像它在独占地使用处理器。如果想用调试器单步执行程序,我们会看到一系列的程序计数器(PC)的值,这些值唯一的对应于包含在运行时动态链接到程序的共享对象中的指令。这个PC的序列叫做逻辑控制流,或者简称逻辑流。进程是轮流适用处理器的,每个进程执行它的流的一部分,然后被抢占,然后轮到其他进程。
在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占了的进程,这种决策就叫做调度,是由内核中称为调度器的代码处理的。当内核选择一个新的进程运行,我们说内核调度了这个进程。在内核调度了一个新的进程运行了之后,它就抢占了当前进程,并使用上下文切换机制来将控制转移到新的进程。
内核模式转变到用户模式:操作系统内核使用上下文切换来实现多任务。内核为每个进程维持一个上下文,它是内核重启被抢占的进程所需的状态,包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构的值。
进程执行到某些时刻,内核可决定抢占该进程,并重新开启一个先前被抢占了的进程,这种决策称为调度。内核调度一个新的进程运行后,通过上下文切换机制来转移控制到新的进程:
- 保存当前进程上下文;
- 恢复某个先前被抢占的进程被保存的上下文
3)将控制转移给这个新恢复的进程。当内核代表用户执行系统调用时,可能会发生上下文切换,这时就存在着用户态与内核态的转换。
6.6 hello的异常与信号处理
hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。
异常可以分为四种:中断、陷阱、故障、终止。
1、正常程序的运行:
- Ctrl+c
Ctrl+c是来自键盘的中断,默认行为是终止进程,因此程序直接退出并被回收。
- Ctrl-Z
停止进程,发送信号SIGSTP,父进程接收到信号并进行了处理。
使用ps指令发现进程并没有终止。
输入fg后又会继续进行被挂起的进程。
输入jobs可以观察到hello程序当前的状态。
输入pstree可以查看当前进程树,这里只展示了部分的进程树。
使用kill发送信号9(SIGKILL)给进程3035,将进程杀死。
- 回车
运行时回车对程序运行没有影响。
- 不停乱按
不停乱按对程序的执行没有影响。
6.7本章小结
在这一章中,我们了解了进程的概念以及作用,并且熟悉了shell-bash的作用和处理流程,知道了fork函数如何创建进程,execve函数如何执行函数,了解了多种信号的处理以及异常中断。
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
1、逻辑地址:逻辑地址指由程序产生的与段相关的偏移地址部分,也叫相对地址。要经过寻址方式的计算或变换才得到内存储器中的实际有效地址,即物理地址。从hello的反汇编代码中看到的地址,它们需要通过计算,通过加上对应段的基地址才能得到真正的地址,这些便是hello中的逻辑地址。
2、线性地址:是逻辑地址到物理地址变换之间的中间层。程序hello的代码会产生逻辑地址,hello的反汇编文件中看到的地址(即逻辑地址)中的偏移量,加上对应段的基地址,便得到了hello中内容对应的线性地址。
3、CPU启动保护模式后,程序hello运行在虚拟地址空间中。注意,并不是所有的“程序”都是运行在虚拟地址中。CPU在启动的时候是运行在实模式的,Bootloader以及内核在初始化页表之前并不使用虚拟地址,而是直接使用物理地址的。
4、放在寻址总线上的地址。放在寻址总线上,如果是读,电路根据这个地址每位的值就将相应地址的物理内存中的数据放到数据总线中传输。如果是写,电路根据这个地址每位的值就在相应地址的物理内存中放入数据总线上的内容。物理内存是以字节(8位)为单位编址的。
7.2 Intel逻辑地址到线性地址的变换-段式管理
逻辑地址由段选择符和偏移量组成,线性地址为段首地址与逻辑地址中的偏移量组成。其中,段首地址存放在段描述符中。而段描述符存放在描述符表中,也就是GDT(全局描述符表)或LDT(局部描述符表)中。具体的管理过程如下: 1、先检查段选择符中的TI字段,以决定段描述符保存在哪一个描述符表中;
2、由于一个段描述符是8字节长,因此她在GDT或LDT内的相对地址是由段选择符的最高13位的值乘以8得到;
3、Base + offset,得到要转换的线性地址。
7.3 Hello的线性地址到物理地址的变换-页式管理
真正的物理地址需要经过线性地址一系列的映射才会到真正的物理地址处。页式管理是一种内存空间存储管理的技术,页式管理分为静态页式管理和动态页式管理。将各进程的虚拟空间划分成若干个长度相等的页(page),页式管理把内存空间按页的大小划分成片或者页面(page frame),然后把页式虚拟地址与内存地址建立一一对应页表,并用相应的硬件地址变换机构,来解决离散地址变换问题。页式管理采用请求调页或预调页技术实现了内外存存储器的统一管理。
CPU对于一个线性地址会取它的高若干位,通过它们去存储在内存中的页表里查询对应的页表条目,得到这个线性地址对应的物理页起始地址,然后与线性地址的低位(页中的偏移)相加就是物理地址。
7.4 TLB与四级页表支持下的VA到PA的变换
先将VA中的VPN分成三段,根据TLBT和TLBI,在TLB中寻找对应的PPN,如果没有找到,即为缺页,就需要到页表中去找。缺页处理程序确定物理内存中的牺牲页 (若页面被修改,则换出到磁盘——写回策略)。缺页处理程序调入新的页面,并更新内存中的PTE。缺页处理程序返回到原来进程,再次执行。
接着将VPN分成更多段(4段),CR3是对应的L1PT的物理地址,然后一步步递进往下寻址,越往下一层每个条目对应的区域越小,寻址越细致,在经过4层寻址之后找到相应的PPN然后和VPO拼接起来得到PA。
7.5 三级Cache支持下的物理内存访问
引入 Cache 的理论基础是程序局部性原理,包括时间局部性和空间局部性。时间局部性原理即最近被CPU访问的数据,短期内CPU 还要访问(时间);空间局部性即被CPU访问的数据附近的数据,CPU短期内还要访问(空间)。因此如果将刚刚访问过的数据缓存在一个速度比主存快得多的存储中,那下次访问时,可以直接从这个存储中取,其速度可以得到数量级的提高。
以 L1 cache为例,三级cache的访问流程基本都是一致。
1. CPU向MMU请求一个虚拟地址VA。
2. MMU从VA中抽取出VPN,结合页表基址计算出相应页表条目地址PTEA,发送给L1 cache
3.若PTEA命中,L1 cache返回相应页表条目PTE给MMU
4. MMU从PTE中解析出物理地址PA发送给 L1 cache
5. L1 cache 根据地址PA返回相应数据
7.6 hello进程fork时的内存映射
当fork函数被当前进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的PID。为了给这个新进程创建虚拟内存,它创建了当前进程的mm_struct、区域结构和页表的原样副本。
当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面,因此,也就为每个进程保持了私有地址空间的抽象概念
7.7 hello进程execve时的内存映射
execve加载后,新的hello程序取代原先的程序,同时删除当前虚拟地址中的区域结构。
然后映射私有区域,为新程序的代码、数据、堆栈创建新的区域结构。
再映射共享区域,与动态链接到这个程序的共享程序链接,映射到虚拟地址的共享区域中。
最后设置程序计数器PC。execve所做的最后一件事是将当前进程上下文中的程序计数器设置为指向代码区域的入口点。
7.8 缺页故障与缺页中断处理
缺页故障:引用一个虚拟地址,在MMU中查找页表时发现该对应的物理地址不在物理内存中,从而引发的故障。
处理过程:
1) 处理器将虚拟地址发送给 MMU
2-3) MMU 使用内存中的页表生成PTE地址
4) 有效位为零, 因此 MMU 触发缺页异常
5) 缺页处理程序确定物理内存中牺牲页 (若页面被修改,则换出到磁盘)
6) 缺页处理程序调入新的页面,并更新内存中的PTE
7) 缺页处理程序返回到原来进程,再次执行缺页的指令
7.9动态存储分配管理(没学,选做)
7.10本章小结
本章主要介绍了hello的存储器的地址空间、段式管理、页式管理、虚拟地址翻译的过程。 以及hello 进程中fork、execve 函数引发的内存映射,程序执行中发生缺页的处理,以及动态存储分配的管理。
(第7章 2分)
第8章 hello的IO管理(没学,选做)
8.1 Linux的IO设备管理方法
(以下格式自行编排,编辑时删除)
设备的模型化:文件
设备管理:unix io接口
8.2 简述Unix IO接口及其函数
(以下格式自行编排,编辑时删除)
8.3 printf的实现分析
(以下格式自行编排,编辑时删除)
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall等.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
(以下格式自行编排,编辑时删除)
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
(以下格式自行编排,编辑时删除)
(第8章1分)
结论
到此我们便亲眼见证了hello程序完整的一生。看似很简单的程序,实则从编写完成到最终的运行经历了颇为复杂的过程。
在程序阶段,首先是预处理部分,经过预处理器作用,hello.c文件进行头文件展开、宏定义、忽略注释等步骤,变成hello.i文件。然后是编译的环节,hello.i文件在编译器作用下,成为hello.s汇编语言程序。然后进行汇编,hello.s经过源程序分析、代码优化等环节,被汇编器转化为二进制的可重定位目标文件hello.o。然后是链接部分,链接器将hello.o文件与动态链接库链接,使其最终成为可执行目标文件hello。
程序方面的准备工作已经完毕,我们转向操作系统一方。当我们在一个进程中要执行hello时,父进程首先会fork一个子进程,作为hello程序运行的进程,并且将父进程运行的环境和共享库等资源给fork出的子进程也复制一份,然后当进程通过fork的返回值判断自己所处的是子进程时,会调用execve函数来将hello加载到内存中并执行。hello运行时会访存,会经历各种信号的处理,会调用一些函数,比如printf函数,这些函数与linux I/O 的设备密切相关。最后是回收阶段,hello程序运行结束后,系统会接收到信号,将终止的进程清除。
这样程序hello度过了完整的一生。
(结论0分,缺失 -1分,根据内容酌情加分)
附件
- Hello.i Hello.c预处理后的文本文件
- Hello.s 编译后得到的文件
- Hello.o 汇编后得到的可重定位目标文件
- Hello 链接之后的可执行目标文件
- Hello.out Hello反汇编之后的可重定位文件
- Elf.txt Hello.o的elf格式文件
7、Hello.txt Hello的反汇编文件
(附件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分)