哈工大ICS大作业

 

计算机系统

大作业

题     目  程序人生-Hello’s P2P 

专       业     计算机类                  

学     号                        

班     级    2003004                   

学       生   郭奥博                  

指 导 教 师   史先俊                    

计算机科学与技术学院

2022年5月

摘  要

本文伴随着hello程序走完了它的“一生”,从开始一开始在键盘上手动编写hello.c源程序,之后运行C预处理器(cpp)将其进行预处理生成hello.i文件,运行C编译器(ccl)将其进行翻译生成汇编语言文件hello.s,然后运行汇编器(as)将其翻译成一个可重定位目标文件hello.o,最后运行链接器程序ld将hello.o和系统目标文件组合起来,创建了一个可执行目标文件hello。当shell接收到./hello的指令后开始调用fork函数创建进程,execve加载hello进入内存,由CPU控制程序逻辑流的运行,中断,上下文切换和异常的处理,随着最后结束进程并由父进程进行回收,hello也走向“生命”的尽头,勾勒其完整奇妙的一生。

关键词:预处理;编译;汇编;链接;进程;储存;IO管理                           

(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分

目  录

第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(From Program to Process):首先编写生成一个hello.c的程序文本,然后经过cpp预处理(hello.i—>ccl编译(hello.s—>as汇编(hello.o—>ld链接,生成一个可执行目标程序,然后使用shell为他fork建立一个子进程,成为一个Program。

O2O(From Zero to Zero):shell帮他execute,将他映射进虚拟内存,然后在开始运行进程的时候分配并载入物理内存,然后进入main函数执行目标代码。运行结束后,shell父进程回收子进程,内核删去有关数据结构。

1.2 环境与工具

1.2.1 硬件环境

64位windows10操作系统 x64CPU;处理器Intel(R) Core(TM) i7-10875H CPU @ 2.30GHz   2.30 GHz;机带RAM16.0GB

1.2.2 软件环境

Windows 10 64位;Vmware15.5.6;Ubuntu19.04。

1.2.3 开发工具

codeblocks64位20.04;Visual Studio2019;vi/vim/gedit+gcc;EDB

1.3 中间结果

hello.i :hello.c的预处理文本

hello.s :hello.i的汇编文本

hello.o :hello.s的可重定位的二进制文件

hello :hello.o的可执行文件

hellos.txt :hello.o使用objdump处理后的反汇编文本

hellos1.txt :hello使用objdump处理后的反汇编文本

hello.elf   :用readelf读取hello.o得到的ELF格式文件

1.4 本章小结

简述了接下来要完成的内容,环境工具以及产生的中间文件。

(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用

预处理的概念:预处理器在程序编译之前,根据以字符#开头的命令(头文件、define等),修改原始的C程序,将引用的所有库展开合并成为一个完整的文本文件。

预处理的作用:1.用#include形式将源文件中声明的文件复制到新的程序中。2.用#define将数值或字符定义为字符串3.方便编译器在对程序进行翻译的时候更加方便

2.2在Ubuntu下预处理的命令

Linux中hello.c文件进行预处理的命令是:gcc -E -o hello.i hello.c

正在上传…重新上传取消

图2-1

正在上传…重新上传取消2.3 Hello的预处理结果解析

图2-2

经过预处理之后,生成了hello.i文件,打开该文件可以发现,文件的内容明显增加,该文件主要为为对原文件中的头文件进行展开,例如声明函数、定义结构体、定义变量、定义宏等内容,如果代码中有#define命令还会对相应的符号进行替换。

2.4 本章小结

预处理的主要作用是对源文件的头部引用的展开,为后续过程添加需要的条件编译和完善程序文本文件等等,预处理可以使程序在后边的操作中减少很多阻碍,是整个过程中非常重要的第一步。

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

编译的概念:1、利用编译程序从源语言编写的源程序产生目标程序的过程。 2、用编译程序产生目标程序的动作。

编译的作用:将输入的高级程序设计语言翻译成以汇编语言或机器语言表示的目标程序作为输出,为不同高级语言的不同编译器提供了通用的输出语言。

3.2 在Ubuntu下编译的命令

图3-1

Linux下的编译的命令:gcc -S hello.i -o hello.s正在上传…重新上传取消

3.3 Hello的编译结果解析

图3-2

正在上传…重新上传取消3.3.1汇编指令介绍

.file 声明源文件
.text 代码节
.section   .rodata 只读代码段
.global 声明全局变量
.type 声明一个符号是函数类型还是数据类型
.size 声明大小
.string 声明一个字符串
.align 声明对指令或者数据的存放地址进行对齐的方式

3.3.2数据

局部变量

       图3-3

正在上传…重新上传取消

main函数声明了一个局部变量i,编译器进行编译的时候将局部变量i会放在堆栈中。如图所示,局部变量i放在栈上-4(%rbp)的位置。

字符串

       图3-4

在本程序中的字符串为 Hello 学号 姓名 秒数!和Hello %s %s,对应存储在.rodata节中,其中汉字被编码成UTF-8格式,一个汉字占据3个字节,每个字节使用\分隔。正在上传…重新上传取消

数组

       图3-5

正在上传…重新上传取消

图3-6

正在上传…重新上传取消

char *argv[]是作为main函数的第二个参数,数组的每个元素都是一个指向字符类型的指针,保存其需要申请32个字节,数组元素一个的大小为八字节,数组的起始地址存放在栈中-32(%rbp)的位置。

3.3.3赋值操作

       整个hello.s只存在一个i=0赋值语句

       图3-7

正在上传…重新上传取消

3.3.4算数操作

   图3-8

正在上传…重新上传取消  

       hello.s中的i++操作,还有算术操作指令如

addq   $24, %rax 使用add修改地址偏移量等等

3.3.5关系操作

正在上传…重新上传取消       图3-9

  

hello.c中argc!=3是条件判断语句,进行编译时,这条指令被编译为cmpl $3,-20(%rbp),且在比较之后还设置了条件码,根据条件码判断是否需要跳转。

图3-10

正在上传…重新上传取消

hello.c中i<8,作为判断循环条件指令被编译为cmpl $7,-4(%rbp),并设置条件码,为下一步 jle 利用条件码进行跳转做准备。

3.3.6控制转移指令

汇编语言中先设置条件码,然后根据条件码来进行控制转移

      图3-11

正在上传…重新上传取消

判断argc是否等于4,如果argc等于4,则不执行if语句,否则执行if语句

正在上传…重新上传取消      图3-12

判断变量i是否满足循环条件i<8

3.3.7函数操作

正在上传…重新上传取消图3-12

图3-13

正在上传…重新上传取消

图3-14

正在上传…重新上传取消

函数的调用在源代码中调用了printf、atoi、getchar、sleep和exit:第一个printf转换成了puts,把.L0段的立即值传入%rdi,然后call跳转到puts。

exit是把立即数1传入到%edi中,然后call跳转到exit第二个printf有三个参数,第一个是.LC1中的格式化字符串%eax中,后面的两个依次是%rdi,%rsi,然后跳转到printf。

sleep有一个参数传到%edi中,之后call跳转到 sleep中,getchar不需要参数,直接call跳转即可。对于返回值,函数的返回值一般在寄存器%eax中,如果有返回值,则要先把返回值存到%eax中,再用ret返回。源程序中有主函数的return 0;就是先把返回值立即数0存到%eax中,再用ret返回。

3.3.8类型转换

本程序中利用atoi函数将argv[3]由字符串转换为了整型。

3.4 本章小结

本章主要介绍了编译器处理c语言程序的基本过程,函数从源代码变为等价的汇编代码,编译器分别从c语言的数据,赋值语句,类型转换,算术操作,逻辑/位操作,关系操作,控制转移与函数操作这几点进行分析,让我们对汇编代码有了进一步的了解。

汇编语言的一个特点就是它所操作的对象不是具体的数据,而是寄存器或者存储器,汇编语言的执行速度要比其它语言快,但也使编程也更加复杂,汇编代码已经与高级语言编写的代码有了很大的不同,汇编语言程序与机器有着密切的联系。

(第32分)

第4章 汇编

4.1 汇编的概念与作用

汇编的概念:汇编器将hello.s 翻译成与之等价的机器语言指令,把这些指令打包成可重定位目标程序的格式,并将结果保存在目标文件hello.o 中。hello.o文件是一个二进制文件,它包含的是程序的指令编码。

汇编的作用:将汇编代码转换为机器指令,使其在链接后能被机器识别并执行。

4.2 在Ubuntu下汇编的命令

图4-1

正在上传…重新上传取消

Linux下命令为:gcc -c -o hello.o hello.s

4.3 可重定位目标elf格式

在linux下生成.elf格式文件的命令为:readelf -a hello.o > hello.elf

图4-2

分析正在上传…重新上传取消.elf文件

图4-3

ELF头(ELF header)以一个16B的序列Magic开始,这个序列描述了生成该文件的系统的字的大小和字节顺序。ELF头剩下的部分包含了帮助链接器语法分析和解释目标文件的信息,其中包括ELF头的大小、目标文件的类型(如可重定位、可执行或者共享的)、机器类型(如x86-64)、节头部表(section header table)的文件偏移,以及节头部表中条目的大小和数量。不同节的位置和大小是有节头部表描述的,其中目标文件中每个节都有一个固定大小的条目(entry)。

图正在上传…重新上传取消4-4

正在上传…重新上传取消Section Headers:节头部表,记录各节名称、类型、地址、偏移量、大小、全体大小、旗标、链接、信息、对齐。 由于是可重定位目标文件,所以每个节都从0开始,用于重定位。

图4-5

.rela.text:保存的是.text节中需要被修正的信息;任何调用外部函数或者引用全局变量的指令都需要被修正;调用外部函数的指令需要重定位;引用全局变量的指令需要重定位; 调用局部函数的指令不需要重定位;在可执行目标文件中不存在重定位信息。

图4-6

正在上传…重新上传取消Symbol table:符号表,存放程序中定义和引用的函数和全局变量的信息。每个可重定位目标文件在.symtab中都有一张符号表,name是符号名称, value是符号相对于目标节的起始位置偏移,对于可执行目标文件,该值是一个绝对运行的地址。size是目标的大小,type数据或者函数。Bind字段表明符号是本地的还是全局的。

4.4 Hello.o的结果解析

利用objdump -d -r hello.o  > hellos.txt 生成hello.o的反汇编文件hellos.txt

与hello.s比较发现以下差别:

(1)分支转移:反汇编的跳转指令用的不是段名称比如.L3,而是用的确定的地址。但在反汇编代码中,分支转移表示为主函数+段内偏移量。反汇编代码跳转指令的操作数使用的不是段名称,因为段名称只是在汇编语言中便于编写的助记符,所以在汇编成机器语言之后显然不存在,而是确定的地址。

(2)函数调用:汇编代码中函数调用时直接个函数名称,而在反汇编的文件中call之后加main+偏移量(定位到call的下一条指令),即用具体的地址表示。在.rela.text节中为其添加重定位条目等待链接。

(3)立即数表示不同:立即数都变成了十六进制,较于十进制更容易处理。

图4-7

正在上传…重新上传取消

4.5 本章小结

本章主要介绍了汇编过程,我们通过查看hello.elf文件内容了解了可重定位目标件的格式,观察了elf文件中的文件头、程序节、符号表等信息,反汇编得到hello.o文件的汇编代码并与hello.i进行了对比,学习了与反汇编形成的汇编代码的异同。
第5章 链接

5.1 链接的概念与作用

链接的概念:

链接是将各种代码和数据片段收集并合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行于编译时,也就是在源代码被翻译成机器代码时;也可以执行于加载时,也就是在程序被加载器加载到内存并执行时;甚至执行于运行时,也就是由应用程序来执行。

链接的作用:

链接器在软件开发过程中扮演着一个关键的角色,因为它们使得分离编译成为可能。我们不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其它文件。

5.2 在Ubuntu下链接的命令

Linux下的命令: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-1

正在上传…重新上传取消

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

  在linux命令行输入命令readelf -h hello即可看到ELF Header的具体信息

       图5-2

正在上传…重新上传取消       可从type类型得知该目标文件格式为可执行文件

       图5-3

正在上传…重新上传取消      在linux命令行输入命令readelf -S hello即可看到Section Headers的具体信息

Section Headers:节头部表,记录各节名称、类型、地址、偏移量、大小、全体大小、旗标、链接、信息、对齐。 因此根据 Section Headers 中的信息我们就可以用 HexEdit 定位各个节所占的区间(起始位置,大小)。其中Address是程序被载入到虚拟地址的起始地址。

       图5-4

在linux命令行输入命令readelf -s hello即可看到hello.elf中Symbol table的具体信息正在上传…重新上传取消。

5.4 hello的虚拟地址空间

图5-5

观察Data Dump窗口,发现虚拟地址从0x400000开始到0x401ff0结束。根据的节头部表,可以通过edb找到各个节的信息正在上传…重新上传取消。

5.5 链接的重定位过程分析

图5-6

正在上传…重新上传取消

正在上传…重新上传取消图5-7

hello生成的.asm文件中节的个数多余hello.o生成的.asm文件中节的个数,多了比如.init和.plt。

图5-8

正在上传…重新上传取消

hello反汇编的代码有确定的虚拟地址,也就是说已经完成了重定位,而hello.o反汇编代码中代码的虚拟地址均为0,未完成可重定位的过程。

hello的重定位过程:重定位节和符号定义链接器将所有类型相同的节合并在一起后,这个节就作为可执行目标文件的节。然后链接器把运行时的内存地址赋给新的聚合节,赋给输入模块定义的每个节,以及赋给输入模块定义的每个符号,当这一步完成时,程序中每条指令和全局变量都有唯一运行时的地址;重定位节中的符号引用这一步中,连接器修改代码节和数据节中对每个符号的引用,使他们指向正确的运行时地址;重定位条目当编译器遇到对最终位置未知的目标引用时,它会生成一个重定位条目。代码的重定位条目放在.rel.txt中。

5.6 hello的执行流程

通过edb打开hello文件,运用step into指令调试,遇到函数会发生跳转

0x00007ffff7e16e20 <_init>

0x0000000000401090<_start>

0x0000000000401150<in __libc_csu_init>

0x0000000000401000<_init>

0x00000000004010c5<main>

0x0000000000401030<puts>

0x0000000000401070<exit>

5.7 Hello的动态链接分析

根据.elf文件可知,got起始表位置为0x403ff0

正在上传…重新上传取消

我们观察链接前的.got.plt表中内容:

图5-9

正在上传…重新上传取消

执行链接后,有:

       图5-10

正在上传…重新上传取消

可以观察到,0404000到0404010之间的一段数据发生了变化。此变化便是GOT表中加载了共享库的内容。

5.8 本章小结

本章主要介绍了链接概念作用及相关种类和操作,并介绍了hello.o通过链接生成可执行文件的过程,同时详细介绍了hello的ELF格式和各个节的含义并且与上一章中hello.o生成的ELF文件进行对比,并且分析了hello的虚拟地址空间、重定位过程、执行流程、动态链接过程,使我们对链接有了更深的理解。

6章 hello进程管理

6.1 进程的概念与作用

进程的概念:

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

进程的作用:

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

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

shell是命令行界面的解析器,能够为用户提供操作界面,提供内核服务。shell能执行一系列的读、求操作,然后终止。读操作读取来自用户的一个命令行。求值操作解析命令并代表用户运行程序。

shell的处理流程为:读取用户输入解析用户输入若要执行内部命令,直接执行若要执行非内部命令,shellfork子进程,在子进程中execve执行相关命令根据&的有无,确定程序的前后台运行

6.3 Hello的fork进程创建过程

在终端中输入命令行./hello H200300401 郭奥博 1后,shell会处理该命令,如果判断出不是内置命令,则会调用fork函数创建一个新的子进程,子进程几乎但不完全与父进程相同。通过fork函数,子进程得到与父进程用户级虚拟地址空间相同的但是独立的一份副本,拥有不同的PID。

6.4 Hello的execve过程

execve的功能是在当前进程的上下文中加载并运行一个新程序。在执行fork得到子进程后随即使用解析后的命令行参数调用execve,execve调用启动加载器来执行hello程序。加载器执行的操作是,加删除子进程现有的虚拟内存段,并创建新的代码、数据、堆和栈段。代码和数据段被初始化为hello的代码和数据。堆和栈被置空。然后加载器将PC指向hello程序的起始位置,即从下条指令开始执行hello程序。

6.5 Hello的进程执行

进程上下文切换:如果系统调用因为等待某个事件发生而阻塞,那么内核可以让当前进程休眠,切换到另一个进程;进程时间片:进程的运行本质上是CPU不断从程序计数器 PC 指示的地址处取出指令并执行,值的序列叫做逻辑控制流,在一个程序被调运行开始到被另一个进程打断,中间的时间就是运行的时间片;用户态和核心态: 进程只有故障、中断或陷入系统调用时才会得到内核访问权限,其他情况下始终处于用户权限之中,这样可以保证系统的安全性;进程调度:即加载保存的寄存器,切换虚拟地址空间。

6.6 hello的异常与信号处理

正常运行

       图6-1

Ctrl正在上传…重新上传取消+z

       图6-2

Ctrl+c

正在上传…重新上传取消       图6-3

正在上传…重新上传取消

Ctrl+z之后输入ps然后和jobs

图6-4

正在上传…重新上传取消

输入pstree

       图6-5

乱按正在上传…重新上传取消

       图6-6

正在上传…重新上传取消

异常可以分为四类:中断、陷阱、故障、 终止。

       图6-7

正在上传…重新上传取消

使用fg将后台程序置于前台

       图6-8

正在上传…重新上传取消

6.7本章小结

本章重点介绍了了进程、shell的概念与作用,分析了hello程序使用fork创建子进程的过程以及使用execve加载并运行用户程序的过程,运用上下文切换、用户模式、内核模式、内核调度等知识,分析了hello进程的执行过程,最后分析了hello对于异常以及信号的处理并进行了实际操作。

7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:逻辑地址是程序经过编译后出现在汇编代码中的地址。逻辑地址用来指定一个操作数或一条指令的地址。它由选择符和偏移量组成。

线性地址(虚拟地址):一个逻辑地址在经过段地址机制的转化后变成一个线性分页地址,它与逻辑地址类似也是一个不真实的地址。其格式可以表示为虚拟地址描述符:偏移量。

物理地址:计算机系统的主存被组织成一个由M个连续的字节大小的单元组成的数组。每字节都有一个唯一的物理地址。物理地址用于内存芯片级的单元寻址,与处理器和CPU链接的地址总线相对应。最后两位涉及权限检查。

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

段是对程序逻辑意义上的一种划分,一组完整逻辑意义的程序被划分为一段。段的长度不确定。段描述符用于描述一个段的详细信息。段选择符用于找到对应的段描述符。流程:通过段选择符的T1字段,确定是GDT中段还是LDT中的段;查找段描述符,获得基地址;基地址+偏移,得到线性地址。

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

基本原理:将程序的逻辑地址空间划分为固定大小的页,而物理内存划分为同样大小的页框。程序加载时,可将任意一页放人内存中任意一个页框,这些页框不必连续,从而实现了离散分配;传输单元:系统将虚拟页作为进行数据传输的单元。Linux下每个虚拟页大小为4KB。物理内存也被分割为物理页, MMU(内存管理单元)负责地址翻译,MMU使用页表将虚拟页到物理页的映射,即虚拟地址到物理地址的映射;n位的虚拟地址包含两个部分:一个p位的虚拟页面偏移(VPO),一个n-p位的虚拟页号(VPN),MMU利用VPN选择适当的PTE,根据PTE,我们知道虚拟页的信息,如果虚拟页是已缓存的,那直接将页表条目的物理页号和虚拟地址的VPO串联起来就得到一个相应的物理地址。VPO和PPO是相同的。如果虚拟页是未缓存的,会触发一个缺页故障。调用一个缺页处理子程序将磁盘的虚拟页重新加载到内存中,然后再执行这个导致缺页的指令。

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

TLB:当处理 器要在主内存寻址时,不是直接在内存的物理地址里查找的,而是通过一组虚拟地址转换到主内存的物理地址,TLB就是负责将虚拟内存地址翻译成实际的物理内 存地址,而CPU寻址时会优先在TLB中进行寻址;VA->PA:处理器生成一个虚拟地址,并将其传送给MMU。MMU用VPN向TLB请求对应的PTE,命中得到PA。否则MMU查询页表,CR3确定第一级页表的起始地址,VPN1确定在第一级页表中的偏移量,查询出PTE,最终在第四级页表中找到PPN,与VPO组合成物理地址PA,添加到PLT。

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

得物理地址之后,先取出组索引对应位,在L1中寻找对应组。如果存在,则比较标志位,相等后检查有效位是否为1.如果都满足则命中取出值传给CPU,否则按顺序对L2cache、L3cache、内存进行相同操作,直到出现命中。然后再一级一级向上传,如果有空闲块则将目标块放置到空闲块中,否则将缓存中的某个块驱逐,将目标块放到被驱逐块的位置。

7.6 hello进程fork时的内存映射

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

7.7 hello进程execve时的内存映射

删除已存在的用户区域;映射私有区域:所有这些新的区域都是私有的、写时复制的。代码和数据被映射至.text区和.data区。bss区域,堆是请求二进制零的区域。映射共享区域:hello程序与共享对象链接,这些对象动态链接到hello程序,然后映射到用户虚拟地址空间。设置程序计数器:设置当前进程上下文的程序计数器,使之指向代码区域的入口点。

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

缺页故障:当指令引用一个虚拟地址,在 MMU中查找页表时发现与该地址相对应的物理地址不在内存中,因此必须从磁盘中取出的时候就会发生故障;缺页中断处理:先检查虚拟地址是否合法,再检查进程是否有读、写或执行该区域页面的权限。如果两者皆无误,选择一个牺牲页面,如果这个牺牲页面被修改过,那么就将它交换出去,换入新的页面并更新页表。再将控制转移给hello进程,再次执行触发缺页故障的指令。

7.9动态存储分配管理

对于每个进程,内核维护着一个变量 brk,它指向堆的顶部。分配器将堆视为一组不同大小的块的结合来维护。每个块就是一个连续的拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可用来分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。动态内存分配器维护着进程的虚拟内存区域,成为堆。分配器将堆视为一组不同大小块的集合。各个块是已分配的或者是空闲的。

显式分配器:要求应用显式地释放任何已分配的块;隐式分配器:要求分配器检测一个已分配块何时不再使用,那么就释放这个块,垃圾收集:自动释放未使用的已经分配的块的过程。

隐式空闲链表:空闲块通过头部中的大小字段隐含地连接着。分配器可以通过遍历堆中所有的块,从而间接地遍历整个空闲块的集合;显式空闲链表:每个空闲块中,都包含一个前驱和后继指针。使用双向链表使首次适配的时间减少到空闲块数量的线性时间。

7.10本章小结

本章简述了系统对于hello的存储管理,介绍了intel段式、页式管理,分析了程序的虚拟地址逐步翻译为物理地址的过程,分析程序运行过程中forkexecve函数进行的内存映射,说明了系统对于缺页异常的处理以及动态存储相关的分配。

8章 hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:所有IO设备都被模型化为文件甚至是内核,所有的输入和输出都能被当做相应文件的读和写来执行。

设备管理:unix io接口,Linux内核有一个简单、低级的接口,称为Unix I/O,这使得所有的输入和输出都能以一种统一且一致的方式来执行。

8.2 简述Unix IO接口及其函数

接口:打开文件:通过内核代开文件,内核返回非负整数,成为描述符。描述符表示这个文件。内核记录有关文件的所有信息。

文件位置:每个打开的文件,内核保持一个文件位置k,表示从文件开头起始的字节偏移量;读写文件:进行复制操作并改变文件位置k的值;关闭文件:内核释放相应数据结构,将描述符恢复到可用的描述符池中。

函数:int open(char *filename, int flags, mode_t mode)将filename转换为文件描述符,返回描述符数字,总返回进程中没有打开的最小描述符。

int close(fd)

关闭一个打开的文件。

ssize_t read(int fd, void *buf, size_t n);

从fd复制至多n个字节到buf

ssize_t write(int fd, const void *buf, size_t n);

从buf复制至多n个字节到fd

8.3 printf的实现分析

printf函数:

int printf(const char *fmt, ...)

{

    int i;

    va_list arg = (va_list)((char *)(&fmt) + 4);

    i = vsprintf(buf, fmt, arg);

    write(buf, i);

    return i;

}

可以发现printf的输入参数是fmt,但是后面是不定长的参数,同时在printf内存调用了两个函数,一个是vsprintf,一个是write。

int vsprintf(char *buf, const char *fmt, va_list args)

{

    char *p;

    chartmp[256];

    va_listp_next_arg = args;

    for (p = buf; *fmt; fmt++)

    {

        if (*fmt != '%')

        {

            *p++ = *fmt;

            continue;

        }

        fmt++;

        switch (*fmt)

        {

        case 'x':

            itoa(tmp, *((int *)p_next_arg));

            strcpy(p, tmp);

            p_next_arg += 4;

            p += strlen(tmp);

            break;

        case 's':

            break;

        default:

            break;

        }

        return (p - buf);

    }

}

其中vsprintf(buf, fmt, arg)函数能够返回我们想要打印的字符串的长度并对我们的格式化字符串进行解析。当获取到字符串的长度后,我们便能够将字符串打印出来。

vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80syscall.

字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。

显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)

8.4 getchar的实现分析

getchar()函数运行时,用户通过键盘输入控制权交给os,输入的内容便会显示在屏幕上。按下回车键表示输入完成,这时控制权将被交还给程序;异步异常-键盘中断的处理:键盘接口产生中断请求,键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区;getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。

8.5本章小结

本章主要介绍了 Linux 的 IO 设备管理方法、Unix IO 接口及其函数,分析了 printf 函数和 getchar 函数的实现。

结论

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

你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。

hello程序的过程可总结如下:

1、编写代码:用高级语言写.c文件

2、预处理:从.c生成.i文件,将.c中调用的外部库展开合并到.i中

3、编译:由.i生成.s汇编文件

4、汇编:将.s文件翻译为机器语言指令,并打包成可重定位目标程序hello.o

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

6、运行并创建子进程:在shell中输入命令,shell调用fork为程序创建子进程并加载,shell调用execve函数,将hello程序加载到该子进程,映射虚拟内存

7、执行指令:CPU为进程分配时间片,加载器将计数器预置在程序入口点,则hello可以顺序执行自己的逻辑控制流

8、访问内存:MMU将虚拟内存地址映射成物理内存地址,CPU通过其来访问

9、动态内存分配:根据需要申请动态内存

10、信号:shell的信号处理函数可以接受程序的异常和用户的请求

11、终止:hello运行结束,被父进程回收,内核删除为该进程创建的数据结构

(结论0分,缺失 -1分,根据内容酌情加分)

附件

列出所有的中间产物的文件名,并予以说明起作用。

hello.i :hello.c的预处理文本

hello.s :hello.i的汇编文本

hello.o :hello.s的可重定位的二进制文件

hello :hello.o的可执行文件

hellos.txt :hello.o使用objdump处理后的反汇编文本

hellos1.txt :hello使用objdump处理后的反汇编文本

hello.elf   :用readelf读取hello.o得到的ELF格式文件

(附件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分)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值