计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 计算学部
学 号 1190201920
班 级 1903008
学 生 张森
指 导 教 师 吴锐
计算机科学与技术学院
2021年5月
本篇论文为计算机系统的结课论文,研究hello.c在linux系统下的整个生命周期。有以下内容:对hello.c的概述;hello.c的预处理;编译;汇编;链接;一直到hello.c的进程;存储;IO管理。在linux下用gcc对其进行分析。通过分析解读linux下的hello从C代码变成可执行程序的整个过程,将本学期所学重点内容进行整理,巩固所学内容,以便学生将理论知识巩固。
关键词:hitics;hello;程序执行;计算机系统
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
目 录
2.2在Ubuntu下预处理的命令............................................................................. - 5 -
5.3 可执行目标文件hello的格式........................................................................ - 8 -
6.2 简述壳Shell-bash的作用与处理流程........................................................ - 10 -
6.3 Hello的fork进程创建过程......................................................................... - 10 -
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 -
8.2 简述Unix IO接口及其函数.......................................................................... - 13 -
第1章 概述
1.1 Hello简介
Hello的P2P:From Program To Process
在linux中,键入的hello.c文件,此时hello只是一个program。通过预处理(cpp)、编译(ccl)、汇编(as)、链接(ld)这一系列操作后,生成可执行目标程序hello。在终端hello的目录下,shell中输入“./hello”命令,进程管理为其创建子进程(fork),此时hello已成为process。
Hello的020:From Zero-0 To Zero-0
execve(执行文件)在父进程中fork一个子进程后,在子进程中调用exec函数启动新的程序,映射入内存,操作系统为这个进程分时间片。当该进程的时间片到达时,操作系统设置CPU上下文环境,并跳到程序开始处。当指令将“Hello World\n”字符串中的字节从主存经一系列cache复制到寄存器文件,再从寄存器文件中经I/O管理复制到显示设备,最终显示在屏幕上。程序执行完毕,shell负责回收这个子进程,此时hello不再占用内存1.2 环境与工具
。
1.2 环境与工具
硬件环境:X64 CPU;4GHz;4G RAM;256GHDDisk
软件环境:Vmware 16,Ubuntu16.04 LTS 64位/优麒麟 64位
开发与调试工具:vim,gcc,as,ld,edb,readelf,HexEdit
1.3 中间结果
作用
hello.i
hello.c经预处理之后得到的文本文件
hello.s
hello.i文件经编译后得到的文本文件
汇编语言源代码文件
hello.o
hello.s文件经汇编后得到的文件
可重定位目标文件
hello
hello.o文件经过链接后得到的文件
可执行目标文件
helloelf
hello.o的ELF格式
hello2.s
hello.o的反汇编代码
helloelf2
hello的ELF格式
hello3.s
hello的反汇编代码
1.4 本章小结
本章概括论述了 hello的 p2p,020 过程,列出了所需环境:硬件环境、软件环境,开发与调试工具。以及生成的中间结果文件的名字,文件的作用等。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
(以下格式自行编排,编辑时删除)
程序设计领域中,预处理一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。典型地,由预处理器(preprocessor) 对程序源代码文本进行处理,得到的结果再由编译器核心进一步编译。这个过程并不对程序的源代码进行解析,但它把源代码分割或处理成为特定的单位——(用C/C++的术语来说是)预处理记号(preprocessing token)用来支持语言特性(如C/C++的宏调用)。
最常见的预处理是C语言和C++语言。ISO C和ISO C++都规定程序由源代码被翻译分为若干有序的阶段(phase) [1] [2] ,通常前几个阶段由预处理器实现。预处理中会展开以#起始的行,试图解释为预处理指令(preprocessing directive) ,其中ISO C/C++要求支持的包括#if/#ifdef/#ifndef/#else/#elif/#endif(条件编译)、#define(宏定义)、#include(源文件包含)、#line(行控制)、#error(错误指令)、#pragma(和实现相关的杂注)以及单独的#(空指令) [1] [2] 。预处理指令一般被用来使源代码在不同的执行环境中被方便的修改或者编译。
预处理器在UNIX传统中通常缩写为PP,在自动构建脚本中C预处理器被缩写为CPP的宏指代。为了不造成歧义,C++(cee-plus-plus) 经常并不是缩写为CPP,而改成CXX。
注意预处理常被错误地当作预编译(precompiling) ,事实上这是两个不同的概念。预处理尽管并不是ISO C/C++要求的单独阶段,但“预处理”这个术语正式地出现并参与构成其它术语,如C的预处理翻译单元(preprocessing translation unit) [1] 以及C/C++词法规则中预处理记号(prerprocessing-token) 这个语法分类 [1] [2] 。预编译是一些编译器支持的特性,不是C/C++语言的特性或实现必须要求遵循的规则涉及到的内容,没有在ISO C/C++全文中出现 [1] [2] 。
2.2在Ubuntu下预处理的命令
gcc -E hello.c -o hello.i
应截图,展示预处理过程!
2.3 Hello的预处理结果解析
编译预处理后会在当前文件夹下生成.i文件。将所有的#define删除,并且展开所有的宏定义;处理所有条件编译指令,如#if,#ifdef等;处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。该过程递归进行,及被包含的文件可能还包含其他文件。删除所有的注释//和 /**/;添加行号和文件标识。保留所有的#pragma编译器指令,因为编译器需要使用它们;打开此.i文件,会发现经过预处理,文件由原来的23行扩展为3121行。且在.i文件中不存在#include,而是将头文件,,的内容插入到该命令所在的位置,从而把头文件和当前源文件连接成一个源文件。
2.4 本章小结
本章主要介绍了预处理的概念与作用、将hello.c进行了预处理,生成hello.i文件,并结合hello.c预处理之后的程序对预处理结果进行了解析,为下一步编译做准备。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
利用编译程序从源语言编写的源程序产生目标程序的过程。编译程序把一个源程序翻译成目标程序的工作过程分为五个阶段:词法分析;语法分析;语义检查和中间代码生成;代码优化;目标代码生成。主要是进行词法分析和语法分析,又称为源程序分析,分析过程中发现有语法错误,给出提示信息。
编译器的构建流程主要分为 3 个步骤:
1. 词法分析器,用于将字符串转化成内部的表示结构。
2. 语法分析器,将词法分析得到的标记流(token)生成一棵语法树。
3. 目标代码的生成,将语法树转化成目标代码。
作用:把预处理完的文件进行一系列词法分析,语法分析,语义分析及优化后生成相应的汇编代码文件。例如,编译器(ccl)将文本文件hello.i翻译成文本文件hello.s,它包含一个汇编语言程序。
注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序
3.2 在Ubuntu下编译的命令
:gcc -S hello.i -o hello.s
应截图,展示编译过程!
3.3 Hello的编译结果解析
3.1 编译的概念与作用
编译的概念:编译是指把代码转化为汇编代码的过程。
作用:用来生成只于CPU相关的汇编指令,为进一步生成机器代码着准备。
3.2 在Ubuntu下编译的命令
gcc -S hello.i -o hello.s
3.3 Hello的编译结果解析
3.3.1字符串
源代码中已规定的字符串在.rodata段中保存:
使用时直接使用标志即可:
3.3.2整型数据
int i;在main函数中作为局部变量直接保存在栈中。
argc: 作为参数传入。
3.3.3数组
传入三个参数与该文件的地址名字组成一个字符串数组argv[],并且这些数据也都保存在栈中,每个元素占8个字节。
这栈中的32个字节就是用来保存四个参数的。
3.3.4关系操作
判断argc是否等于4,cmpl一句计算argc-4,并且更新条件码,以便后面判断。
判断i是否小于8,同上,这个是小于等于而已。
3.3.5控制转移
A.if语句:
两个分支结构,判断条件是否成立之后,若相等,跳转到L2,若不成立,对用puts输出结果。
B.for语句
.L2是循环语句初始化过程,然后跳转到.L3-判断条件是否满足,若满足,则回溯到.L4即循环体的内容,执行完后,顺序进入到.L3判断过程,直到条件不满足停止循环。
3.3.6算术操作
i++;
3.3.7函数调用
A.printf先把要打印字符串的地址放到rdi中,即作为参数传给函数,因为只有一个字符串,故函数printf退化为函数puts,然后调用函数puts。
B.exit,传入退出状态参数1,然后调用函数exit.
C.Printf,因为默认函数调用前保存参数的寄存器顺序为rdi,rsi,rdx,rcx.故因为本次调用涉及三个参数,
rdi中保存字符串,rsi中是argv[1],rdx中是argv[2].最后调用printf。
D.atoi,把字符串的首地址放入rdi中,然后调用函数,并且函数的返回值会放入寄存器eax中。
E.sleep,把atoi的返回值放入rdi中,调用sleep函数。
F.Getchar,没有参数,直接调用。
3.3.8函数返回
调用函数main进行控制转移,来开始main的运行,执行完所有功能代码后返回0,把0移入默认返回寄存器eax,然后ret.
3.3.9数组操作
数组保存在栈中,利用与rbp的相对位置进行寻址。
,找到初始位置,后面引用加相对位置即可。
此部分是重点,说明编译器是怎么处理C语言的各个数据类型以及各类操作的。应分3.3.1~ 3.3.x等按照类型和操作进行分析,只要hello.s中出现的属于大作业PPT中P4给出的参考C数据与操作,都应解析。
3.4 本章小结
本章介绍了编译的概念与作用,通过对hello.s的解析,读懂汇编语句,了解汇编语句下:数据:常量、表达式、类型、,算数操作,逻辑/位操作,关系操作,控制转移以及函数操作的基本知识。
通过编译,我们已将原始.c文件生成为汇编语言程序,距离可执行目标文件又进一步。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
把汇编语言翻译成机器语言的过程称为汇编。汇编器(as)将.s文件中的汇编代码翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在.o目标文件中。
注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。
4.2 在Ubuntu下汇编的命令
应截图,展示汇编过程!
4.3 可重定位目标elf格式
分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。
项目分析:
偏移量是指需要进行重定向的代码在.text或.data节中的偏移位置。
信息:包括symbol和type两部分,其中symbol占前4个字节,type占后4个字节,symbol代表重定位到的目标在.symtab中的偏移量,type代表重定位的类型。
类型:重定位到的目标的类型。
名称:重定向到的目标的名称。
加数:计算重定位位置的辅助信息。
对于相对寻址的重定位,需要计算其目标机器代码中的地址:
在(偏移量+该节的基地址)处修改,再使下一条指令的地址减去变量地址即可。
4.4 Hello.o的结果解析
objdump -d -r hello.o 分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。
说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。
机器语言指令由指令指示符、寄存器指示符和操作数指示符构成。除含具体操作数的指令外,机器语言与汇编语言有一一对应关系,任何汇编指令都有唯一的机器语言编码。
不一致:
A.在指令跳转时,包括实现分支转移、循环和函数调用时需要的地址,在汇编代码中,会直接使用规定的标志符号或函数名称,是字符集表示,而在反汇编代码中则运用了相对于该函数的偏移量,而在二进制代码中,则把该地址置为NULL,以后重定位时再填充。
B.在访问全局变量时,汇编代码中使用的是,而反汇编中用的是0,,因为rodata中数据地址也是在运行时确定,故访问也需要重定位。所以在汇编成为机器语言时,将操作数设置为全0并添加重定位条目。
4.5 本章小结
本章介绍了通过汇编器(as)将hello.s汇编程序翻译成机器语言指令,把这些指令打包成可重定位目标程序的格式的过程。最终生成文件为hello.o。并且生成了hello.o的ELF文件格式,介绍了其中的内容。通过hello.o获得反汇编代码,并且与hello.s文件进行了对比。发现反汇编后分支转移,函数调用,全局变量访问的方式有所不同。间接了解到从汇编语言映射到机器语言汇编器需要实现的转换。
(第4章1分)
第5章 链接
5.1 链接的概念与作用
将多个可重定位的目标文件合并成可执行文件。链接器将每个符号引用与一个确定的符号定义关联起来。将多个单独的代码节和数据节合并为单个节。将符号从它们的在.o文件的相对位置重新定位到可执行文件的最终绝对内存位置。更新所有对这些符号的引用来反映它们的新位置。
注意:这儿的链接是指从 hello.o 到hello生成过程。
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
使用ld的链接命令,应截图,展示汇编过程! 注意不只连接hello.o文件
5.3 可执行目标文件hello的格式
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。
上图为节头部表,是该ELF文件所有节构成,第一列为各段大小,倒数第二列为各段起始地址。
5.4 hello的虚拟地址空间
使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。
PHDR:保存程序头表
INTERP:动态链接器的路径
LOAD:可加载的程序段
DYNAMIN:保存了由动态链接器使用的信息
NOTE保存辅助信息
GNU_STACK:标志栈是否可执行
GNU_RELRO:指定重定位后需被设置成只读的内存区域
使用edb打开hello从Data Dump窗口观察hello加载到虚拟地址的状况,并查看各段信息。
在0x400000~0x401000段中,程序被载入,自虚拟地址0x400000开始,到0x400fff结束,这之间每个节的地址同5.3中图(2)中的地址声明。
5.5 链接的重定位过程分析
objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。
结合hello.o的重定位项目,分析hello中对其怎么重定位的。
使用edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程。请列出其调用与跳转的各个子程序名或程序地址。
与hello.o生成的反汇编文件对比发现,hello1.txt中多了许多节。hello0.txt中只有一个.text节,而且只有一个main函数,函数地址也是默认的0x000000.hello1.txt中有.init,.plt,.text三个节,而且每个节中有许多的函数。库函数的代码都已经链接到了程序中,程序各个节变的更加完整,跳转的地址也具有参考性。
hello比hello.o多出的节头表。
.interp:保存ld.so的路径
.note.ABI-tag
.note.gnu.build-i:编译信息表
.gnu.hash:gnu的扩展符号hash表
.dynsym:动态符号表
.dynstr:动态符号表中的符号名称
.gnu.version:符号版本
.gnu.version_r:符号引用版本
.rela.dyn:动态重定位表
.rela.plt:.plt节的重定位条目
.init:程序初始化
.plt:动态链接表
.fini:程序终止时需要的执行的指令
.eh_frame:程序执行错误时的指令
.dynamic:存放被ld.so使用的动态链接信息
.got:存放程序中变量全局偏移量
.got.plt:存放程序中函数的全局偏移量
.data:初始化过的全局变量或者声明过的函数
部分截图如下:
5.6 hello的执行流程
(1) 载入:_dl_start、_dl_init
(2)开始执行:_start、_libc_start_main
(3)执行main:_main、_printf、_exit、_sleep、
_getchar、_dl_runtime_resolve_xsave、_dl_fixup、_dl_lookup_symbol_x
(4)退出:exit
程序名称 地址
ld-2.27.so!_dl_start 0x7fb85a93aea0
ld-2.27.so!_dl_init 0x7f9612138630
hello!_start 0x400582
lib-2.27.so!__libc_start_main 0x7f9611d58ab0
hello!puts@plt 0x4004f0
hello!exit@plt 0x400530
5.7 Hello的动态链接分析
分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的内容变化。要截图标识说明。
在进行动态链接前,首先进行静态链接,生成部分链接的可执行目标文件hello。此时共享库中的代码和数据没有被合并到hello中。加载hello时,动态链接器对共享目标文件中的相应模块内的代码和数据进行重定位,加载共享库,生成完全链接的可执行目标文件。
动态链接采用了延迟加载的策略,即在调用函数时才进行符号的映射。使用偏移量表GOT+过程链接表PLT实现函数的动态链接。GOT中存放函数目标地址,为每个全局函数创建一个副本函数,并将对函数的调用转换成对副本函数调用。
调用init之前的.got.plt
调用init之后的.got.plt
从图中可以看到.got.plt的条目发生变化。
5.8 本章小结
概括了链接的概念和作用,重点分析了hello程序的虚拟地址空间、重定位和执行过程。简述了动态链接的原理
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
进程的概念:进程是操作系统对一个正在运行的程序的一种抽象,进程是对处理器、主存和I/O设备的抽象表示,进程是一个执行中程序的实例。
进程的作用:当运行一个程序时,进程允许操作系统提供一种假象,就好像系统上只有这个程序在运行,程序看上去是独占地使用处理器、主存和I/O设备。处理器看上去就像在不间断地一条接一条地执行程序中的指令,即该程序的代码和数据是系统内存中唯一的对象。
6.2 简述壳Shell-bash的作用与处理流程
作用:shell是一个交互性的应用级程序,它代表用户运行其他程序,shell执行一系列的读求值步骤,然后终止。
处理流程:
读步骤读取来自用户的一个命令行,然后eval函数对命令行求值,首先调用parseline函数,这个函数解析了以空格分隔的命令行参数,并构造出最终会传递给execve的argv向量。第一个参数被假设为要么是一个内置的shell命令名,马上就会解释这个命令,要么是一个可执行的目标文件,会在一个新的子进程的上下文中加载并运行这个文件。最后一个参数如果是&字符,那么parseline返回1,表示应该在后台执行,否则返回0,在前台执行。
在解析了命令行之后,eval函数调用builtin_command函数,该函数检查第一个参数是否是一个内置的shell命令名,马上就会解释这个命令,并返回1.如果返回0,shell就会创建一个子进程,并在子进程中执行所请求的程序。
如果用户要求在后台运行该程序,那么shell返回到循环的顶部,等待下一个命令行,否则shell使用waitpid函数等待作业终止,当作业终止时,shell就开始下一轮迭代。
6.3 Hello的fork进程创建过程
系统调用fork函数,创建一个子进程,该子进程拥有和父进程完全一致的代码、数据和资源,由于fork函数在子进程和父进程中的返回值不同,利用条件语句if(fork()==0)后加子进程内容即可加入只在子进程中执行的代码,故创建了一个hello进程。
6.4 Hello的execve过程
在为hello创建的子进程中,系统调用execve函数,该过程加载可执行文件,之后调用启动代码,启动代码设置栈,并将控制传递给hello程序的主函数。
6.5 Hello的进程执行
进程调度的过程:
在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先的被抢占了的进程。这种决策就叫做调度。 在内核调度了一个新的进程运行后,它就抢占当前进程,并使用种称为上下文切换的机制来将控制转移到新的进程。
上下文切换1)保存当前进程的上下文,2)恢复某个先前被抢占的进程被保存的上下文,3)将控制传递给这个新恢复的进程。
当内核代表用户执行系统调用时,可能会发生上下文切换。如果系统调用因为等待某个事件发生而阻塞,那么内核可以让当前进程休眠,切换到另一个进程。
例如,系统调用sleep函数,就先将本来运行在用户模式中的hello程序,转移到核心态,由内核完成sleep函数的执行,最后再把控制转移给用户即回到用户态。
6.6 hello的异常与信号处理
hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。
可能会出现中断、陷阱和系统调用、故障和终止这几类异常,会产生下列几种信号:
名称 默认行为 相应事件
SIGINT 终止 来自键盘的中断
SIGKILL 终止 杀死程序(该信号不能被捕获不能被忽略)
SIGSEGV 终止 无效的内存引用(段故障)
SIGALRM 终止 来自alarm函数的定时器信号
SIGCHLD 忽略 一个子进程停止或终止
输入回车:刚好8个,并没有影响该进程,说明输入的回车键被忽略。
Ctrl+C:说明终止了该进程,发送了SIGINT信号。
Ctrl+Z:发送一个 SIGTSTP 信号给前台进程组中的进程,从而将其挂起.
输入命令ps查看所有进程及其运行时间,发现hello还没有终止,
输入命令jobs查看当前暂停的进程,发现了hello的进程,输入命令fg使之在前台运行,导致了剩余几行的打印,输入命令kill,结束进程hello。
此时,再用ps查看,已经不存在hello进程了。
6.7本章小结
本章通过对进程的讨论,使shell加载和运行hello程序的过程完整地展现出来,并且通过进一步操作,了解信号对进程的影响。
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
(以下格式自行编排,编辑时删除)
逻辑地址是指由程式产生的和段相关的偏移地址部分,它是相对于当前进程数据段的地址,程序代码会产生逻辑地址。
线性地址是由逻辑地址即段中的偏移地址加上相应段的基址得到的,是处理器可寻址的内存空间中的地址, 是逻辑地址到物理地址变换之间的中间层。
物理地址是用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。现代操作系统都提供了一种内存管理的抽像,即虚拟内存。进程使用虚拟内存中的地址,即虚拟地址,由操作系统协助相关硬件,把它“转换”成真正的物理地址。hello.s中使用的就是虚拟空间的虚拟地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
首先,将逻辑地址分成段选择符+段描述符的判别符(TI)+地址偏移量的形式,然后先判断TI字段,判断它是局部段描述符(ldt)还是全局段描述符(gdt),然后再将其组合成段描述符+地址偏移量的形式,这样就转换成线性地址了。
7.3 Hello的线性地址到物理地址的变换-页式管理
(以下格式自行编排,编辑时删除)
在这个转换中要用到翻译后备缓冲器(TLB),首先我们先将线性地址分为VPN(虚拟页号)+VPO(虚拟页偏移)的形式,然后再将VPN拆分成TLBT(TLB标记)+TLBI(TLB索引)然后去TLB缓存里找所对应的PPN(物理页号)如果发生缺页情况则直接查找对应的PPN,若是没有找到,即有效位为0,则去告诉缓冲器或主存中,找到后更新TLB,若没有找到,则触发一个缺页异常,最终找到PPN之后,将其与VPO组合变为PPN+VPO就是生成的物理地址了。
7.4 TLB与四级页表支持下的VA到PA的变换
(以下格式自行编排,编辑时删除)
虚拟地址VA被分成VPN和VPO两部分,VPN被分为TLBT和TLBI用于在TLB中查询。根据TLBI确定TLB中的组索引,并根据TLBT判断PPN是否已被缓存到TLB中,如果TLB命中,则直接返回PPN,否则会到页表中查询PPN。在页表中查询PPN时,VPN会被分为四个部分,分别用作一二三四级页表的索引,而前三级页表的查询结果为下一级页表的基地址,第四级页表的查询结果为PPN。将查询到的PPN与VPO组合,得到物理地址PA。
7.5 三级Cache支持下的物理内存访问
(以下格式自行编排,编辑时删除)
7.6 hello进程fork时的内存映射
fork函数为每个进程提供私有的虚拟地址空间。首先,创建当前进程的mm_struct, vm_area_struct和页表的原样副本,两个进程中的每个页面都标记为只读,两个进程中的每个区域结构(vm_area_struct)都标记为私有的写时复制。当hello返回时,hello进程拥有与调用fork进程相同的虚拟内存,随后的写操作通过写时复制机制创建新页面。
7.7 hello进程execve时的内存映射
1.删除已存在的用户区域。
2.创建新的私有区域(.malloc,.data,.bss,.text)
3.创建新的共享区域(libc.so.data,libc.so.text)
4.设置PC,指向代码的入口点。
7.8 缺页故障与缺页中断处理
缺页故障:当指令引用一个虚拟地址,在MMU中查找页表时发现与该地址相对应的物理地址不在内存(高速缓存和主存)中,因此必须从磁盘中取出的时候就会发生故障。
缺页中断处理:缺页处理程序是系统内核中的代码,选择一个牺牲页面,如果这个牺牲页面被修改过,那么就将它写回内存,磁盘中,换入新的页面并更新页表。当缺页处理程序返回时,CPU重新启动引起缺页的指令,这条指令再次发送VA到MMU,这次MMU就能正常翻译VA了。
7.9动态存储分配管理
动态内存分配器维护着一个进程的虚拟内存区域-堆,分配器将堆视为一组不同大小的块来维护,每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。用户调用相应的申请和释放函数,动态内存分配器就会改变相应的块来完成要求,或检查相应的块,或遍历寻找空闲块。
带边界标签的隐式空闲链表分配器原理
边界标记即在有效荷载的前端和后端都加一个相同的标记,一个称为头部,另一个称为脚部,结构为 (块大小 | alloc),这样分配器就可以在常数时间内知道任意一个块它的前驱和后继的情况。
显示空间链表的基本原理
显示空闲链表将空闲块组织为某种形式的显示数据结构,并且实现这个数据结构的指针存放在这些空闲块的主体里。
使用双向链表,可以使用后进先出的顺序维护链表,即将新释放的块放置在链表的开始处,也可以按照地址顺序来维护链表,即每个块的地址都小于它后继的地址。
7.10本章小结
首先是计算机系统中各种形式地址的使用与它们之间的相互转换方法,然后对于hello进程中是如何使用这些地址的过程的了解和动态内存分配中的具体过程。
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:文件,所有的I/O设备(例如网络、磁盘和终端)都被模型化为文件,而所有的输入和输出都被当作对响应文件的读和写来进行。
设备管理:unix io接口,使得所有的输入和输出都能以一种统一且一致的方式来进行,包括打开文件,改变当前的文件位置,读写文件和关闭文件等操作。
8.2 简述Unix IO接口及其函数
A.打开或创建文件:
int open(char *filename, int flags, int perms);
int creat(char *name, int perms);
flags的主要值有三个 O_RDONLY O_WRONLY O_RDWR
perms即unix文件权限,对于open,perms填0就可以读取。
B.关闭文件与删除文件:
int close(int fd);用于断开与文件的链接 释放文件描述符
int unlink(char *filename);用于删除文件
C. 读取文件与写入文件:
int read(int fd, char *buf, int n );
int write(int fd, char *buf, int n);
buf是缓冲区,可以是一个数组名, 也可以时char指针,n为每次要传输的字节数,read和write的返回值均为真实传输的字节数
D.游标移动
int lseek(int fd, long offset, int origion);
lseek可以在文件中随意移动而不会修改文件内容
其中offset是相对于origin移动的距离,origin可以为 0 1 2,分别代表文件头、当前位置、文件尾。
8.3 printf的实现分析
https://www.cnblogs.com/pianist/p/3315801.html
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall等.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
arg表示的是…中的第一个参数的地址,vsprintf函数生成显示信息,并且返回arg所指字符串中的字符个数,vsprintf的作用就是格式化。它接受确定输出格式的格式字符串fmt。用格式字符串对个数变化的参数进行格式化,产生格式化输出。然后通过系统调用安全的write函数说明打印buf中的i个字符。
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
(以下格式自行编排,编辑时删除)
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
int getchar(void)
{
char c;
return (read(0,&c,1)==1)?(unsigned char)c:EOF
}
getchar函数通过调用read函数来读取字符。 int read(int fd, char *buf, int n );
read函数由三个参数,第一个参数为文件描述符fd,fd为0表示标准输入;第二个参数为输入内容的指针;第三个参数为读入字符的个数。read函数的返回值是读入字符的个数,若出错则返回-1。
当用户按键时,会触发一个异步异常,进行键盘中断的处理,键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
(以下格式自行编排,编辑时删除)
unix io接口为我们提供了一种机制,使得所有的输入和输出都能以一种统一且一致的方式来进行,可以方便的用来实现一些功能,例如printf,getchar等。
(第8章1分)
结论
用计算机系统的语言,逐条总结hello所经历的过程。
你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。
(结论0分,缺失 -1分,根据内容酌情加分)
附件
列出所有的中间产物的文件名,并予以说明起作用。
hello.c:源代码
hello.i:预处理后的文本文件
hello.s:编译之后的汇编文件
hello.o:汇编之后的可重定位目标执行文件
hello:链接之后的可执行文件
a.txt:hello.o的ELF格式
helloelf.txt:hello的ELF格式
hello0.txt:hello.o反汇编代码
hello1.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分)