种地 发表于 2024-6-26 17:45:22

HIT盘算机系统大作业——步伐人生

摘  要
本文将使用盘算机系统课程的知识对hello步伐的运行过程举行系统性先容,包括预处理、编译、汇编、链接等过程及进程管理、存储管理、I/O管理等方面。通过在linux+x86-64系统上逐步拆解hello步伐的运行,从而更是深入地理解盘算机系统。
关键词:盘算机系统、步伐、运行
目  录

第1章 概述
1.1 Hello简介
1.2 环境与工具
1.3 中心效果
1.4 本章小结
第2章 预处理
2.1 预处理的概念与作用
2.2在Ubuntu下预处理的命令
2.3 Hello的预处理效果解析
2.4 本章小结
第3章 编译
3.1 编译的概念与作用
3.2 在Ubuntu下编译的命令
3.3 Hello的编译效果解析
3.4 本章小结
第4章 汇编
4.1 汇编的概念与作用
4.2 在Ubuntu下汇编的命令
4.3 可重定位目标elf格式
4.4 Hello.o的效果解析
4.5 本章小结
第5章 链接
5.1 链接的概念与作用
5.2 在Ubuntu下链接的命令
5.3 可实行目标文件hello的格式
5.4 hello的虚拟所在空间
5.5 链接的重定位过程分析
5.6 hello的实行流程
5.7 Hello的动态链接分析
5.8 本章小结
第6章 hello进程管理
6.1 进程的概念与作用
6.2 简述壳Shell-bash的作用与处理流程
6.3 Hello的fork进程创建过程
6.4 Hello的execve过程
6.5 Hello的进程实行
6.6 hello的异常与信号处理
6.7本章小结
第7章 hello的存储管理
7.1 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动态存储分配管理
7.10本章小结
第8章 hello的IO管理
8.1 Linux的IO装备管理方法
8.2 简述Unix IO接口及其函数
8.3 printf的实现分析
8.4 getchar的实现分析
8.5本章小结
结论
附件
参考文献


第1章 概述

1.1 Hello简介

根据Hello的自白,利用盘算机系统的术语,简述Hello的P2P,020的整个过程。
P2P:hello由hello.c文件开始,先经过预处理过程,对宏界说、头文件等举行处理,文件格式从.c变成.i。之后在编译过程中,编译器举行常量表达式的盘算等工作,文件格式变成.s。然后在汇编阶段,汇编语言被转化为二进制代码,生成hello.o文件。最后在链接阶段,举行重定位等工作,最终得到步伐hello(可实行文件)
020:用户在shell壳中输入./hello命令后,系统调用Fork函数生成一个只有pid与父进程差别的子进程,并在子进程中调用evecve函数。evecve函数启动加载器loader,将原来的上下文等内容全部丢弃,并新建出task_struct及其目次的数据布局,用于映射内存中的私有地区和共享地区,然后设置步伐计数器到代码地区的入口点,使步伐开始运行。经过一系列的函数的调用、代码的实行,步伐运行竣事,成为僵尸子进程,等待被父进程回收。
1.2 环境与工具

列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开辟与调试工具。
硬件环境:Inter corei5处理器,2.5GHz,16GRAM
软件环境:Ubuntu
开辟及调试工具:CodeBlocks;vi/vim/gpedit+gcc
1.3 中心效果

列出你为编写本论文,生成的中心效果文件的名字,文件的作用等。
1 hello.c
作用:高级语言源步伐

2 hello.i
作用:预处理生成的.i文件

3 hello.s
作用:编译生成的.s文件

4 hello.o
作用:汇编生成的.o文件


5 hello.o.asm
作用:hello.o的反汇编格式,用汇编语言的格式来观察可重定位的目标文件

6 hello
作用:链接生成的可实行的目标文件

7 hello.asm
作用:hello的反汇编格式,用汇编语言的格式来观察可实行的目标文件
1.4 本章小结

本章从P2P、020的角度概括了hello的一生,并列出了开辟环境与工具,以及后续所有操作分析中所产生的中心产物。

第2章 预处理

2.1 预处理的概念与作用

概念:预处理一般是指在步伐源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。处理器根据以字符#开头的命令,修改原始的C步伐。通常包含以下几个方面:

[*]宏睁开:睁开所有的宏界说,并删除#define
[*]头文件睁开:将#include包含的文件插入到该指令位置
[*]条件编译:处理所有的条件预编译指令
[*]删除注释

作用:预处理使得编译器对代码的翻译更加方便

2.2在Ubuntu下预处理的命令

命令:gcc hello.c -E -o hello.i
https://img-blog.csdnimg.cn/direct/fee45c3079c94273ad9969d46ebe2a28.png
图2-1 预处理指令及编译得到的效果(.i文件)
2.3 Hello的预处理效果解析

https://img-blog.csdnimg.cn/direct/10f175c49323476ab59c8a9d7d7a9217.png
图2-2 预处理效果文件
可以看到,源文件仅30多行的代码在预处理后变成了3000多行,而其中只有最后一小部门是源文件的内容,别的均是在预处理过程中插入的内容。
同时,通过浏览.i文件可以看到,源文件开头的注释部门已被扫除,#开头的引用头文件部门也被扫除,而头文件已被插入对应位置。
2.4 本章小结

本章主要举行了预处理阶段的实行和分析。可以看出,预处理过程并不会对源文件的内容举行太多更改,只是对注释举行删除并插入头文件等。

第3章 编译

3.1 编译的概念与作用

概念:编译是指把用高级步伐设计语言书写的源步伐,翻译成等价的呆板语言格式目标步伐。
作用:开端翻译高级语言,将其转化为模式化的汇编指令,便于呆板实行步伐        

3.2 在Ubuntu下编译的命令

命令:gcc hello.i -S -o hello.s
https://img-blog.csdnimg.cn/direct/53d5ba60eda743f39c1eeea0486f693c.png
图3-1  编译指令及预处理得到的效果(.s文件)


