计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 计算机科学与技术
学 号 2021110541
班 级 2103101
学 生 刘奥
指 导 教 师 刘宏伟
计算机科学与技术学院
2022年5月
摘 要
在本大实验中,我们以一个hello.c源程序为例,深入跟踪一个程序,理解它从出生到终结的一生:预处理,编译,汇编,链接生成可执行文件hello, 同时理解计算机系统是如何和hello程序进行进程管理,存储管理,I/O管理的,通过探索程序的一生和它与计算机系统之间的工作配合,来深化对计算机系统的理解。
关键词:预处理、编译、汇编、链接、可执行文件、进程管理、存储管理、I/O管 理
目 录
第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简介
根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。
一、Hello的P2P:预处理器(cpp)将源程序文本hello.c根据以#开头的命令,转换成修改了的源程序(仍为文本文件)hello.i,接着编译器(ccl)将文本文件hello.i翻译成一个包含汇编语言程序的文本文件hello.s,接下来汇编器(as)将hello.s翻译成机器语言指令,指令打包成可重定位目标程序格式并保存在目标文件hello.o中(二进制文本),最后由链接器将多个可重定位目标程序(例如printf.o)与hello.o合成一个可执行文件hello,并存放在磁盘上。
然后为了在Unix系统上运行,将文件名输入到shall命令行解释器中,fork一个子进程,并用execve运行程序,最终生成一个进程。(过程如图1.1)
图1.1由源程序生成进程的过程
二、Hello的020:shell对其子进程调用execve函数,然后映射出虚拟内存(为每个进程提供一致的地址空间并保护其不被其他进程破坏的功能),并在程序开始运行时载入物理内存,运行中CPU为程序分配时间片执行逻辑控制流,程序结束时,父进程回收并释放子进程的程序数据与空间占用,最终实现程序执行的全过程。
1.2 环境与工具
列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开发与调试工具。
硬件环境:X64CPU;16CPUs;3.2GHz;16384MB RAM;BIOS J6CN40WW
软件环境:Windows11 64位;Codeblocks;Ubantu16.04 LTS64位
开发与测试工具:gcc,vim,edb/gdb,readelf等
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
hello.c:程序源代码
hello.i:预处理之后得到的修改了的文本文件
hello.s:编译器编译后得到的汇编语言文本文件
hello.o:汇编器生成的可重定位目标文件(二进制文件)
hello: 链接得到的可执行目标文件(二进制文件)
hello_elf1:可重定位文件生成的elf格式文件
hello_elf2:可执行文件生成的elf格式文件
asm1.txt: 可重定位文件反汇编生成的汇编语言文本文件
asm2.txt: 可执行文件反汇编生成的汇编语言文本文件
1.4 本章小结
本小节是整个大作业的目录部分,简要介绍了hello程序的生成与执行过程,并介绍本次实验的实验环境以及过程中生成的文件,详细的讲解将在下方完成。
第2章 预处理
2.1 预处理的概念与作用
预处理的概念:预处理是在程序编译前进行的基于宏定义命令、文件包含命令、条件编译命令等命令的处理,是对程序的简单加工,不进行语法错误检查,是进行编译前的程序准备阶段。
预处理的作用:本讲解主要基于C语言讨论,C中定义了三种预处理功能:1、宏定义。2、文件包含。3、条件编译。
一、宏定义(带参数的宏定义和不带参数的宏定义)
不带参数的宏定义就是在作用区间内简单的字符串替换,而带参数的宏定 义还要在此基础上进行参数的替换。
不带参数的宏定义:#define 标识符 字符串;
带参数的宏定义:# define 宏名(参数表) 字符串;
宏定义可以在一定程度上使代码简洁,提高程序运行效率。
二、文件包含(一个源文件将另一个源文件的全部内容包含进来)
一般有两种格式:#include “file” 和 #include
使用双引号:首先到当前目录下查找被包含的文件,再到系统指定的包含 文件目录;使用尖括号:直接到系统指定的包含文件目录查找。文件包含 的目的是利用复制功能,来避免后续重复工作。
三、条件编译(根据宏定义进行部分代码的静态编译)
其由一些伪指令构成,如:#ifdef、#ifndef,其目的是减少下一步代码 编译的数量,从而节省空间占用,并且提高了C语言通用性,不同计算机 能兼容执行代码程序。
2.2在Ubuntu下预处理的命令
Linux下,在命令行中输入gcc -E -o hello.i hello.c实现预处理,如下图2.2.1 所示
图2.2.1 Linux下对源程序进行预处理过程
2.3 Hello的预处理结果解析
根据下图2.3.1我们不难看出,程序的文本文件变得繁多复杂,原因是预处理过程中生成的文本文件将头文件的代码添加进去,宏定义进行代换处理,包含的文件也进行写入,最后还去掉了注释
。
图2.3.1 预处理文件的生成结果
宏定义展开:图2.3.2
图2.3.2宏定义展开成extern FILE *()形式
文件包含:图2.3.3
图2.3.3将声明内容加入到源文件
2.4 本章小结
本小节简单介绍了可执行文件生成的第一步:预处理。在这个过程中我们看似将代码变得繁杂,其实本质上是有利于后续操作(尤其是编译过程),预处理过程将多个程序,文件整合在一起能提高效率、节约空间、提高兼容性。
第3章 编译
3.1 编译的概念与作用
编译的概念: 编译是将代码转化为汇编指令的过程,汇编指令只与CPU相关,其可以将逻辑一致的代码转化为统一形式。在这个转化过程中,编译进行语法的检查、中间代码的生成、源程序的优化等过程。
一、语法检查:以C语言程序为例,编译过程可以分析出正确的C语言指令以错误的顺序排列造成的编译错误,或者C语言符号使用不当造成的错误,这里语法检测的实现是基于C语言特定的语法树的,但是编译过程不能分析得出动态的语义错误,因为其没有违反C语言的规则。
二、中间代码的生成:中间代码的生成能使编译结构在逻辑上更加简单明确,而且可以进行对于中间代码的优化工作,使得后续优化变得简单,因此除了少部分简单计算,大部分复杂语句都会进行中间代码的生成。
三、源程序的优化:编译过程可以基于中间代码(或不生成中间代码)的前提下进行程序优化,例如乘除变为移位、直接寻址与间接寻址的选择、复杂指令化简等操作。
3.2 在Ubuntu下编译的命令
在Linux下,输入指令gcc -S hello.i-o hello.s来实现编译过程,如下图3.2.1所示
图3.2.1编译的过程与汇编语言文本文件的生成
3.3 Hello的编译结果解析
3.3.1汇编指令开头声明:如下图3.3.1图
图3.3.1汇编指示部分
.file:声明对应源文件
.text: 代码段,只读和可执行的
.section:定义内存段,将代码划分为多个段,在操作系统加载时不同段加载到
不同的地址中,赋予不同的操作权限。
.align:数据/地址的对其方式
.string:字符串声明
.globl :声明全局变量
.type:声明一个字符的类型是函数/数据
3.3.2数据:
(1)常量
立即数常量表示形式为$+数字(十进制)。如图3.3.2.1
图3.3.2.1 立即数
字符串常量可在.string声明中看到,其存储形式为十进制数(依照ASCII码转换生成)
图3.3.2.2 字符串常量
(2)变量:
1.全局变量:全局变量存储在内存中,在汇编代码阶段,可在.globl处查看,如图3.3.2.3main为全局变量
图3.3.2.3全局变量
2.局部变量:局部变量存储在寄存器中,当局部变量不够被寄存器存放时将其存放在栈中。当然对于数组和拥有&的局部变量也需要将其存放在内存(栈)中。
图3.3.2.4存放在栈中局部变量
3.静态变量:一般通过static定义,仅使用在文本文件中的变量,其也被编译器存放在栈中。
(3)表达式:编译器将它进行处理结果放入寄存器或者内存中
(4)宏定义:在预处理阶段已经展开,编译阶段仅需译码即可
3.3.3 赋值
赋值在汇编语言中就是将一个值赋给寄存器,这个值可以来自立即数、寄存器或者内存中,一般来说在汇编语言中通过movX(b、w、l、q)及其相关变形指令(movsXX、movzXX)来实现。
对于movX指令,根据操作数据大小的不同将其分为1、2、4、8字节(对应b、w、l、q)如图3.3.3.1
如图3.3.3.1简单数据传输MovX指令
movsXX:符号扩展数据传输指令,与简单类似不过高位通过符号位扩展
movzXX:零扩展数据传输指令,高位通过0位扩展
图3.3.3.2 数据传输指令
如图3.3.3.2第一行和第四行是将内存中的值赋给(返回值)寄存器中,第七行是寄存器向寄存器赋值。
3.3.4 类型转换(显示或者隐式)
显示类型转换是程序员用强制转换符()对程序操作进行的数据类型转换,隐式类型转换是编译器内部将数据类型进行的转换。
我们常见的整形与整型、字符型的转换都是依据按位操作通过截断和扩展实现的;当整形转化为浮点型时,是根据数据的值进行的,可能存在舍入操作。
本程序中的转换为字符转化为整形,运用atoi函数按位操作转换的,如下图
图3.3.4.1 类型转换(显示)
3.3.5 sizeof
sizeof()是一个内存容量度量函数,以字节为单位返回变量大小,如下图
图3.3.5.1 间接寻址中的偏移计算
指针大小为八,其为返回值开辟了四个相应的内存空间。
3.3.6算术操作以及逻辑/位操作
下方列举出整形相关的算术操作,除了加载有效地址(leaq)外其余的指令都 是更加标准的一元或二元操作,》A表示算术右移,》L表示逻辑右移。
图3.3.6.1算术操作与位移操作
图3.3.6.2加法操作
上图将立即数加到寄存器%rax存储的值上
图3.3.6.3加载地址
上图将%rip中存储的数加上.LC1的值所对应地址中的内容加载到寄存器%rdi中
3.3.7关系操作和跳转
在条件码寄存器中存在控制执行条件分支的指令:
CF:进位标志,最近操作使最高位产生进位。
ZF: 零标志,最近操作结果为零。
SF:符号标志,最近操作结果为负。
OF:溢出标志,最近操作补码溢出。
对应的我们列举如下比较和测试指令:
图3.3.7.1比较和测试指令
以下是针对大作业中的具体实例:
图3.3.7.2比较双字
将%rbp-20地址中内容与4进行比较,并执行后续相同则跳转的指令。
控制转移部分用到jump指令,如下
图3.3.7.3 jump指令
具体例子:
图3.3.7.4 具体跳转指令
上面三个跳转指令分别对应,直接无判断跳转、有符号小于等于跳转、相等/零时跳转。
3.3.8数组/指针/结构操作
对于数组/指针/结构的操作是通过基址地址偏移寻址的方式存取的,其偏移量的大小基于对应变量的字节大小。
其内存访问形式有如下几种,对于引用数组和结构元素时,比较复杂的寻址常常应用:
图3.3.8.1内存访问形式
具体实例:
图3.3.8.2 参数数组的访问
上图实现了对参数数组的访问,基于(基址+偏移量)寻址来实现的。
3.3.9函数操作
函数操作时基于call指令对指定函数调用的操作,当控制从函数p转移到函数q时需要把PC设置为Q的代码起始位置并将p当前地址的下一个地址压栈用于返回时继续执行函数p的操作。
函数传递需要有参数,因此对寄存器%rdi、%rsi、%rdx、%rcx、%r8、%r9分别存放六个参数,多余的参数将在栈中存储。
当调用过程中出现了局部变量,需要为其分配内存空间,并在结束返回时释放。
图3.3.9.1 运行时的栈
图3.3.9.2 调用指令
具体在大作业中的实现:
%dei中存放sleep的秒数。
%dei中存放退出的状态。
这里的ret是主函数的返回
图3.3.9.3 函数的调用实现
3.4 本章小结
简述了编译的定义及作用,用gcc实现了编译的过程,通过观察源文件,分析了九个方面:数据、赋值、类型转换、sizeof、算术和逻辑/位操作、关系操作和跳转、数组/指针/结构操作、函数操作的具体实现过程。
第4章 汇编
4.1 汇编的概念与作用
汇编的概念:把汇编语言翻译成机器语言的过程(生成机器能识别的二进制文件),
汇编的过程中把机器语言指令打包生成可重定位目标程序的格式,保存在hello.o中。
汇编的作用:生成机器能看懂的二进制机器语言,用于后续生成可执行文件。
4.2 在Ubuntu下汇编的命令
在Linux下,汇编过程的命令为gcc -c hello.s -o hello.o,如下图:
4.3 可重定位目标elf格式
下图为经典可重定位目标文件的格式:
图4.3.1典型的ELF可重定位目标文件
为了便于分析观察我们通过指令readelf -a hello.o > ./hello_elf1.txt将elf格式文本导入到hello_elf1.txt文本文件当中,截图如下:
图4.3.2 生成可读文本文件hello_elf1.txt
图4.3.3 hello_elf1.txt中的内容
一、ELF头
ELF头是以一个16字节的序列开始的,这个序列描述了生成该文件的系统的字的大小和字节顺序。头以下的部分是帮助链接器语法分析和解释目标文件信息的(ELF头的大小、目标文件的类型、机器类型、节头部表的文件偏移以及节头部表中条目的大小和数量。
图4.3.4ELF头
二、节头部表
不同节的位置和大小是由节头部表描述的,其中目标文件中每个节都有一个固定大小的条目。
图4.3.5节头部表
我们可以从中读出节的名称,类型,地址、偏移量以及对齐信息等,以.rodata为例类型为PROGBITS,地址为Ox0,偏移量为0xd8,对其要求为8字节。
三、重定位节
重定位的节记录引用外部变量的信息,便于后续基于表中信息与相应的内存空间连接起来,也就是基于基址和偏移地址计算正确的地址:
图4.3.6重定位节
我们可以从图中分析得出偏移地址、基址信息、链接器识别修改类型、重定位到目标的名称等。
四、符号表
每个可重定位目标模块m都有一个符号表,它包含m定义和引用的符号信息,符号表中的符号包括:由模块m定义并能被其他模块引用的全局符号、外部符号、
只被模块m定义和引用的局部符号。
需要注意的是.symtab中的符号表对应于本地非静态程序变量的任何符号,这些符号是被栈管理的。
图4.3.7符号表
通过上图,我们可以分析得到符号名称、定义的节以及对于定义节起始位置的偏移大小、类型,符号是全局还是本地等相关信息。
4.4 Hello.o的结果解析
图4.4.1 生成反汇编文本文件
图4.4.2反汇编文件内容
经过对比发现,反汇编文件中不仅有汇编语言还有机器语言,我们可以发现一条汇编语言的实现对应这相应的机器语言,这是一个双射,表明两者可以相互转化,下面就不同点进行分析。
一、分支转移
图4.4.3 跳转的对比
在hello.s文件中跳转指令是以段名称(.L3)作为目标的,而在反汇编文件中跳转地址是用下一条指令的相对地址偏移量给出的。
二、函数调用
图4.4.4函数调用的对比
在hello.s文件中函数调用是以函数名称作为目标的,而在反汇编文件中函数调用是用下一条指令的相对地址偏移量给出的,这是因为机器无法识别函数名称,需要将其转化为地址给出。注意:特殊情况下可能需要等到链接,动态链接外部函数后才能确定地址。
三、操作数
图4.4.5操作数对比
在hello.s文件中操作数都是十进制,而反汇编文件中立即数以十六进制存储。
4.5 本章小结
简述了汇编这一过程的概念与作用,进行汇编操作后对程序ELF格式文件进行分析,然后反汇编生成文本文件与hello.s文本文件比较异同,加深对汇编过程的理解。
第5章 链接
5.1 链接的概念与作用
链接的概念:链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可以被加载到内存并执行。广义上的链接可以执行于编译时(源代码被翻译成机器代码时)、加载时(程序被加载器加载到内存并执行时)和运行时(应用程序执行)。
链接的作用:大作业中链接作用是将hello.o与其他可重定位文件(例如printf.o)组合共同生成可执行文件hello,并让其加载到内存运行。
5.2 在Ubuntu下链接的命令
在Linux下输入命令:gcc -m64 -Og -no-pie -fno-stack-protector -fno-PIC hello.c -o hello
图5.2.1 在shall命令行中输入链接命令并生成可执行文件
5.3 可执行目标文件hello的格式
与生成重定位目标文件的ELF格式类似,输入到命令行指令readelf -a hello > hello_elf2.txt生成可执行文件的ELF格式,如图:
图5.3.1 hello的ELF格式
一、ELF头
图5.3.2 ELF头
与可重定位文件的格式类似,ELF头描述文件的总体格式,还包括程序的入口点,也就是当程序运行时要执行的第一条地址(如图第11行)。
二、节头部表
图5.3.3 节头部表
其与hello.o 的节头部表对于节信息的描述范围一致,包括:节的名称,类型,地址、偏移量以及对齐信息等
三、程序头部表
图5.3.4程序头部表
程序头部表用于描述一种映射关系,可执行文件的连续片被映射到连续的内存段。
从程序头部表我们会看到根据可执行目标文件的内容初始化为四个内存段,以第一个段为例(第五第六行)有读/执行访问权限,地址开始于0x400000。
四、动态链接段表
图5.3.5动态链接段表
存放着动态链接的信息,例如名称、类型等。
五、重定位的节
图5.3.6重定位的节
我们可以从图中分析得出偏移地址、基址信息、链接器识别修改类型、重定位到目标的名称等。
六、符号表
图5.3.7符号表
5.4 hello的虚拟地址空间
图5.4.1edb加载hello
从0x00400000开始载入,节的排布与地址中相同
对照程序头说明:
程序头表中记录了运行时加载的内容,提供出各段的虚拟空间和物理内存的大小,读写权限、对其方式(之前已经说明)
以下是对程序头表中的各个类型进行分析
INTERP:表示动态链接器等
LOAD:从二进制文件映射到虚拟空间的段,保存了常量数据与代码等相关内容。
DYNAMIC:动态链接器的实用信息
NOTE:辅助信息
GNU_STACK:栈执行许可标志
GNU_RELRO:重定位后的内存只读区域。
5.5 链接的重定位过程分析
图5.5.1在shall 下生成可执行文件hello的反汇编文件
hello与hello.o的不同:
1.hello中多了.init 和.plt节
图5.5.2 新增的.init和.plt节
.init节定义了一个小函数,叫做_init,程序的初始化代码会用到它。而.plt节表示过程链接表,每一个plt条目都是一小段可执行代码。
2.hello中多了许多新函数
图5.5.3 新增的函数
这是新增的节中出现的函数
3.函数调用及一些跳转指令后接的是虚拟地址
图5.5.4跳转后接虚拟地址
因此分析得出,链接重定位的过程为:
一旦链接器完成了符号解析这一步,就把代码中的每个符号的引用和正好一个符号的定义关联起来,此时由于知道代码节数据节的确切的大小,因此链接器开始将所有相同类型的节合并为同一类型的新的聚合节,再把代码节和数据节中对每个符号的引用修改,使得他们指向正确的运行地址,这一过程依赖于重定位条目,而重定位条目是基于汇编器遇到的最终位置未知的目标引用时生成的。
5.6 hello的执行流程
执行过程为:从_start、_libc _start_main开始,接着执行主函数中的过程:_main、_printf、_exit、_sleep、_getchar;最后退出。
图5.6.1
根据上图并对照之前图片分析得到
子程序名以及地址:
_start0x4010f0
_libc_start_main 0x2f12271d
_main0x401125
_printf0x401040
_exit 0x401070
_sleep0x401080
_getchar 0x401050
5.7 Hello的动态链接分析
动态链接: 共享库是一个目标模块,在运行或加载中,可以加载到任意的内存地址,并且和内存中的程序链接起来的过程。动态库不需要像静态库一样,需要定期维护与更新,而且也不需要消耗大量的内存。
当程序调用一个由共享定义的函数时,编译器无法预测地址,我们可以通过延迟绑定的方式,将过程地址的绑定推迟到第一次调用该过程时。
延迟绑定通过GOT和PLT实现,GOT是数据段的一,PLT是代码短的一部分。接下来我们结合具体例子分许动态链接:
图5.7.1
由上图分析,GOT起始位置为0x404000
图5.7.2
然后在_start函数前设置断点,再次查看,发现0x404008和0x404010处的两个8字节的数据发生改变,分析得出0x7f445f3cf190和0x7f445f3b8ae0,这就是GOT[1]和GOT[2]的地址。
5.8 本章小结
详细分析了链接的概念以及作用,说明了shall命令行下的链接指令,并对hello的ELF格式各个部分进行了分析,还使用edb加载hello,查看本了进程的虚拟地址空间各段信息,并通过反汇编hello文件,将其与hello.o反汇编文件进行了对比,详细了解了重定位过程;此外,还遍历了整个hello的执行过程,在最后对hello进行了动态链接分析。
第6章 hello进程管理
6.1 进程的概念与作用
进程的概念:进程是操作系统对正在运行的程序的一种抽象,进程的定义是具有独立功能的一个程序关于某个数据集合的一次运行活动。进程会提供一种系统上好像只有某一个程序在运行,而且该程序看上去是独占地使用处理器、主存、I/O设备,该程序的代码和数据是系统内存中唯一的对象的假象。
进程的作用:给应用程序提供两个抽象:1. 一个独立的逻辑控制流2.一个私有地址
6.2 简述壳Shell-bash的作用与处理流程
作用:读取用户命令行,串联用户与内核,也就是说shall是用户与操作系统之间交互的窗口。
处理流程:1.用户对shall输入的命令进行解析
2.shall切分命令,判断类型并且据此获得参数
3.判断其是否为内置命令,是则执行
4.如果不是,fork一个子进程执行命令
5.子进程完毕后由父进程回收
6.3 Hello的fork进程创建过程
在终端输入./+可执行文件名,shell将会对此内容进行分析解读。判断出其为可执行文件后,shell将会调用fork创建一个新的子进程,然后在这个子进程中调用execve函数装入要执行的程序。产生的父进程和子进程是相互独立的,但是同时执行的。当子进程先于父进程结束,父进程负责子进程空间的回收工作并释放;如果晚于父进程结束,子进程变成孤儿进程,系统将自身负责回收孤儿进程。
6.4 Hello的execve过程
子进程创建成功之后,子进程调用execve函数在当前的上下文空间运行hello程序,具体步骤如下:
1.删除当前用户虚拟地址中的已存在区域结构
2. 为代码、数据、.bss和栈区域映射创建私有区域,并在写时进行复制。其中.bss区域和堆栈区域请求二进制零。
3.当存在与hello程序由共享对象链接的,如标准库libc.so,将其动态链接并虚拟共享到用户虚拟地址空间
4.设置程序计数器,使得下次运行时能从代码区域入口点进行。
6.5 Hello的进程执行
shell接收到可执行文件的执行命令,fork创建出一个子进程。此子进程将调用execve函数在当前进程的上下文中加载并运行刚才的可执行文件。加载器删除子进程现有的虚拟内存,并创建一块新的空间并初始化为0。然后,通过将虚拟地址空间中的页映射到可执行文件的页,代码段和数据段都会被设置为可执行文件的内容。
当hello调用getchar的时候,实际执行输入流是stdin的系统调用read函数,hello之前运行在用户模式,在进行read调用之后进入内核,内核中的陷阱处理程序请求来自键盘缓冲区的DMA传输,并且安排在完成从键盘缓冲区到内存的数据传输后,中断处理器。此时进入内核模式,内核执行上下文切换,切换到其他进程。当完成键盘缓冲区到内存的数据传输时,引发一个中断信号,此时内核从其他进程进行上下文切换回到hello进程。
6.6 hello的异常与信号处理
程序中可能出现的异常如下:1. 外部I/O设备引起的中断异常2.函数调用相关3.缺页故障 4.缓存器损坏造成的奇偶错误
1.正常停止:
图6.6.1进程以回车结束
2.不停乱按
图6.6.2
键盘中的输入进入缓冲区stdin,当getchar()的时候读出一个’\n’结尾的字串,当hello结束后stdin中的字符会被shall当作命令行输出,因此乱输入会出现在每次进程的内容之前输出,不会影响内容输出。
3.疯狂按回车
图6.6.3
每次键入一个回车键就会多空出一行并且会引起当前子进程结束,增加一次回到shell请求用户输入命令的进程
4.输入Ctrl C
图6.6.4
输入Ctrl C会让程序终止,通过ps观察得出结论
5. 输入Ctrl Z
图6.6.5
输入Ctrl Z程序不会终止仅是停止,也就是说hello程序被挂起,此时可以输入各种命令查看工作信息/杀死进程,程序仍可恢复运行。
图6.6.6
6.7本章小结
本阶段介绍了进程的概念和作用,介绍了shell的功能和工作机制,最后通过hello程序加强对信号处理的了解。
第7章 hello的存储管理
7.1 hello的存储器地址空间
结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。
7.2 Intel逻辑地址到线性地址的变换-段式管理
7.3 Hello的线性地址到物理地址的变换-页式管理
7.4 TLB与四级页表支持下的VA到PA的变换
7.5 三级Cache支持下的物理内存访问
7.6 hello进程fork时的内存映射
7.7 hello进程execve时的内存映射
7.8 缺页故障与缺页中断处理
7.9动态存储分配管理
Printf会调用malloc,请简述动态内存管理的基本方法与策略。
7.10本章小结
第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本章小结
结论
hello的经历了代码撰写、预处理、编译、汇编、链接、运行、创建子进程、加载、执行指令、访问内存、动态内存分配、发送信号、终止的过程。
(1) 预处理:将.c文件生成.i文件,将.c中调用的外部库展开合并到.i中
(2) 编译:由.i生成.s汇编文件
(3) 汇编:将.s文件翻译为机器语言指令,并打包成可重定位目标程序.o
(4) 链接:将.o可重定位目标文件和动态链接库链接成可执行目标程序hello
(5) 运行:在shell中输入命令
(6) 创建子进程:shell用fork为程序创建子进程
(7) 加载:shell调用execve函数,将hello加载到子进程,映射出虚拟内存
(8) 执行指令:CPU为进程分配时间片,加载器将计数器预置在程序入口点,则hello可以顺序执行自己的逻辑控制流
(9) 访问内存:MMU将虚拟内存地址映射成物理内存地址,CPU通过其来访问
(10) 动态内存分配:根据需要申请动态内存
(11) 信号:shell的信号处理函数可以接受程序的异常和用户的请求
(12) 终止:执行完成后父进程回收子进程,内核删除为进程创建的数据结 构
附件
列出所有的中间产物的文件名,并予以说明起作用。
hello.c:程序源代码
hello.i:预处理之后得到的修改了的文本文件
hello.s:编译器编译后得到的汇编语言文本文件
hello.o:汇编器生成的可重定位目标文件(二进制文件)
hello: 链接得到的可执行目标文件(二进制文件)
hello_elf1:可重定位文件生成的elf格式文件
hello_elf2:可执行文件生成的elf格式文件
asm1.txt: 可重定位文件反汇编生成的汇编语言文本文件
asm2.txt: 可执行文件反汇编生成的汇编语言文本文件
参考文献
[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.