计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 2+x人工智能
学 号 2021111506
班 级 21WL023
学 生 陶骊勋
指 导 教 师 郑贵滨
计算机科学与技术学院
2023年5月
本文按照哈尔滨工业大学计算机系统课程大作业要求,以一个简单的hello.c程序开始,介绍了一个程序在Linux下运行的完整生命周期,包括预处理、编译、汇编、链接、进程管理、存储管理、I/O管理这几部分。
本文从预处理编译开始,到IO管理结束,通过在Linux下用各种工具对hello程序进行分析,一步步详细介绍了程序从被键盘输入、保存到磁盘,直到最后程序运行结束,程序变为僵尸进程的全过程。清晰地观察hello.c的完整周期,直观地表现其生命历程。以此来加强自己对计算机系统的认识,并希望能够帮助到其它想要了解计算机系统的同学。
关键词:计算机系统;Linux;预处理;编译;汇编;链接;进程;存储;IO管理;
目 录
第1章 概述
(0.5分)
HelloWorld想必是每一个程序员的启蒙小怪,当我们打开VSCode,输入一行行代码,按下编译运行后,我们便完成了第一次代码编写,向世界问好。这个看似非常简单的程序实则是早先第一个实现的P2P。
P2P:P2P指的是hello从一个源程序(program)变为一个在计算机上运行的进程(process)。
我们利用基本的C语言语法在编辑器中完成代码的编写,并将其保存在电脑的磁盘中,hello的program就产生了。接下来,我们将hello的源程序hello.c经过cpp进行预处理得到hello.i文件,再经过cc1进行编译得到hello.s的汇编语言文件,然后经过as进行汇编变为可重定位目标文件hello.o,最后经过ld进行链接生成可执行目标文件hello。然后用户通过在shell中输入./hello的命令,shell解析并执行命令,通过fork函数创建新的子进程,然后在子进程中执行execve函数加载并执行我们的hello。
020:020指的是hello程序从无到有,再从有到无的过程。
我们从“0”开始,利用editor编写程序的源代码,再经过预处理,编译,汇编,链接成为一个可执行目标程序。当我们在shell中输入./hello的命令,shell解析并执行命令,通过fork函数创建新的子进程,然后在子进程中执行execve函数加载并执行我们的hello程序:将hello目标程序中的代码和数据从磁盘“拷贝”到主存,加载器会从开始地址开始,进入主函数执行目标代码,处理器(CPU)则会为我们的程序分配时间片和逻辑控制流,我们的程序开始执行,输出的内容会从主存复制到寄存器中,再从寄存器复制到显示设备上,最终会在屏幕上呈现。当我们的程序运行结束后,shell父进程会回收子进程,清理掉程序运行时所占用的内存,关闭程序打开的文件描述符,释放程序运行占用的物理资源,清理程序在内核中的数据结构等,操作系统通过上下文切换,恢复shell的上下文,将控制权重新还给shell,我们的程序有回归为“0”。
1.2 环境与工具
硬件环境:X64 CPU;2GHz;2G RAM;256GHD Disk
软件环境:Windows10 64位;VMware Workstation Pro15.5.1;Ubuntu 20.04.4
开发和调试工具:gdb;edb;readelf;objdump;Code::Blocks20.03,Visual studio
-
- 中间结果
编写这篇文章生成的中间结果文件以及文件内容如下表格所示:
文件名称 | 作用 |
hello.c | 储存hello程序源代码 |
hello.i | 源代码经过预处理产生的文件(包含头文件等工作) |
hello.s | hello程序对应的汇编语言文件 |
hello.o | 可重定位目标文件 |
hello_o.s | hello.o的反汇编语言文件 |
hello.elf | hello.o的ELF文件格式 |
hello | 二进制可执行文件 |
hello.elf | 可执行文件的ELF文件格式 |
hello.s | 可执行文件的汇编语言文件 |
1.4 本章小结
本章对hello进行了总体的介绍,分别对P2P和020的意思进行了解释,并给出了实验进行的软件环境,硬件环境,以及开发/调试工具,最后给出了在实验过程中生成的中间结果文件。
第2章 预处理
(0.5分)
2.1 预处理的概念与作用
预处理的概念:
预处理就是预先处理,是指在程序代码被翻译为目标代码的过程中,生成二进制文件之前的过程。这个过程一般包括包含头文件等工作。
预处理的作用:
预处理为编译做准备工作,主要进行代码的文本替换工作,它会根据预处理指令来修改源代码。一般预处理有以下的作用:
1. 将源文件中以“include”格式包含的文件复制到编译的源文件中。
2. 用实际值替换用“#define”定义的字符串。
3. 根据“#if”后面的条件决定需要编译的代码。
2.2在Ubuntu下预处理的命令
在Ubuntu下,使用gcc的-E参数对程序进行预处理,还可以添加-o参数让结果重定向输入到某以特定文件中,下面例子为将hello.c预处理后,结果重定向输出到hello.i文件中。
2.3 Hello的预处理结果解析
处理结果分析:
通过查看hello.i中的内容我们可以发现,整个程序文件比原来的hello.c大了很多,这是因为预处理将源文件中以“include”格式包含的文件复制到编译的源文件中,cpp到默认的环境变量下依次寻找我们指定的文件,用实际值替换用“#define”定义的字符串,并根据“#if”后面的条件决定需要编译的代码。
2.4 本章小结
这一章主要介绍了hello.c程序预处理方面的内容,包括预处理的概念和作用,以及进行了hello.c文件的预处理和结果展示。预处理作为编译运行的第一步是非常重要的一部分,查看.i文件会让我们更加直观的感受到预处理前后源文件的变化。
第3章 编译
(2分)
3.1 编译的概念与作用
编译的概念:
编译器将文本文件 hello.i 翻译成文本文件 hello.s。编译程序所要做的工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。
编译的作用:
编译将高级语言转化为汇编语言能够检测代码的正确性,并为接下来将汇编语言生成机器可识别的机器码做准备。
编译包括以下基本流程:
语法分析:编译程序的语法分析器以单词符号作为输入,分析单词符号串是否形成符合语法规则的语法单位,如表达式、赋值、循环等,最后看是否构成一个符合要求的程序,按该语言使用的语法规则分析检查每条语句是否有正确的逻辑结构,程序是最终的一个语法单位。
中间代码:源程序的一种内部表示。中间代码的作用是可使编译程序的结构在逻辑上更为简单明确,特别是可使目标代码的优化比较容易实现中间代码。
代码优化:指对程序进行多种等价变换,使得从变换后的程序出发,能生成更有效的目标代码。有两类优化:一类是对语法分析后的中间代码进行优化,它不依赖于具体的计算机;另一类是在生成目标代码时进行的,它在很大程度上依赖于具体的计算机。
目标代码:生成是编译的最后一个阶段。目标代码生成器把语法分析后或优化后的中间代码变换成目标代码。此处指汇编语言代码,须经过汇编程序汇编后,成为可执行的机器语言代码。
3.2 在Ubuntu下编译的命令
Linux中使用如下指令进行编译:
gcc -S hello.i -o hello.s
3.3 Hello的编译结果解析
file 指明源文件名
text 代码段:用来存放程序代码的区域
section 指示将代码分成若干个段
rodata 用于维护只读数据
align 8 地址对齐伪指令,用来指定符号的对齐方式
string 字符串的存储位置
global 用来让一个符号对链接器可见,可以供其他链接对象模块使用,告诉编译器后续跟的是一个全局可见的名字
type 指定是对象类型或者函数类型
3.3.1 数据
(1)常量:
常量例如立即数:16
- 变量:
①全局变量:
初始化的全局变量储存在.data节中,在汇编代码中一般会标注为global。
②局部变量:
局部变量存储在寄存器或栈中。
例:
C代码
汇编
可以看到局部变量i被存储在栈中-4(%rbp)的位置。
3.3.2 赋值:
- 通过mov来实现赋值:
在这段代码中,汇编语言通过mov立即数的方式来实现对i的赋值。
- 通过leal来实现赋值:
3.3.3 算术操作:
在hello.s中所涉及的算术操作有加与减两种,分别使用add,sub指令进行操作。
- Add
此处将局部变量i(-4(%rbp))的值加1。
- Sub
此处将栈顶指针减去32,实现了栈幁的增长。
3.3.4 数组/指针/结构操作:
主函数main的参数中有指针数组char *argv[]。在argv数组中,argv[0]指向输入程序的名称,argv[1]和argv[2]分别指向两个字符串。C语言代码中的数组访问有argv[1],argv[2],argv[3],在汇编代码中访问这三个量都使用首地址加偏移量的方式。
3.3.5 函数操作:
利用call指令来调用函数,将函数需要的参数存放在寄存器中或栈中进行参数传递,函数调用结束之后,返回值保存在寄存器%rax中。
hello中出现的函数操作有:
调用puts
puts:将传入的参数输出在屏幕上。
调用exit
exit:从函数中退出。
调用sleep
sleep:实现程序的休眠。
调用getchar
getchar:从缓冲区中读取字符。
3.4 本章小结
本章对汇编指令做了以下简单的介绍,以及查看了Hello的机器级实现。经过简单的思考,我们便可以发现这些汇编指令和C语言代码语句之间的对应关系。同时,根据一个程序的汇编代码我们也可以翻译出相应的C语言程序的大致样貌。
第4章 汇编
(2分)
4.1 汇编的概念与作用
汇编的概念:汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程,汇编器(as)将.s汇编程序翻译成机器语言并将这些指令打包成可重定目标程序的格式存放在.o目标文件中,.o 文件是一个二进制文件,它包含程序的指令编码。
汇编的作用:汇编的作用很明晰了,就是完成从汇编语言文件到可重定位目标文件的转化过程。
4.2 在Ubuntu下汇编的命令
Linux系统中使用如下命令进行汇编:gcc hello.s -c -o hello.o
在linux下生成hello.o文件elf格式的命令:readelf -a hello.o > hello.elf
4.3 可重定位目标elf格式
分析hello.o的ELF格式
1. ELF头:
ELF头部以一个16字节的序列开始,描述生成该文件的系统的字的大小和字节顺序。剩下的部分包含帮助链接器分析语法和解释目标文件的信息,其中包含ELF头大小、目标文件的类型、及其类型、节头部表的文件偏移,以及节头部表中条目的大小和数量。
2.节头表
节头表是节头的集合,节头描述了每个节的名字,类型,偏移地址,大小等基本信息。
3.符号表:
存放在程序中定义和引用的函数和全局变量的信息。
- 重定位节:
.rela.text,保存的是.text节中需要被修正的信息;任何调用外部函数或者引用全局变量的指令都需要被修正;调用外部函数的指令需要重定位;引用全局变量的指令需要重定位; 调用局部函数的指令不需要重定位;在可执行目标文件中不存在重定位信息。本程序需要被重定位的是printf、puts、exit、sleepsecs、getchar、sleep和.rodata中的.L0和.L1。
4.4 Hello.o的结果解析
用指令objdump -d -r hello.o对hello.o进行反汇编。
看到hello.o的反汇编文件我们很是熟悉,它使用的汇编代码和hello.s汇编文件的汇编代码是一样的,但是在这反汇编文件的字里行间中,也混杂着一些我们相对陌生的面孔,也就是机器代码。
这些机器代码是二进制机器指令的集合,每一条机器代码都对应一条机器指令,到这儿才是机器真正能识别的语言。每一条汇编语言都可以用机器二进制数据来表示,汇编语言中的操作码和操作数以一种相当于映射的方式和机器语言进行对应,从而让机器能够真正理解代码的含义并且执行相应的功能。
对hello.o进行反汇编,与hello.s进行对比分析:
1. 数的表示:hello.s中的操作数时十进制,hello.o反汇编代码中的操作数是十六进制。
2. 分支转移:反汇编代码中跳转语句之后不是.L1等助记符,而是相对偏移的地址。
3. 函数调用:hello.s中,call指令后跟着的是函数名称,而反汇编代码中 call指令的目标地址是当前下一条指令。这是因为 hello.c 中调用的函数都是共 享库中的函数,最终需要通过动态链接器才能确定函数的运行时执行地址。在汇编成为机器语言的时候,对于这些不确定地址的函数调用,将其call指令后的相 对地址设置为全0,最后在链接时才填入正确的地址。
4.5 本章小结
本章对hello.s进行了汇编,生成了hello.o可重定位目标文件,并且分析了可重定位文件的ELF头、节头部表、符号表和可重定位节,比较了hello.s和hello.o反汇编代码的不同之处,分析了汇编语言与机器语言的对应关系。
第5章 链接
(1分)
5.1 链接的概念与作用
1.链接的概念:
链接是将各种不同文件的代码和数据部分收集(符号解析和重定位)起来并组合成一个单一文件的过程,这个文件可以被加载到内存并执行。链接可以执行于编译时,也就是在源代码被编译成机器代码时;也可以执行于加载时,也就是在程序被加载器加载到内存并执行时;甚至于运行时,也就是由应用程序来执行。
2.链接的作用:
链接器在软件开发过程中扮演着一个关键的角色,因为它们使得分离编译成为可能。我们不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其它文件。
5.2 在Ubuntu下链接的命令
Linux系统hello.c使用如下指令进行链接
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 -a hello > hello1.elf
- ELF头:hello的文件头和hello.o文件头的不同之处如下图标记所示,hello是一个可执行目标文件,有27个节
- 节头:对 hello中所有的节信息进行了声明,包括大小和偏移量
- 重定位节.rela.text:
- 符号表.symtab:
5.4 hello的虚拟地址空间
分析程序头LOAD可加载的程序段的地址为0x400000
使用edb打开hello从Data Dump窗口观察hello加载到虚拟地址的状况,并查看各段信息。
可知在0x400000~0x401000段中,程序被载入,虚拟地址0x400000开始,到0x400fff结束。
5.5 链接的重定位过程分析
命令: objdump -d -r hello > hello2.txt
与hello.o的反汇编文件对比发现,hello2.txt中多了许多节。hello.o的反汇编文件中只有一个.text节,而且只有一个main函数,函数地址也是默认的0x000000.hello2.txt中有.init,.plt,.text三个节,而且每个节中有很多函数。这是因为库函数的代码都已经链接到了程序中,程序各个节变的更加完整,跳转的地址也具有参考性。
hello比hello.o多出的节头表:
例如.dynsym 动态符号表
.dynstr 动态符号表中的符号名称
.gnu.version 符号版本
.gnu.version_r 符号引用版本
.rela.dyn 动态重定位表
.rela.plt .plt节的重定位条目
.init 程序初始化
.plt 动态链接表
.fini 程序终止时需要的执行的指令
.dynamic 存放被ld.so使用的动态链接信息
.got 存放程序中变量全局偏移量
.got.plt 存放程序中函数的全局偏移量
.data 初始化过的全局变量或者声明过的函数
hello重定位的过程:
(1)重定位节和符号定义链接器将所有类型相同的节合并在一起后,这个节就作为可执行目标文件的节。然后链接器把运行时的内存地址赋给新的节,赋给输入模块定义的每个节,以及赋给输入模块定义的每个符号,当这一步完成时,程序中每条指令和全局变量都有唯一运行时的地址。
(2)重定位节中的符号引用这一步中,连接器修改代码节和数据节中对每个符号的引用,使他们指向正确的运行时地址。
(3)重定位条目当编译器遇到对最终位置未知的目标引用时,它就会生成一个重定位条目,代码的重定位条目放在.rel.txt中
(4)重定位过程的地址计算算法如图所示:
5.6 hello的执行流程
1.开始:_start、_libc_start_main
2.执行:_main、_printf、_exit、_sleep、_getchar
3.退出:exit
程序名称 | 地址 |
_start | 0x4010f0 |
_libc_start_main | 0x2f12271d |
main | 0x401125 |
_printf | 0x401040 |
_exit | 0x401070 |
_sleep | 0x401080 |
_getchar | 0x401050 |
5.7 Hello的动态链接分析
动态共享库是致力于解决静态库缺陷的一个现代创新产物。共享库是一个目标模块,在运行或加载时,可以加载到任意的内存地址,并和一个程序链接起来,这个过程就是动态链接。
对于变量而言,我们利用代码段和数据段的相对位置不变的原则计算正确地址。对于库函数而言,需要plt、got合作,plt初始存的是一批代码,它们跳转到got所指示的位置,然后调用链接器。初始时got里面存的都是plt的第二条指令,随后链接器修改got,下一次再调用plt时,指向的就是正确的内存地址。plt就能跳转到正确的区域。
5.8 本章小结
本章主要介绍了链接的概念与作用,链接可分为符号定义和重定位,了解了可执行文件的ELF格式,分析了hello的虚拟地址空间,重定位过程,执行过程,动态连接过程,对链接有了更深的理解。
第6章 hello进程管理
(1分)
6.1 进程的概念与作用
1. 进程的概念:
进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。
2. 进程的作用:
进程为用户提供以下假象:我们的程序好像是系统中当前运行的唯一程序一样,我们的程序好像是独占的使用处理器和内存,处理器好像是无间断的执行我们程序中的指令,我们程序中的代码和数据好像是系统内存中唯一的对象。
6.2 简述壳Shell-bash的作用与处理流程
Shell-bash的作用:Linux系统中,Shell是一个交互型应用级程序,为使用者提供操作界面,接收用户命令,然后调用相应的应用程序。
处理流程:
(1)终端进程读取用户由键盘输入的命令行。
(2)分析命令行字符串,获取命令行参数,并构造传递给execve的argv向量
(3)检查第一个命令行参数是否是一个内置的shell命令
(4)如果不是内部命令,调用fork( )创建新进程/子进程
(5)在子进程中,用步骤2获取的参数,调用execve( )执行指定程序。
(6)如果用户没要求后台运行否则shell使用waitpid(或wait)等待作业终止后返回。
(7)如果用户要求后台运行,则shell返回;
6.3 Hello的fork进程创建过程
当在shell上输入./hello命令时,命令行会首先判断该命令是否为内置命令,如果是内置命令则立即对其进行解释。否则将其看成一个可执行目标文件,再调用fork创建一个新进程并在其中执行。
当shell运行一个程序时,父进程通过fork函数生成这个程序的进程。新创建的子进程的代码、数据段、堆、共享库以及用户栈几乎与父进程相同。父进程和子进程之间最大的区别在于他们有不同的PID。父进程与子进程是并发运行的独立进程,内核能够以任意方式交替执行它们的逻辑控制流的指令
6.4 Hello的execve过程
当Hellol的进程被创建之后,他会调用execve函数加载并调用程序。exevce函数在被调用时会在当前进程的上下文中加载并运行一个新程序。它被调用一次从不返回,执行过程如下:
1.删除已存在的用户区域
2.映射私有区:为 hello 的代码、数据、.bss 和栈区域创建新的区域结构,所有这些区域都是私有的、写时才复制的
3.映射共享区:比如 hello 程序与共享库 libc.so 链接
4.设置程序计数器
5.execve() 在调用成功的情况下不会返回,只有当出现错误时,例如找不到需要执行的程序时,execve() 才会返回到调用程序
6.5 Hello的进程执行
逻辑控制流:一系列程序计数器 PC 的值的序列叫做逻辑控制流。由于进程是轮流使用处理器的,同一个处理器每个进程执行它的流的一部分后被抢占,然后轮到其他进程。
用户模式和内核模式:处理器使用一个寄存器提供两种模式的区分。用户模式的进程不允许执行特殊指令,不允许直接引用地址空间中内核区的代码和数据;内核模式进程可以执行指令集中的任何命令,并且可以访问系统中的任何内存位置。
上下文:上下文就是内核重新启动一个被抢占的进程所需要恢复的原来的状态,由寄存器、程序计数器、用户栈、内核栈和内核数据结构等对象的值构成。
hello进程的执行:在进程调用execve函数之后,进程已经为hello程序分配了新的虚拟的地址空间,最初hello运行在用户模式下,输出hello 学号 姓名,然后调用sleep函数进程进入内核模式,运行信号处理程序,之后再返回用户模式。运行过程中,cpu不断切换上下文,使运行过程被切分成时间片,与其他进程交替占用cpu,实现进程的调度。
6.6 hello的异常与信号处理
1.异常和信号异常种类
类别 | 原因 | 异步/同步 | 返回行为 |
中断 | 来自I/O设备的信号 | 异步 | 总是返回到下一条指令 |
陷阱 | 有意的异常 | 同步 | 总是返回到下一条指令 |
故障 | 潜在可恢复的错误 | 同步 | 可能返回到当前指令或终止 |
终止 | 不可恢复的错误 | 同步 | 不会返回 |
- 运行结果
①正常运行
②按下 ctrl-z
当按下Ctrl+Z时,进程收到SIGSTP信号 ,hello程序被挂起,当使用ps查看进程时,可以看到进程仍然存在,通过fg加job号可以恢复执行。
③按下Ctrl+c
当按下Ctrl+C时,进程会收到SIGINT信号,结束hello程序,使用ps查看进程,发现进程被彻底结束。
④不停乱按
在中途乱按,会将输入当做是命令缓存到缓冲区中,在程序执行结束后执行命令。
6.7本章小结
本章介绍了进程的概念和作用、shell-bash的处理过程与作用并且着重分析了调用fork创建新进程,调用execve函数执行hello,hello的进程执行过程,以及hello在运行时遇到的异常与信号处理。
第7章 hello的存储管理
( 2分)
7.1 hello的存储器地址空间
1.逻辑地址:逻辑地址是指在计算机体系结构中是指应用程序角度看到的内存单元、存储单元、网络主机的地址。逻辑地址往往不同于物理地址,通过地址翻译器或映射函数可以把逻辑地址转化为物理地址。
- 线性地址:线性地址是逻辑地址到物理地址变换之间的中间层。在分段部件中逻辑地址是段中的偏移地址,然后加上基地址就是线性地址。
- 虚拟地址:CPU启动保护模式后,程序运行在虚拟地址空间中。保护模式下,hello 运行在虚拟地址空间中,它访问存储器所用的逻辑地址。
4.物理地址:它是在地址总线上,以电子形式存在的,使得数据总线可以访问 主存的某个特定存储单元的内存地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
逻辑地址由段标识符与段内偏移量组成。段标识符是一个16位长的字段组成,称为段选择符,其中前13位是一个索引号。后面三位包含一些硬件细节。
索引号就是“段描述符(segment descriptor)”的索引,段描述符具体地址描述了一个段,很多个段描述符,就组了一个数组,叫“段描述符表”,这样,就可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符。
Base字段,表示的是包含段的首字节的线性地址,也就是一个段的开始位置的线性地址。一些全局的段描述符,就放在“全局段描述符表(GDT)”中,一些局部的,例如每个进程自己的,就放在所谓的“局部段描述符表(LDT)”中。什么时候该用GDT,什么时候该用LDT是由段选择符中的T1字段表示的,=0,表示用GDT,=1表示用LDT,GDT在内存中的地址和大小存放在CPU的gdtr控制寄存器中,而LDT则在ldtr寄存器中。
7.3 Hello的线性地址到物理地址的变换-页式管理
虚拟内存被组织为一个由存放在磁盘上的N个连续字节大小的单元组成的数组。如下图,虚拟内存被分为一些固定大小的块,这些块称为虚拟页块。这些页块根据不同的映射状态也被划分为三种状态:未分配、为缓存、已缓存。
未分配:虚拟内存中未分配的页
未缓存:已经分配但是还没有被缓存到物理内存中的页
已缓存:分配后缓存到物理页块中的页
7.4 TLB与四级页表支持下的VA到PA的变换
Core i7采用四级页表的层次结构。CPU产生虚拟地址VA,虚拟地址VA传送给MMU,MMU使用VPN高位作为TLBT和TLBI,向TLB中寻找匹配。如果命中,则得到物理地址PA。如果TLB中没有命中,MMU查询页表,CR3确定第一级页表的起始地址,VPN1确定在第一级页表中的偏移量,查询出PTE,以此类推,最终在第四级页表中找到PPN,与VPO组合成物理地址PA,添加到PLT。
7.5 三级Cache支持下的物理内存访问
1.先选择虚拟地址的组索引位,找到对应的组。再将虚拟地址的标记和组中各行的标记位比较。命中的条件是高速缓存行有效位为1且标记位匹配。
2. 高速缓存命中后,再用偏移量找到需要字节的块内偏移位置,将其取出返回给CPU。
3. 若产生了不命中的现象,那么需要从L2中寻找所请求的块,若找到就将其存储在cache的一行中,如果没找到就从L3中找,若还没找到则在主存中重复以上过程。
7.6 hello进程fork时的内存映射
当fork函数被当前进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的PID,同时为这个新进程创建虚拟内存。它将两个进程中的每个页面都标记位只读,并将两个进程中的每个区域结构都标记为私有的写时复制。当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面。因此,也就为每个进程保持了私有空间地址的抽象概念。
7.7 hello进程execve时的内存映射
在执行shell执行execve时,会删除当前进程虚拟地址的用户部分中的已存在的区域结构,为新程序的代码、数据、bss和栈区域创建新的区域结构,再映射共享区域,最后设置程序计数器,使其指向代码区域的入口。
7.8 缺页故障与缺页中断处理
因为程序运行时,不会将所有的数据都加载到内存中,而是放在磁盘上等需要的时候再加载进入内存。如果程序运行需要的数据还未从磁盘上加载进入内存,也就是MMU查找PTE时没有找到对应的地址,此时就会引发缺页故障异常,而缺页故障程序会根据当前内存状态,选择牺牲内存中的一个内存页,然后把磁盘上的物理页加载进入内存中。缺页异常处理完后,会跳转到引发缺页故障的那条指令重新执行。
7.9本章小结
本小节讨论了存储空间的管理,首先解释了四种地址的概念,逻辑地址、线性地址、虚拟地址、物理地址,接着描述了逻辑地址到线性地址再到物理地址的转换过程。再用TLB和四级页表的实例描述了va到pa的转变过程,说明了如何使用物理内存访问cache。在将进程使用到的fork,execve的内存映射情况和缺页故障与缺页中断处理加以描述。
结论
(0分,必要项,如缺失扣1分,根据内容酌情加分)
hello所经历的过程:
hello.c经过预处理后得到hello.i文本文件
hello.i经过编译后得到hello.s汇编文件
hello.s经过汇编得到二进制可重定位目标文件hello.o
hello.o经过链接得到了可执行文件hello
bash进程调用fork函数,生成子进程;并由execve函数加载运行当前进程的上下文中加载并运行新程序hello
hello在变化过程中会有各种地址,但最终我们真正想要的是PA物理地址。
hello再运行时会调用一些函数,比如printf函数
hello最终被shell父进程回收,内核会收回为其创建的所有信息
从hello这样一个简单的程序的变化过程,我们可以看出即使是一个简单的程序,也需要硬件、操作系统、软件的相互协作配合,才能最终让它完美地实现自己的功能,通过本次大作业,我系统的认识了整个计算机系统结构,在这个过程中遇到了非常多不懂的东西,在通过翻阅书籍和查阅网站把这些内容搞懂后,不禁感叹计算机系统是多么复杂而又精密的系统,计算机从出现到如今,只有短短80年不到,而发展却是如此的迅猛,从简简单单的电子管电路,到如此精密的系统,不禁感叹,人类的智慧是如此神奇而美妙。
文件名称 | 功能 |
hello.c | 源程序 |
hello.i | 预处理后文件 |
hello.s | 编译后的汇编文件 |
hello.o | 汇编后的可重定位目标执行文件 |
hello | 链接后的可执行文件 |
hello.elf | hello.o的ELF格式 |
hello1.txt | hello.o的反汇编 |
hello2.txt | hello的反汇编代码 |
hello1.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.
(参考文献0分,缺失 -1分)