3.3 Hello的编译效果解析

3.3.1 数据存储
(1)main函数中界说了一个未赋值的局部变量int i。局部变量会存在栈中,其生命周期与main函数自身相同。在主函数main的栈中,rsp向下移动了32个字节,其中就有给int i预留的空间。
https://img-blog.csdnimg.cn/direct/ef05301d92804d3a8603fbd82926c2a0.png
图3-2  main函数的栈

通事后续for循环对应的循环变量赋值语句可以得知,i存储在rbp向下的4个字节的位置。
https://img-blog.csdnimg.cn/direct/4aeb5c7f7a004629a9c571944da515e4.png
图3-3  循环变量赋值语句

(2)根据汇编代码,我们可以发现argc和argv分别存储在%edi,%rsi中,并在一开始首先分别生存到了-20(%rbp),-32(%rbp)的位置。
https://img-blog.csdnimg.cn/direct/6a0a505121d94128b74df3740711033e.png
图3-4  命令行参数存储
(3)字符串参数均在.text节中存储,而且各自有一个标号。
https://img-blog.csdnimg.cn/direct/5d1ad60e3c8f417ea3908ecc717042a9.png
图3-5  字符串存储
后续访问时,使用的是rip+段偏移量间接寻址
https://img-blog.csdnimg.cn/direct/4d2415b5d06d42ccbcdb563c22afdb4b.png
图3-6  间接寻址
3.3.2 赋值操作
在汇编语言下,赋值操作使用movl指令。
https://img-blog.csdnimg.cn/direct/687adc43697b481495d57fd2c2824293.png
图3-7  赋值操作
3.3.3 算术操作
该步伐中只有一个简单的算术操作,即循环语句中的i++。在汇编语言下,使用addl实现。
https://img-blog.csdnimg.cn/direct/e1331ad31c8e471ea040ce130c7c37e0.png
图3-8  算术操作
3.3.4 比力操作
该步伐中有两个比力操作,一个是!=,一个是<。
(1)!=在汇编语言中,使用cmp和je的组合举行实现。cmp命令仅设置标志位,je命令通过标志位举行判断。也即,cmp负责举行比力,而je则通过效果相等或不等举行对应的代码跳转。
https://img-blog.csdnimg.cn/direct/d3689f1a07dd43e4b3d4dcbc96720f4c.png
图3-9  !=的实现
(2)<在汇编语言中,使用cmp和jle的组合实现,实现过程与上述!=类似,cmp负责比力,jle根据效果举行跳转。
https://img-blog.csdnimg.cn/direct/af674b5821f8470482dd55aeb1af8608.png
图3-10  <的实现
3.3.5 循环操作
基于比力操作下的比力和代码跳转举行实现。
循环变量i初始值为0,与8举行比力,共循环8次。
https://img-blog.csdnimg.cn/direct/2f2d51744b79435593350dd6a50764b6.png
图3-11  循环操作

3.3.6 数组操作
数组的操作一般都是通过首所在加上偏移量得到的,汇编代码中可以观察到这种方式用在了取argv中的字符串的所在。argv数组中的内容存储在了栈中,我们从中取出对应的字符串的所在,并分别放到%rsi和%rdx中,作为printf的第二和第三个参数,最终输出到了屏幕上。
https://img-blog.csdnimg.cn/direct/944acf90c4bd48d0a2a6d53e99b59a0a.png
图3-12  数组操作
3.3.7 函数调用
函数调用在汇编中的实现很简单,就是使用call指令。
(1)printf函数
步伐中有两次调用printf函数。
第一次调用时,只有一个参数(字符串),被转化为了puts函数,使用寄存器%rdi传入。
https://img-blog.csdnimg.cn/direct/4843865bd96e4645897ce8d60d95e0d8.png
图3-13  第一次printf调用与传参

第二次调用时,共有字符串、argv、argv三个参数,分别通过寄存器%rdi、%rsi、%rdx传入。
https://img-blog.csdnimg.cn/direct/398f57c2cf6d4889a00bc32a6667ee5d.png
图3-14  第二次printf调用与传参
(2)exit函数
将1作为参数给寄存器%rdi传入
https://img-blog.csdnimg.cn/direct/c4ac40428ce04b2a8f230a585bdb7261.png
图3-15  exit调用与传参
(3)sleep函数与atoi函数嵌套
先分析里层的atoi函数,agrv作为参数给寄存器%rdi传入。
https://img-blog.csdnimg.cn/direct/b84974018add452293d086e37aa0b31d.png
图3-16  atoi函数调用与传参
再分析sleep函数。Atoi函数的返回值存入%rax中,再作为参数给寄存器%rdi传入sleep。
https://img-blog.csdnimg.cn/direct/7143d5b0abf74ad0981e3f18b15ff51b.png
图3-17  sleep函数调用与传参
3.3.8 函数返回
函数返回前通常会有如许几个操作:规复存储被调用者的寄存器的值、规复旧的帧指针%rbp(不一定有这个操作)、跳转到原来的控制流的所在。最终一般以ret指令结尾。
https://img-blog.csdnimg.cn/direct/e3e8a67ae2be4f9b96e98c5f540839f2.png
图3-18  函数返回


3.4 本章小结

本章主要分析了编译效果,详细解释了生成的汇编语言文件hello.s。主要设计到的操作有:数据存储、赋值操作、算术操作、比力操作、循环操作、数组操作、函数调用、函数返回等。

第4章 汇编

4.1 汇编的概念与作用

概念:汇编器将hello.s翻译成呆板语言指令,并将效果生存在呆板可以读懂的二进制文件即目标文件hello.o中。
作用:将汇编语言翻译成可重定位的二进制目标文件
4.2 在Ubuntu下汇编的命令

指令:as hello.s -o hello.o
https://img-blog.csdnimg.cn/direct/f844b22d33c54d61a185d4c6fdd04433.png
图4-1  汇编指令及汇编得到的效果(.o文件)
4.3 可重定位目标elf格式

4.3.1 ELF头
https://img-blog.csdnimg.cn/direct/412e8d64aff2454aab3f6eb08b7d9b95.png
图4-2  ELF头

ELF头以一个16字节的序列开始,该序列称为魔数,描述生成了该文件的系统的字的巨细和字节顺序。
剩下的部门包含资助链接器语法分析和解释目标文件的信息。其中包括ELF头的巨细、目标文件的范例、呆板范例、节头部表的文件偏移及其中条目的巨细和数目。

4.3.2 节头
https://img-blog.csdnimg.cn/direct/787c3776665342ccb472084370fbc153.png
图4-3  节头
在ELF头中,我们可以看到一共有13个节,而节头则展示了这些节更为详细的信息。
每一列分别表明白各个节的名称、巨细、范例、全体巨细、所在、旗标、链接、信息、偏移量和对齐。
4.3.3 重定位节
https://img-blog.csdnimg.cn/direct/08de4c0e0ffc4ba8ac70705a4dcfb096.png
图4-4  重定位节
‘.rela.text’节是text节的重定位信息,给出了偏移量、信息、寻址范例、符号值、符号名称还有addend的数值。因为还没有举行重定位,以是符号值一定都是0。
‘.rela.eh_frame’节是eh_frame节的重定位信息。
4.3.4 符号表
https://img-blog.csdnimg.cn/direct/b1ac8a83bccc471ab76354c6d7701373.png
图4-5 符号表
符号表记录了步伐中使用的各个符号的相关信息,各列分别展示了他们的编号、重定位值、巨细、范例、全局照旧局部、是否可见、是否被界说及名称。同样,因为还没有举行重定位,以是其重定位值均为0。
可以看到,puts、exit、printf等须要从外界调用的函数,此时处于未被界说的状态。
4.4 Hello.o的效果解析

(1)hello.o.asm(hello.o的反汇编代码文件)中有代码的所在且代码之间有顺序关系,而hello.s代码的没有位置信息温顺序关系。
(2)hello.asm代码跳转使用代码的所在,hello.s则使用段标号
https://img-blog.csdnimg.cn/direct/67179ee03abc4f10ae18bf6e0eced1ba.png
图4-6  代码跳转对比(.asm)

https://img-blog.csdnimg.cn/direct/1d805fb91b354c11997fa7ef30139912.png
图4-7  代码跳转对比(.s)
(3)hello.asm的函数调用是跳转到相应的所在,hello.s则使用call加函数名举行调用
https://img-blog.csdnimg.cn/direct/803887ef6b014a389a9c73c9b870c975.png
图4-8  函数调用对比(.asm)

https://img-blog.csdnimg.cn/direct/9afab594a3e5419ebbb52a6c7196d7b8.png
   图4-9  函数调用对比(.s)
4.5 本章小结

本章主要对汇编后的可重定向文件hello.o举行了分析,使用readelf和objdump工具查看hello.o中的信息。并将hello.o与hello.s举行对比,进一步理解二者的内容和关系。

第5章 链接

5.1 链接的概念与作用

概念:链接是将各种代码和数据片段收集并组合成为单一文件的过程。
作用:链接使步伐模块化编写成为大概,一个大型的步伐拆分成多个模块,分别举行编写、编译,最终通过链接得到须要的步伐。如许不但方便编写,后续步伐的维护和修改效率也会极大提高。
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
https://img-blog.csdnimg.cn/direct/c545a98f0b564ad6afe33348a920a935.png
图5-1  链接指令及编译得到的效果(可实行文件hello)
5.3 可实行目标文件hello的格式

hello的elf格式与hello.o的elf格式非常类似
5.3.1 ELF头
https://img-blog.csdnimg.cn/direct/42de935695384371a5e00460c1899c11.png
图5-2  ELF头
开头是16个字节构成的魔数,后续是ELF头的巨细、目标文件的范例、呆板范例、节头部表的文件偏移及其中条目的巨细和数目等信息。
5.3.2 节头
https://img-blog.csdnimg.cn/direct/3a429de322404f1b90f82c83aba6cb42.png
https://img-blog.csdnimg.cn/direct/8ff40711a3c340aeb7b9693e656cb8a0.png
图5-3  节头
每一列分别表明白各个节的名称、巨细、范例、全体巨细、所在、旗标、链接、信息、偏移量和对齐。
可以看到,因为此时已不须要举行重定位,以是已不存在记录重定位信息的‘.rela.text’和‘.rela.eh_frame’
5.3.3 步伐头
https://img-blog.csdnimg.cn/direct/3dbd946c79594029ac987509b555bc61.png
图5-4  步伐头

每个步伐头记录了一个内存段或为准备步伐实行而使用的内存的信息。而ELF文件的节与内存段并非逐一对应关系,一个内存段大概对应一个或多个节。
步伐头只对可实行文件或共享目标文件有意义,对于其它范例的目标文件而言,步伐头的信息可以忽略。
5.4 hello的虚拟所在空间

5.4.1 节所在
根据ELF文件中的节头表,可知各个节的起始所在,利用edb的Bookmarks即可对相应所在举行查找,看到各个节对应的汇编代码内容。
https://img-blog.csdnimg.cn/direct/8a14a85211b949628c1fbd9b1f0c0073.png
图5-5  节头表中’.interp’节的信息
https://img-blog.csdnimg.cn/direct/1727596769f64e8ab2324642e183a405.png
图5-6  利用edb看到的’.interp’节的汇编代码
5.4.2 数据段
根据ELF文件中的步伐头部门,可得到数据段的存储所在,而edb的data dump部门的出现了数据段的内容。
https://img-blog.csdnimg.cn/direct/a93ff7c1212047529bb8cb46d1a3c6f6.png
图5-7  步伐头中数据段的所在信息
https://img-blog.csdnimg.cn/direct/aab4ac3845b64bde86d7d330ac2f5d81.png
图5-8  edb中的data dump
5.5 链接的重定位过程分析

5.5.1 所在表现差别
在hello.o.asm中,所在使用的是内存中的绝对所在,利用相对偏移量举行表现。而链接之后的hello.asm中。所在使用的则是虚拟内存中的所在。
https://img-blog.csdnimg.cn/direct/b75120b91ed6449fa1382cb4ca2701c8.png
图5-9  hello.o.asm中使用相对偏移量表现绝对所在

https://img-blog.csdnimg.cn/direct/158835d8df944621ba388f120eb12001.png
图5-10  hello.asm中使用虚拟内存所在

5.5.2 函数调用表现差别
该步伐中所调用的几个函数均不是其本身界说的,均为界说在库文件中的函数。链接之前,系统找不到这些函数的界说,而链接后库文件与步伐组合为团体,系统就可以找到它们的界说。
表现在汇编代码中,就是hello.o.asm和hello.asm函数调用表现的差别。hello.o.asm中没有函数界说,调用只能通过跳转所在。hello.asm中有函数的界说,直接调用即可。
https://img-blog.csdnimg.cn/direct/d43f79bd35cb497bbba8ee8976555f3f.png
图5-11  hello.o.asm中的函数调用

https://img-blog.csdnimg.cn/direct/605cff6b8a1441cb8b2ea2df67189e39.png
图5-12  hello.asm中的函数调用

5.5.3 重定位过程分析
https://img-blog.csdnimg.cn/direct/13f6dc2eb1484617816b2da5571efc01.png
图5-13  重定位前

https://img-blog.csdnimg.cn/direct/b3acc773c8694b9aa621c9255f7a5886.png
图5-14  重定位后

https://img-blog.csdnimg.cn/direct/3c551ca0b34d4933b9535a0a9b918b13.png
图5-15  ‘.rodata’节的信息

根据节头表可知, ‘.rodata’节的所在为0x402000,偏移量为8。从hello.o.asm中可以看到,重定位使用的是PC相对引用的方式。在图中的lea命令发生时,PC值应当处于下一条命令的位置,即0x401145。通过盘算0x402000+0x8-0x401145得到效果为0xec3,即为lea中使用的虚拟内存所在。
5.6 hello的实行流程

1._dl_start
2._dl_init
3._cax_atexit
4._new_exitfn
5._libc_start_main
6._libc_csu_init
7._main
{
8._printf
9._atoi
10._sleep
}循环8次;
11._getchar
12._exit
13._dl_runtime_resolve_xsave
14._dl_fixup
15._dl_lookup_symbol_x
16.exit

5.7 Hello的动态链接分析

ELF使用的是一种叫做耽误绑定的技术,步伐调用由共享库界说的函数时,只有当这个函数在初次被用到时才会被绑定。hello步伐通过该技术和PLT和GOT进办法态链接。其中GOT中存放函数目标所在,PLT使用GOT中所在跳转到目标函数。
https://img-blog.csdnimg.cn/direct/19dadbc5b15146e3b3fe570e44aeba1f.png
图5-16  ‘.got’和‘.got.plt’节的信息

GOT是所在构成的数组,每个元素为8个字节。和PLT 使用进办法态链接时,GOT和GOT包含动态链接器在解析函数所在时会使用的各种信息;GOT是动态链接器在ld-linux.so模块中的入口点;别的元素分别对应一个步伐调用的函数,初次使用该函数时,其所在就会被解析。
https://img-blog.csdnimg.cn/direct/2f98dcc729b64363b296c6cde9132c37.png
图5-17  动态链接前GOT的内容

https://img-blog.csdnimg.cn/direct/efdbdf21660f4a698c908df56b4af7a1.png
图5-18  动态链接后GOT的内容
5.8 本章小结

本章主要对链接过程举行了先容,分析链接得到的可实行文件hello与重定位目标文件hello.o的区别。并使用edb对链接使用的虚拟内存及动态链接等过程举行可视化分析。

第6章 hello进程管理

6.1 进程的概念与作用

狭义界说:进程就是一段步伐的实行过程。
广义界说:进程是一个具有一定独立功能的步伐关于某个数据聚集的一次运行活动,它是操作系统动态实行的根本单位,在传统的操作系统中,进程既是根本的分配单位,也是根本的实行单位。

作用:进程为步伐提供两个关键抽象:一个独立的逻辑控制流,如同步伐独占处理器;一个私有的所在空间,如同步伐独占内存系统。这两个关键抽象,使得步伐可以或许高效的运行。
6.2 简述壳Shell-bash的作用与处理流程

作用:作为一个命令行解释器,shell壳为用户提供了一种更方便、更安全的与linux内核建立接洽的方式。同时,shell壳也能将步伐运行的效果输出,直观的反映给用户。

处理流程:
1.读取命令行作为输入
2.通过元字符对输入举行切割,将其分为一个个小的词元(token)。shell的元字符有:space,tab, newline,‘|’, ‘&’, ‘;’, ‘(’, ‘)’, ‘<’, or ‘>’。
3.将词元解析为命令
4.实行各种shell睁开(大括号睁开,波浪符睁开,参数睁开,命令替换,算术睁开,分词,文件名睁开)
5.举行命令所需的重定向
6.实行命令
(1)假如命令中含有/,则会实行对应路径下的步伐
(2)假如命令中没有/,则会判断其是否是shell的内置函数,若是则实行对应的操作
(3)假如不是内置函数,则会在PATH路径下举行查找
7.等待命令实行完毕
6.3 Hello的fork进程创建过程

https://img-blog.csdnimg.cn/direct/afda9bc6b86b41c48f78bc2a7b7b06a3.png
图6-1  进程开始实行的命令

在终端输入图中的命令后,回车将命令传入shell壳。此时,shell壳将命令拆分后,判断词元并非内置函数。然后找到hello步伐,将其存入内存。
之后实行fork()函数,创建一个子进程,其拥有和父进程完全相同的虚拟所在空间副本,相对父进程来说是独立的进程。二者有相同的代码段和数据段、堆,共享库和用户栈,区别在于pid的差别。
6.4 Hello的execve过程

fork生成的子进程会调用execve来实行hello步伐,该过程共有四步:
1.删除已存在的用户地区
2.映射私有地区,为hello步伐的代码、数据、.bss和栈地区创建新地区布局。
3.动态链接hello步伐,将其映射到共享地区
4.设置步伐计数器PC指向_start所在

6.5 Hello的进程实行

 时间片:一个进程实行它的控制流的一部门的每一时间段叫做时间片
进程上下文:上下文就是内核重新启动一个被抢占的进程所须要的状态,它由通用寄存器、浮点寄存器、步伐计数器、用户栈、状态寄存器、内核栈和各种内核数据布局等构成

步伐运行期间,会根据划分的时间片举行进程的转换,而发生进程转换时,一定会发生上下文切换,即生存当前进程的上下文、规复新进程的上下文、将控制权通报给新进程。
hello进程最初运行在用户模式中,直到其调用sleep函数,请求休眠,休眠时间由用户输入,此时便发生上下文切换。休眠时间竣事后,再次发生上下文切换,继承hello进程。


6.6 hello的异常与信号处理

6.6.1 正常运行
https://img-blog.csdnimg.cn/direct/3056e372e58c41d39be8166554babfd0.png
图6-2  正常运行
以三个参数运行hello步伐,步伐会举行8次循环输出,每次输出隔断2秒(即参数3,由学号%4盘算得到的秒数)。循环完毕后,再随意输入一个字符后步伐竣事。

6.6.2 运行过程中按ctrl+z

按ctrl+z,步伐停止运行,使用ps命令可以看到,此时步伐处于挂起状态。这是因为ctrl+z向shell壳通报了SIGTSTP信号,使步伐被挂起。
https://img-blog.csdnimg.cn/direct/f04291ee13ee422d8bc74b0c1551fdcd.png
图6-3  ctrl+z步伐停止

https://img-blog.csdnimg.cn/direct/4b7c0310a2f4443bb697db444899d00d.png
图6-4  fg使步伐在前台继承运行

之后输入fg,将背景步伐转到前台,通报信号SIGCONT,使步伐继承在前台运行。

6.6.3 ctrl+c

按ctrl+c,步伐停止运行,使用ps命令可以看到,此时hello步伐从步伐列表中被删除。这是因为ctrl+c向shell壳通报了SIGINT信号,使步伐被终止,并让父进程调用waitpid函数等待其子进程竣事并回收其子进程。
https://img-blog.csdnimg.cn/direct/c4a8022c45774df986f92b48408af00e.png
图6-5  ctrl+c步伐停止
6.6.4 jobs
步伐运行过程中,分别按ctrl+z和ctrl+c使步伐停止运行,然后输入jobs查看系统中目前存在的作业。
https://img-blog.csdnimg.cn/direct/697225c10818416bba41c23633485ac1.png
图6-6  jobs命令
今后也可以看出二者的差别:ctrl+c是使进程终止,同时从作业列表删除;ctrl+z则是使步伐停止运行,并在作业列表中标明
6.6.5 kill
步伐实行过程中,按ctrl-z将步伐挂起,并实行ps命令得到其pid。然后实行命令kill +pid,再实行一次ps发现hello进程仍然存在。但再实行fg 命令后提示“继承运行”、“终止”,再实行ps命令发现hello进程已经不复存在。
https://img-blog.csdnimg.cn/direct/42288b4e798046968dc9454cc7a1b601.png
https://img-blog.csdnimg.cn/direct/88df86c18c474fd289c242574140dda4.png
图6-7  kill命令
分析,kill命令仅会向父进程通报SIGINT信号使子进程终止,而不会使父进程使用waitpid函数等待子进程终止并回收,后者是由fg造成的。
6.6.6 pstree
步伐实行过程中,按ctrl-z将步伐挂起,然后输入pstree命令,可以看到进程之间的关系,即进程树。
https://img-blog.csdnimg.cn/direct/64ca2ca515b64c9ab908cf7587b23660.png
https://img-blog.csdnimg.cn/direct/cec723bd1d114d948d92c044b9d81773.png
https://img-blog.csdnimg.cn/direct/9b95ef49f1554009a95773070a594ada.png
https://img-blog.csdnimg.cn/direct/e2a73c2eceb34feeba518314fd0229bb.png
图6-8  进程树
6.6.7 乱按
步伐实行过程中乱按键盘,可以看到乱按的内容会立即输出,但不会对步伐自身实行造成影响。
https://img-blog.csdnimg.cn/direct/864b9ec2116c461b81f595b44a3aa179.png
图6-9  乱按
6.7本章小结

本章中主要先容了hello步伐运行过程中的进程管理的各个方面,包括从加载到运行再到运行时各种异常与信号处理的测试。首先分析白进程的概念,然后通过分析fork函数及execve函数,详细讨论了hello步伐运行过程中进程的创建及后续实行过程。最后对进程的异常实行与多种信号的处理举行测试。

第7章 hello的存储管理

7.1 hello的存储器所在空间

逻辑所在:逻辑所在指的是呆板语言指令中,用来指定一个操作数大概是一条指令的所在。一个逻辑所在,是由一个段标识符加上一个指定段内相对所在的偏移量,表现为 [段标识符:段内偏移量]。

线性所在:跟逻辑所在类似,它也是一个不真实的所在,假如逻辑所在是对应的硬件平台段式管理转换前所在的话,那么线性所在则对应了硬件页式内存的转换前所在。(在没开启分页功能的情况下线性所在就等于虚拟所在)

虚拟所在:这是对整个内存(不要与呆板上插那条对上号)的抽像描述。它是相对于物理内存来讲的,可以直接理解成“不直实的”,“假的”内存。进程使用虚拟内存中的所在,由操作系统协助相关硬件,把它“转换”成真正的物理所在。

物理所在:用于内存芯片级的单位寻址,与处理器和CPU毗连的所在总线相对应。假如是读,电路根据这个所在每位的值就将相应所在的物理内存中的数据放到数据总线中传输。假如是写,电路根据这个所在每位的值就在相应所在的物理内存中放入数据总线上的内容。物理内存是以字节(8位)为单位编址的。

7.2 Intel逻辑所在到线性所在的变动-段式管理

一个逻辑所在由两部份构成,[段标识符:段内偏移量]。
段标识符(也叫段选择符)是由一个16位长的字段构成,称为段选择符。其中前13位是一个索引号,后面3位包含一些硬件细节。
https://img-blog.csdnimg.cn/direct/a062f1fc1408427c888d1e820246d4e9.png
图7-1  段标识符的布局

通过段标识符中的索引号从GDT大概LDT找到该段的段描述符,段描述符中的base字段是段的起始所在。一些全局的段描述符,就放在“全局段描述符表(GDT)”中,一些局部的,例如每个进程自己的,就放在所谓的“局部段描述符表(LDT)”中。GDT在内存中的所在和巨细存放在CPU的gdtr控制寄存器中,而LDT则在ldtr寄存器中。
首先,给定一个完整的逻辑所在[段选择符:段内偏移所在],
(1)看段选择符的T1=0照旧1,知道当前要转换是GDT中的段,照旧LDT中的段,再根据相应寄存器,得到其所在和巨细。我们就有了一个数组了。
(2)拿出段选择符中前13位,可以在这个数组中,查找到对应的段描述符,如许,就得到了基所在。
(3)段起始所在+ 段内偏移量 = 线性所在。
7.3 Hello的线性所在到物理所在的变动-页式管理

CPU的页式内存管理单位,负责把一个线性所在,最终翻译为一个物理所在。
线性所在被分为以固定长度为单位的组,称为页(page),例如一个32位的呆板,线性所在最大可为4G,可以用4KB为一个页来划分,如许,整个线性所在就被划分为一个tatol_page的大数组,共有2的20个次方个页。
另一类“页”,我们称之为物理页,大概是页框、页桢的。是分页单位把所有的物理内存也划分为固定长度的管理单位,它的长度一般与内存页是逐一对应的。
每个进程都有自己的页目次,当进程处于运行态的时间,其页目次所在存放在cr3寄存器中。每一个32位的线性所在被划分为三部门,[页目次索引(10位):页表索引(10位):页内偏移(12位)]
依据以下步调举行转换:

[*]从cr3中取出进程的页目次所在(操作系统负责在调度进程的时间,把这个所在装入对应寄存器);
[*]根据线性所在前十位,在数组中,找到对应的索引项,因为引入了二级管理模式,页目次中的项,不再是页的所在,而是一个页表的所在。(又引入了一个数组),页的所在被放到页表中去了。
[*]根据线性所在的中心十位,在页表(也是数组)中找到页的起始所在;
[*]将页的起始所在与线性所在中最后12位相加。
https://img-blog.csdnimg.cn/direct/6a4eee4b4476436aa03c15f60e3f1749.png
图7-2  线性所在到物理所在变动
7.4 TLB与四级页表支持下的VA到PA的变动

变动步调如下:

[*]首先查看Virtual Address 的高16位VA是否为全0,假如全0,使用TTBR0_EL1寄存内放的Level 0 Page Table的基所在; 否则,使用TTBR1_EL1
[*]由于是4K的页表,4K页表的巨细是如许的盘算的: 4KB = 1024 × 8 × 4 = 512 × 64 bit. 就是说4K 的页表要分为512个Entry, 每个Entry的巨细为64bit。每个Entry存放的数据,实际是下一个level 的转换表的所在。对于某个VA, 使用VA来索引。如许就可以找到第二级转换表(Level 1 page table)的首所在
[*]同样,Level 1 page table 也是4K 共512 个Entry,每个Entry 存放下一个页表的首所在,这个首所在的存放的位置要用VA去索引Level 1 page table的Entry 得到. 样就可以找到第三级转换表(Level 2 page table)的首所在
[*]同样,Level 2 page table 也是4K 共512 个Entry,每个Entry 存放下一个页表的首所在,这个首所在的存放的位置要用VA去索引Level 1 page table的Entry 得到. 样就可以找到第四级转换表(Level 3 page table)的首所在
[*]Level 3 page table 内存放的就是VA 向 PA转换的Descriptor了, 也是512个entry,每个Entry 存放64bit的数据。通过VA来索引使用那个Entry的Descriptor。在这个Descriptor中就可以得到我们想要的物理所在的 PA
[*]最终的所在转换完成,VA 转换为 PA = {来自Level 3 转换表的PA, VA}. 就是Descriptor 中给出物理所在的,而虚拟所在给出物理的值的
https://img-blog.csdnimg.cn/direct/4eb285e5bbfa47d88670db4ed1c37e91.png
图7-3  VA到PA的变动

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

PA被分为了CT、CI、CO分别是标志位、组号和偏移量。首先我们根据组号在L1cache中找到对应的组,然后挨个比力标志位,假如标志位对应且有用位为1,则分析发生了hit,然后根据CO偏移量得到我们想要取的数据。假如发生了miss,则依次到L2cache、L3cache、主存中去找。
https://img-blog.csdnimg.cn/direct/d27a15630de74facbab39437106b28d5.png
图7-4  三级Cache下的物理内存访问

7.6 hello进程fork时的内存映射

当fork函数被当前进程调用时,内核为新进程创建各种数据布局,并分配给它一个唯一的pid。为了给这个新进程创建虚拟内存,它创建了当前进程的mm_struct、地区布局和页表的原样副本。将两个进程中的每个页面都标志为只读,并将两个进程中的每个地区布局都标志为私有的写时复制。
当fork从新进程返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当这两个进程中的任一个厥后举行写操作时,写时复制机制就会创建新页面,因此,也就为每个进程保持了私有所在空间的概念。
7.7 hello进程execve时的内存映射

1.删除当前进程虚拟所在的用户部门中的已存在的地区布局
2.映射私有地区,为hello步伐的代码、数据、.bss和栈地区创建新地区布局。所有这些新的地区都是私有的、写时复制的
3.动态链接hello步伐,将其映射到用户虚拟所在空间中的共享地区
4.设置步伐计数器PC指向_start所在

7.8 缺页故障与缺页停止处理

假设MMU在试图翻译某个虚拟所在A时,出发了一个缺页。这个异常导致控制转移到内核缺页处理步伐,处理步伐随后就实行下面的步调:

[*]判断虚拟所在A是否合法。异常处理步伐搜索地区布局的链表,将A与每一个地区布局的头和尾相比力,判断A是否属于某个地区布局。假如没有匹配到任何效果,分析所在A是不合法的,于是报出段错误。
[*]判断举行的内存访问是否合法。假如试图举行的访问是不合法的,那么缺页处理步伐会触发一个保护异常,从而终止这个进程。
[*]假如缺页故障是对合法所在举行合法访问时出现的,就开始处理缺页。内核会选择一个捐躯页面,假如这个捐躯页面被修改过,那么就将它交换出去,换入新的页面并更新页表。当缺页处理步伐返回时,CPU重新启动引起缺页的指令,这条指令将再次发送A到MMU,此时,MMU就能正常的翻译A了。
7.9动态存储分配管理

动态内存分配器维护着一个进程的虚拟内存地区(堆),分配器将堆视为一组差别巨细的块的聚集来维护,每个块就是一个一连的虚拟内存片,要么是已分配的,要么是空闲的。分配器有两种风格,即显式分配器和隐式分配器。
显式分配器,要求应用显式地释放任何已分配的块。C标准库提供一种叫做malloc步伐包的显式分配器。C步伐通过调用malloc函数来分配一个块,并通过调用free函数来释放一个块。内核通过调用sbrk函数扩展和紧缩堆。
隐式分配器,要求分配器检测一个已分配块何时不再被步伐所使用,那么就释放这个块,也叫垃圾收集器。
7.10本章小结

本章主要讨论了hello步伐涉及到的存储管理方式。首先先容了逻辑所在、线性所在、虚拟所在、物理所在四种所在空间,又详细分析白其之间的转换方式,即段式管理、页式管理。此外,本章也分析白hello进程实行过程中fork函数与execve函数的内存映射。同时,也详细叙述了基于MMU的判断缺页异常原因的方式以及对缺页故障的处理机制。最后又对动态存储分配管理机制举行了简单先容。

第8章 hello的IO管理

8.1 Linux的IO装备管理方法

装备的模型化:文件
inux 把装备看成一种特别文件整合到文件系统中,一般通常位于 /dev 目次下。可以使用与平凡文件相同的方式来对待这些特别文件。
特别文件一般分为两种:
块特别文件是一个能存储固定巨细块信息的装备,它支持以固定巨细的块,扇区或群集读取和(可选)写入数据。每个块都有自己的物理所在。所有传输的信息都会以一连的块为单位。块装备的根本特性是每个块都较为对立,可以或许独立的举行读写。常见的块装备有硬盘、蓝光光盘、USB 盘。与字符装备相比,块装备通常须要较少的引脚。
另一类特别文件是字符特别文件。字符装备以字符为单位发送或吸收一个字符流,而不考虑任何块布局。字符装备是不可寻址的,也没有任何寻道操作。常见的字符装备有打印机、网络装备、鼠标、以及大多数与磁盘差别的装备。
装备管理:unix io接口
每个装备特别文件都会和装备驱动相关联。每个驱动步伐都通过一个主装备号来标识。假如一个驱动支持多个装备的话,此时会在主装备的后面新加一个次装备号来标识。主装备号和次装备号共同确定了唯一的驱动装备。在盘算机系统中,CPU 并不直接和装备打交道,它们中心有一个叫作 装备控制器(Device Control Unit)的组件,例如硬盘有磁盘控制器、USB 有 USB 控制器、显示器有视频控制器等。这些控制器就像署理商一样,它们知道怎样应对硬盘、鼠标、键盘、显示器的举动。
8.2 简述Unix IO接口及其函数

1.int open(const char *pathname,int flags,mode_t mode)(该函数有两种情势,别的一种比力常用,int open(const char *pathname,int flags))
作用:打开一个存在的文件或是创建一个新文件
参数:
(1)pathname:打开文件的路径
(2)flags:打开文件的方式

常用的flag:
O_RDONLY(只读)  
O_WRONLY(只写) 
 O_RDWR(读写)  
O_CREATE(假如文件不存在就创建)
O_TRUNC(假如文件存在就清空内里的内容)
O_APPEND(以追加的方式打开文件)

 (3)mode:假如文件被新建,指定其权限
返回值:乐成:return 文件描述符;  失败:return -1

2.int close(int fd) 
作用:关闭某个打开的文件
参数: fd:文件描述符
返回值:乐成:return 0;  失败:return -1

3.ssize_t read(int fd,void *buf,size_t count)
作用:读取文件fd的内容
参数:

[*]fd:文件描述符
[*]buf:存放读到字符的缓冲区
[*]count: 要读多少字节
返回值:乐成:return 乐成读到的字符个数;  失败:return -1

4.ssize_t write(int fd,const void*buf,size_t count)
作用:向文件fg写入内容
参数:

[*]fd:文件描述符
[*]buf:存放将要写入字符的缓冲区
[*]count: 要写多少字节
返回值:乐成:return 乐成写入的字符个数;  失败:return -1



5.off_t  lseek(int fd,off_t offset,int whence)
作用:主要用于调解文件位置
参数:

[*]fd:文件描述符
[*]offset:新位置相对于基准点的偏移
[*]whence:基准点、
返回值:乐成:return 新文件的偏移量;  失败:return -1
8.3 printf的实现分析

printf函数的原型如下:
int printf(const char *fmt, ...)
{
int i;
char buf;
va_list arg = (va_list)((char*)(&fmt) + 4);
i = vsprintf(buf, fmt, arg);
write(buf, i);
return i;
}
((char*)(&fmt) + 4)表现的是可变参数中的第一个参数的所在。

从vsprintf生成显示信息,vsprintf的作用是格式化。
以下是vsprintf的函数体:

int vsprintf(char *buf, const char *fmt, va_list args)
{
char* p;
char tmp;
va_list p_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);
}
这个函数返回的是要打印的字符串的长度。
接下来要调用write函数,我们反汇编追踪一下:
write:
mov eax, _NR_write
mov ebx,
mov ecx,
int INT_VECTOR_SYS_CALL
我们可以找到INT_VECTOR_SYS_CALL的实现
init_idt_desc(INT_VECTOR_SYS_CALL,DA_386IGate,sys_call,PRIVILEGE_USER),表现要通过系统来调用sys_call这个函数。
ys_call的实现:
    sys_call:
     call save
     push dword
     sti
     push ecx
     push ebx
     call
     add esp, 4 * 3
     mov , eax
     cli
     ret
然后实行字符显示驱动子步伐:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
最后显示芯片按照革新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析

getchar()是stdio.h中的库函数,它的作用是从stdin流中读入一个字符,也就是说,假如stdin有数据的话不用输入它就可以直接读取了。第一次调用getchar()时,确实须要人工的输入,但是假如你输了多个字符,以后的getchar()再实行时就会直接从缓冲区中读取了。
getchar函数的原型如下:
int getchar(void)
{
static char buf;
static char* bb=buf;
static int n=0;
if(n==0)
{
n=read(0,buf,BUFSIZ);
bb=buf;
}
return (–n>=0)?(unsigned char)*bb++:EOF;
}
异步异常-键盘停止的处理:键盘停止处理子步伐。担当按键扫描码转成ascii码,生存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到担当到回车键才返回。
8.5本章小结

本章简要总结了unix I/O的有关知识,先容了Linux对I/O装备的管理机制及Unix I/O的接口和其常用函数。又根据源码详细分析了printf和getchar函数。


结论

hello步伐从诞生到竣事会经历很多过程,一个简单的步伐的实行过程却囊括了盘算机系统课程险些一个学期所学的内容。
首先,hello步伐生成的过程经历五个步调:

[*]将代码从键盘输入,得到hello.c文件
[*]hello.c经过预处理,处理了#开头的行,包括宏界说、文件包含和条件编译,得到hello.i文件
[*]hello.i通过编译转化为汇编指令,得到hello.s文件
[*]hello.s由汇编过程转化为二进制文件,得到hello.o文件
[*]hello.o在链接过程中举行符号解析和重定位,最终得到可实行文件hello
而后,hello步伐在实行过程中,又设计到进程管理、存储管理、I/O管理。
要实行hello步伐,须要借助shell壳。通过输入实行命令及命令行参数,使shell壳通过fork创建一个子进程,然后,操作系统使用execve,在当前子进程的上下文中加载并运行hello步伐。在步伐实行过程中,又会涉及到实行异常与信号处理。
在hello步伐实行过程中,须要对内存举行访问。MMU将步伐中使用的虚拟内存所在通过页表映射成物理所在。printf会调用malloc向动态内存分配器申请堆中的内存。通过追踪hello实行时内存访问的过程,我们也对盘算机系统的存储管理机制举行了简要的叙述。
hello步伐的实行效果须要显式的输出到屏幕上,而其实行过程中我们也可以人为从键盘输入一些指令,向系统通报对应的信号,进而影响到hello步伐的实行。这些都与I/O操作相关。linux的I/O管理机制,会把所有的外部I/O装备模型化为一个文件,对装备的操作就可以等价为对文件举行读写等操作。
当hello步伐彻底实行完毕后,它会以僵尸子进程的情势继承存在,也继承占用着一部门资源。直到其父进程将其回收,它才算是彻底消散。

通过追踪hello步伐重新到尾的整个生命流程,我最大的感悟就是盘算机系统各个过程间密切的相关性。书本上、讲堂上的知识是以割裂的情势学习的,而如许一个对简单步伐的完整生命周期的观察,将学到的盘算机系统各部门内容在脑中紧密联合了起来了,也让我对盘算机的精妙与复杂有了进一步的认识。

附件

1 hello.c
作用:高级语言源步伐

2 hello.i
作用:预处理生成的.i文件

3 hello.s
作用:编译生成的.s文件

4 hello.o
作用:汇编生成的.o文件

5 hello.elf
作用:hello.o的elf格式,用于展示可重定位的elf文件格式

6 hello.asm
作用:hello.o的反汇编格式,用汇编语言的格式来观察可重定位的目标文件

7 hello
作用:链接生成的可实行的目标文件

8 hello_exe.elf
作用:hello的elf格式,用于展示可实行的elf文件格式

9 hello_exe.asm
作用:hello的反汇编格式,用汇编语言的格式来观察可实行的目标文件

参考文献


[*] Randal E.Bryant ,David R.O’Hallaron. 《深入理解盘算机系统》.机械工业出书社.
[*]预处理、编译、汇编和链接_已知hello.h和hello.c两个文件,按所需命令写在下划线上-CSDN博客
[*]进程管理(一)--进程管理的根本概念_进程管理之(一)-CSDN博客
[*]深入理解Linux内核信号处理机制原理(含源码解说) - 知乎
[*]逻辑所在、物理所在、虚拟所在_虚拟所在 逻辑所在-CSDN博客
[*]【ARM-MMU】ARMv8-A 的4K页表四级转换(VA -> PA)的过程-CSDN博客
[*]浅析 Linux 中的 I/O 管理 - 知乎
[*]linux系统io常用接口函数-CSDN博客
[*]https://www.cnblogs.com/pianist/p/3315801.html


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: HIT盘算机系统大作业——步伐人生