民工心事 发表于 2024-7-21 09:48:23

HITICS大作业--------步伐人生


摘  要
本文将围绕hello的整个生命周期,详细介绍步伐的预处理惩罚、汇编、编译、链接过程,以及系统的进程管理、存储管理、I/O管理等方面。本文将用详细的例子来阐述这些概念,以及概念背后的原理

关键词:Hello;进程;Shell;系统                            









目  录

第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.c P2P的全过程:步伐预处理惩罚----------->编译---------------->汇编----------------->链接-------------->生成可实行文件------------------>调用fork函数创建进程----------------->使用execve函数加载进程
1.2 环境与工具

软件环境:Ubuntu 22.04 LTS
硬件环境:12th Gen Intel(R) Core(TM) i7-12700H   2.30 GHz
工具:VSCode、vim、gdb、gcc、edb、objdump、readelf、Shell(Bash)
1.3 中间效果

https://img-blog.csdnimg.cn/direct/6cbf9c8d654443fe859f50998b24210c.png
图1 中间产物
Makefile:简化一些过程使用的文件,如编译、预处理惩罚、实行等等。
Hello.i:hello.c预处理惩罚的产物
Hello.s:hello.i编译后的产物
Hello.o:hello.s汇编形成的产物
Hello:链接形成的产物
Hellodump.txt:hello.o反汇编(OBJDUMP,下同)形成的产物
Helloelf2.txt:hello反汇编产物。
Helloelf.txt:对hello.o使用readelf的产物

1.4 本章小结

本部分简要介绍了hello有关的基本情况,中间产物等。


第2章 预处理惩罚

2.1 预处理惩罚的概念与作用

预处理惩罚的概念:根据原始C步伐中以#开头的语句(宏),对源代码进行更换、插入、删除等一系列利用,并终极生成.i文件(预处理惩罚文件)。如将所有包含#define背面的别名的语句全部更换为其代表的文本,大概将#include 对应的头文件内容全部插入到C步伐中。
预处理惩罚的作用:将宏界说更换为详细的变量名大概函数体,大概针对不同的宏界说选取不同的代码,终极形成完整的预处理惩罚文件。
如编译器将会把步伐中与#define背面的宏命名相同的名称全部更换成该宏界说的内容,直至遇到第一个#undef。
又如编译器会根据#if、#elif等条件编译语句后的条件来弃取其包罗的代码。
再如编译器会将#include背面的头文件内部代码全部拷贝进.c文件中。

2.2在Ubuntu下预处理惩罚的下令

下令如下所示:
https://img-blog.csdnimg.cn/direct/cb0e38ff85ea49ebbdf780a8e696270b.png
图2 预处理惩罚下令
终极生成的文件如下所示:
https://img-blog.csdnimg.cn/direct/5e9534f7a8cd487ca49cec15b6f770ad.png
图3 生成产物

2.3 Hello的预处理惩罚效果解析

Hello.c中包含三个头文件:<stdio.h>、<unistd.h>和<stdlib.h>。在预处理惩罚后,原来的宏界说(#include)被更换,这三个头文件的内容被复制进了hello.i中。如下图所示:
https://img-blog.csdnimg.cn/direct/7995ca8a71e74ed588b9039bcb4c46dc.png
https://img-blog.csdnimg.cn/direct/5724c7da611a4dccb0e70feef705b22c.png
图4-图5 预处理惩罚后的步伐
原hello.c剩余内容如下:(最底部)
https://img-blog.csdnimg.cn/direct/e8ffaec224204176a5d8aa8b09300b68.png
图6 预处理惩罚后原代码部分
2.4 本章小结

预处理惩罚阶段,hello.c中的宏被编译器处理惩罚,颠末一系列的更换、添加、删除等利用,终极生成了hello.i文件,为接下来的汇编等工作做好前期准备。

第3章 编译

3.1 编译的概念与作用


编译是指将预处理惩罚文件(.i)经编译器翻译为汇编文件(.s)的过程。根据不同的优化选项(-Og、-Ox(x=1,2,3)),终极编译产生的汇编文件也有所不同。别的,不同的呆板、不同的利用系统,其编译出来的汇编文件也有所不同。
编译的作用:将高级语言翻译为呆板语言,使之可以大概更好地被呆板所理解并实行。        

3.2 在Ubuntu下编译的下令

编译下令如下图所示:
https://img-blog.csdnimg.cn/direct/9cb11f2502b44392aa3408b025d64184.png
图7 编译下令
编译后如图:
https://img-blog.csdnimg.cn/direct/2fdde2e0d1f6466a87bc71d94c811e42.png
图8 生成产物
3.3 Hello的编译效果解析

hello.i被编译为hello.s后,其代码被翻译为汇编语句。hello.s的汇编语句中包含如下成分:
3.3.1 常量
字符串常量”Hello %s %s”和“用法: Hello 学号 姓名 秒数”(以Unicode情势存储)存放于如图所示区域:
https://img-blog.csdnimg.cn/direct/a8e974d631a44433840c6caa770fce3b.png
图10 常量
汇编中.string字段表明将部分空间开辟给该字符串。
3.3.2 局部变量
局部变量i被存放在寄存器%ebp中,并在循环中被调用:
https://img-blog.csdnimg.cn/direct/bb9f90bf9e07444b9ef6befe29e6b93b.png
《----------https://img-blog.csdnimg.cn/direct/fcda3add9d4a4418998999072f256157.png
图11 局部变量
3.3.3 赋值利用
局部变量i在上述循环中被赋予初值0。相关汇编代码如下所示:
https://img-blog.csdnimg.cn/direct/0415108995184cb0bb17b1de880b1cc3.png
图12 赋值利用
该段代码将立刻数0传送给%ebp地点内存位置,并把高32位设置为0。
3.3.4 算术利用
上述循环对局部变量i进行了自增利用。对应汇编代码如下:
https://img-blog.csdnimg.cn/direct/8db92e0946d54bb493657311a0047450.png
图13 自增利用
3.3.5 关系利用
在条件分支【if(argc!=4)】中出现了比较是否相称利用。相关汇编代码如下:
https://img-blog.csdnimg.cn/direct/bb0152f202ac49e0b25c6083eebbaf02.png
图14 关系利用
该部分将立刻数4和存放于%edi的argc比较,若不相称则跳转到.L6段调用exit函数(jne==jump if not equal,条件跳转指令),相称则继续实行下列利用。
在循环体【for(i=0;i<8;i++)】中出现了比较巨细的利用。相关汇编代码如下所示:
https://img-blog.csdnimg.cn/direct/f6286ff6d39548f39cfac9a7fea21da1.png
图15 比较利用
该段代码将立刻数7与存储于%ebp的数i进行比较。若i<=7(jle,jump if less than or equal to)则跳转到.L3部分,否则继续实行下列利用。注意到此处与原代码不同的是,编译器将i<8翻译为了i<=7。
3.3.6指针利用
Hello.c中使用了字符串数组argv,并在以下语句中出现相关调用:
Argv数组的基址一开始存储于%rsi寄存器中,后移入%rbx寄存器:
https://img-blog.csdnimg.cn/direct/609940ea396c414ab92ab2051ac7d6d3.png
图16 基址移动
而后,printf函数调用了argv、argv:
https://img-blog.csdnimg.cn/direct/138f75b86be14d20847a81a56aac88db.png
图17 参数通报
以上语句是将位于%rbx+0x16处的值(argv的起始地点)传送给了寄存器%rcx(第四个参数),将%rbx+0x8处的值(argv的起始地点)传送给了%rdx(第三个参数)。
最后,atoi函数调用了argv,传值方式同上:
https://img-blog.csdnimg.cn/direct/e714f2f7dae849d6b78a2d75b1ef5476.png
图18 参数通报2
3.3.7控制转移

[*]条件语句
原代码条件语句如下所示
https://img-blog.csdnimg.cn/direct/ce8ec5b6e4454b859a57a89593dfab5e.png
图19 原代码条件语句
该条件语句判断argc是否等于4,若不等于4则实行代码块内内容。对应汇编代码如下所示:
https://img-blog.csdnimg.cn/direct/5fbbc7dce7634c00a223bf5e2b0d755e.png
图20 汇编代码的条件语句
此处先比较%edi的值和4的巨细(cmpl $4,%edi,argc在%edi中),若不相称则跳转(jne .L6)到.L6段代码(即代码块内的内容)。此处使用了条件跳转指令jne和比较指令cmpl来实现该条件分支。

[*]循环语句
原代码的循环语句如下图所示:
https://img-blog.csdnimg.cn/direct/8b7d44db202e4054b80c39de7e5266f0.png
图21 原代码的循环语句
该循环语句共实行8次循环体内部内容。其对应的汇编代码使用了下列布局:
https://img-blog.csdnimg.cn/direct/b48f0bfd5b924af89fca8754c51291dd.png
图22 汇编代码的循环语句
若上述的条件判断为假,代码会跳转到.L2段。此时会先比较%ebp中的值(i)是否小于等于7,若满意条件则跳转到循环体内部代码(.L3段)。在.L3段的最后,使用了addl指令实现了i++的利用。
由上述可知,条件分支和循环语句的实现离不开判断语句(cmp、test等)和条件跳转语句(jne、jle等)。
3.3.8函数调用

[*]函数参数通报
主函数main中共有两个参数:argc(存放于%rdi中)和argv(存放于%rsi中)。这两个参数由下令行通报,故其通报过程无法在hello.s中表现。
Hello.c中一共有四个函数有参数通报过程:printf、exit、atoi和sleep。
Printf参数通报:
第一处:

https://img-blog.csdnimg.cn/direct/f48246a23e7d49f98c23ec20f993e0f4.png
图23 第一处printf
对应汇编代码:
https://img-blog.csdnimg.cn/direct/b598ea4f82ea4bc784e6700d910740f5.png
图24 汇编代码
该处把存储于.LC0段的字符串常量通报给了%edi(第一个参数)。注意到此处使用了puts函数代替了printf函数。
第二处:
https://img-blog.csdnimg.cn/direct/feaa45893d294f5e8437bb3f296da79a.png
图25 第二处printf
对应汇编代码:
https://img-blog.csdnimg.cn/direct/88823f0ebec44c5484a1ab0d644de5d8.png
图26 汇编代码
该段代码将函数体内的三个参数分别传给了%esi、%edx和%ecx。这之后调用了__printf_chk函数实行字符串的打印。
Exit函数部分:
https://img-blog.csdnimg.cn/direct/5b255779e59a4fa98cd6c2612448dc34.png
图27 exit函数
汇编代码:
https://img-blog.csdnimg.cn/direct/34fcb4371959424aa0b2ac31708e5211.png
图28 exit调用对应的汇编代码
参数1传给了%edi后调用了exit函数。
Atoi函数和Sleep函数部分:
https://img-blog.csdnimg.cn/direct/a181c9da0eeb43ce878ddc4f46dda7a6.png
图29 sleep和atoi函数
汇编代码:
https://img-blog.csdnimg.cn/direct/1e48e0412ff841c89d55843371f77a05.png
图30 sleep和atoi函数的汇编代码
Atoi函数首先将参数通报给%rdi,调用该函数,而后Atoi的返回值再赋给sleep(%eax->%edi)的参数,调用sleep。

[*]函数调用
由上述可知,所有调用函数的利用均使用了call语句。

3.4 本章小结

.i文件需要先编译为.s文件才能进行后续的利用。相比.i文件,.s文件更接近呆板语言,更容易被呆板实行。


第4章 汇编

4.1 汇编的概念与作用


汇编的概念:汇编即将汇编文件.s翻译为可重定向二进制文件.o的过程
汇编的作用:将汇编语言翻译为呆板码,使之可以大概更好地被呆板所理解。同时,这一利用使得文件可以被链接,为后续的链接工作做好准备。
4.2 在Ubuntu下汇编的下令

下令如下:
https://img-blog.csdnimg.cn/direct/136473b580a44b54b367ecd6d27931f7.png
图31 汇编下令
4.3 可重定位目标elf格式

一样平常elf文件的格式如下:
https://img-blog.csdnimg.cn/direct/0ba91bf9683c4f669011615509827666.png
图32 ELF文件的基本格式
通过对hello.o使用readelf得到以下内容:
首先是elf头:
https://img-blog.csdnimg.cn/direct/73dd891eb0fa43a09c8a6ac103170629.png
图33 ELF头
通过elf头可以知道许多信息,比方系统为64位(Class)、使用小端法(Data)、呆板类型、版本号等等。
接下来是各节的头(Section Header),这一部分会生存各个节起始位置的地点、名称、巨细、偏移量等信息。
https://img-blog.csdnimg.cn/direct/11ad0bb2c5734aaaa20065d2eb876b58.png
图34 节头表
以上节中,.text节生存代码,.data节生存已初始化的全局/静态变量,.bss节生存未初始化或初值为0的全局/静态变量,.symtab为符号表(包含函数以及全局/静态变量),.strtab为字符串表,.rodata为只读数据区。带.rel开头的部分均为重定向后的部分。.eh_frame则是栈回溯时使用的信息,当需要检察某一时候的栈帧变革将会调用这部分信息。
https://img-blog.csdnimg.cn/direct/2546132447fd4bc2a55b1c89f7aaaf89.png
图35 section group、步伐头表和动态链接部分
由于目标文件不可实行,故不存在“段”,从而不存在program header。
由于此时并未进行链接利用,故dynamic section和section group并不存在。
以下是重定位节和符号表部分:

https://img-blog.csdnimg.cn/direct/99bd1b7f9f524523acefbd1692c39efb.png
图36 重定位部分和符号表
其中.rela.text是重定位的代码,即存放着.text节的重定位信息。.rela.text此时并不会存放函数的绝对地点,而是存放相对于.text的偏移量,因为系统并未为步伐分配内存,故不知道各函数的详细位置。
.rela.text的Type部分显示着各函数/变量的重定位类型。其中R_X86_64_32为重定位对绝对地点的引用,R_X86_64_PC32为重定位对PC地点的相对引用,R_X86_64_PLT32为PLT的延迟绑定。
.rela.eh_frame生存着栈帧入栈时的相关信息
.symtab部分则是一些符号表,包含函数、全局变量、静态变量以及一些字符串常量等等。这些符号是链接器在符号解析过程中需要处理惩罚的部分。
4.4 Hello.o的效果解析

以下是objdump反汇编后的效果,首先是main函数的起始部分:
https://img-blog.csdnimg.cn/direct/8d63d1d4697b4d87a244d15981a50b06.png
图31 main函数起始部分的汇编
可以看到相比.s文件,其增加了指令对应的呆板码。别的,它将.s文件中所有跳转语句(jne、jle等)的.L2、.L6等更换成了相对于main函数的偏移量(如jne 19 <main+0x19>)。
由上可以知道汇编语言在被gcc汇编后会转换成呆板码,而不同指令的呆板码其对应的长度是不同的。一些指令的利用数也会被嵌入到对应的呆板码中,如cmp、mov、sub等。
其他全部重定位部分如下图所示:
https://img-blog.csdnimg.cn/direct/9259fc45e6c340a08ff6e787a080d96a.png
图38 重定向部分的汇编
和上文类似,调用函数的call指令背面的利用数全部变成了相对main的偏移量。而且有一些函数,如getc、__printf_chk等,由于并未进行链接,其在转换成.o文件时并没有出现实现对应功能的呆板码。
4.5 本章小结

汇编的过程即将汇编语言转换为带有呆板码的.o文件。呆板码的情势使得呆板可以辨认并对其做出反应。只管如此,.o文件无法被实行,然而.o文件可以被重定向,这使得其可以与其它的目标文件进行链接,从而得到完整的可实行文件。

第5章 链接

5.1 链接的概念与作用

链接的概念:通过调用链接器ld,对一系列的目标文件进行符号解析和重定向等利用,终极整合生成可实行目标文件的过程。
链接的作用:对目标文件中的代码节、数据节等重新进行组合、排序、复制、插入等,终极形成一个具有完整功能的可实行目标文件。
别的,链接的存在使得步伐员不必将所有的代码全部整合到一个文件中去,这大大降低了编程时的麻烦,并使得项目标组织变得清晰且有序。动态链接的存在降低了步伐对内存的要求,共享库使得多个文件可以共享同一份代码,也降低了内存的使用量。
5.2 在Ubuntu下链接的下令

https://img-blog.csdnimg.cn/direct/45379b2d1dc74d338f256f1874b70b29.png
图39 链接时的下令
以上为链接时的下令。
5.3 可实行目标文件hello的格式

以下是hello通过readelf得出的详细信息。
首先是ELF头:
https://img-blog.csdnimg.cn/direct/bc5b1f0bb5bf46dcbee0e8ddc3d2fdb7.png
图40 ELF头
相比链接前,该段的一些数据发生了变革,比方program header的相关数据。而后是各节的header:
https://img-blog.csdnimg.cn/direct/52801af03cf340c08ce527eb9dade69e.png
图41 节头表

https://img-blog.csdnimg.cn/direct/ede24bf131f547129958fe8468aa2886.png
图42 节头表
相比链接前,这一部分多了几个头,如.hash、.init、.plt、.got等等。其中GOT为全局偏移量表,紧张和PLT配合用于动态链接库相关的重定向。
接下来是节到段的映射,以及与动态链接有关的部分。
https://img-blog.csdnimg.cn/direct/28e3d54b4e2646e7992b459c56210b44.png
图43 链接相关
而后是与重定向有关的内容:
https://img-blog.csdnimg.cn/direct/ba189fe7987d48759058c71cc087dfbd.png
图44 重定向相关
接下来是动态链接相关的和hello.o中的符号表:
https://img-blog.csdnimg.cn/direct/4b519d3db4be4722ad1b353f879ab2eb.png
图45 符号表
5.4 hello的虚拟地点空间

edb下hello的数据段如下所示:
https://img-blog.csdnimg.cn/direct/09308189965b433090c7a6defcaea223.png
图46 Data Dump
节头表的一些头地点可以再此处找到,比方.text头(004010e0)。这一段仅展示到.rodata头为止(地点为00402000)。
堆栈区域如图所示:
https://img-blog.csdnimg.cn/direct/3fb43db4a3f147dab4fcbc788e518367.png
图47 Stack
代码区域如图所示:
https://img-blog.csdnimg.cn/direct/4ae468812e41436e962b1f1fab90bcf4.png
图48 代码区域

5.5 链接的重定位过程分析

观察objdump -d -r hello指令反汇编后的效果如下。首先,hello中多出了以下部分:
https://img-blog.csdnimg.cn/direct/789db115249b48f19870432079ef9fb9.png
图49 过程链接表
此段为过程链接表PLT,和全局偏移量表GOT(Global Offset Table)一起对共享库中的函数进行管理。共享库的函数使用延迟绑定机制,仅在过程被第一次调用的时间进行过程地点的绑定,如许会大大节省步伐的内存占用。PLT和GOT的协调配合使得调用共享库的函数成为大概。
以下是对应共享库函数的PLT:
https://img-blog.csdnimg.cn/direct/c44741f5b6c247e697685aa637e47e2a.png
图50 共享库函数的PLT
别的,hello中还多了_start函数部分,这是步伐的入口函数:
https://img-blog.csdnimg.cn/direct/5cb63e0a293148abbebd4cdb28718319.png
图51 _start函数

Main函数也进行了重新构造:
https://img-blog.csdnimg.cn/direct/34e77a978cd24097a316b08ed7cf0b99.png
图52 main函数
末端处还多了fini:
https://img-blog.csdnimg.cn/direct/a66df5c186334b99b602d174d2049527.png
图53 fini
可以观察到,上述所有的语句全部都与一个绝对地点绑定,并且call和jmp(以及变种)后的利用数变成了绝对地点,一些变量的值变成了其地点值。可知,重定位过程中,(非共享库)函数与内存地点进行了绑定。
由上可知,链接器在链接的过程中进行了重定位,而与hello.o不同的是,步伐员所写的代码均与内存中的地点进行了绑定,相对应的偏移量利用数换成了详细的地点值。而共享库由于延迟绑定机制,库函数地点只有在它们第一次被调用的时间被确定,而调用利用由PLT和GOT共同管理。
5.6 hello的实行流程

以下是edb实行hello的实行过程:

[*]调用ld-linux-x86-64.so.2的内部函数加载hello(loader)
地点:0x00007f998ec22290
https://img-blog.csdnimg.cn/direct/b13ec12538054020bc8acfd19347b51d.png
图54 ld-linux-x86-64.so.2

[*]实行_start
地点:0x00000000004010e0
https://img-blog.csdnimg.cn/direct/d48130b74654493eb1db5d6076f21f3e.png
图55 _start

[*]实行__libc_start_main
地点:0x00007f33c4029dc9
https://img-blog.csdnimg.cn/direct/c04eab1de2624562bfa1b4db8f686c90.png
图56 _libc_main_start

[*]实行__cxa_ateixt
地点:0x00007fbe766458c0
https://img-blog.csdnimg.cn/direct/ff56f0a98f704e7cbb964be519000f6f.png
图57 __cxa_ateixt

[*]调用__setjmp
地点:0x00007f6952a421e0
https://img-blog.csdnimg.cn/direct/75da46374b8f460baaae83583f271ff5.png
图58 __setjmp
......
6.实行main
函数名:main 地点:0x0000000000401115
https://img-blog.csdnimg.cn/direct/a7571d09212242808d01511105d1ff32.png
图59 main
7.步伐调用__printf_chk函数(通过PLT和GOT)
地点:0x0000000000401040(PLT)
https://img-blog.csdnimg.cn/direct/f8678377b34f44d1aa44fd19f54ac223.png
https://img-blog.csdnimg.cn/direct/0f4ef2cbb1e44dfabc8d6dc8be726bb5.png
图60 __printf_chk

8.步伐调用atoi函数(通过PLT和GOT)
地点:0x0000000000401090(PLT)
https://img-blog.csdnimg.cn/direct/762824d31b044e5fb004c01c41a82c02.png
图61 atoi
9.步伐调用sleep函数(通过PLT和GOT)
地点:0x00000000004010c0(PLT)
https://img-blog.csdnimg.cn/direct/c5696f8aafb44aa88fc55c1ed705120a.png
图62 sleep
10.上述7-9步骤循环8次
   11.步伐调用getc函数(通过PLT和GOT)
地点:0x0000000000401090(PLT)
   12.调用exit函数
地点:0x00007fbb0da455f0
https://img-blog.csdnimg.cn/direct/ab97558f2d3c4d83bbfcd5f0d48a3b50.png
图63 exit函数
5.7 Hello的动态链接分析

hello步伐中的动态链接项目有printf(__printf_chk)、exit、getchar(getc)、sleep。
动态链接的共享库函数接纳延迟绑定机制,仅在第一次调用的时间这些函数的地点才会被确定。以__printf_chk函数为例,根据PLT和GOT得知该函数对应的地点信息将会被存放于0x404028。故观察该区域:
dl_init前:
https://img-blog.csdnimg.cn/direct/7ced983a30ce46b29b8a93c19fcd9947.png
图64 dl_init前
dl_init后:
https://img-blog.csdnimg.cn/direct/9dbdcc24ee3849d197b9a0980329fa08.png
图65 dl_init后
可以看到在动态链接后,这一段的信息发生了变动。该过程为:首先步伐跳转到了PLT中地点为0x4010a0的部分,而后通过PLT取出GOT对应条目,再通过一系列利用终极得到__printf_chk的函数地点,而后返回。
5.8 本章小结

通过链接,步伐员可以通过多文件编译的方式来实现更高效的步伐编写,同时可以一定程度上淘汰步伐所需的内存空间,提高步伐的性能。共享库和动态链接的存在使得步伐员在调用函数的时间可以让多个文件使用同一个副本,大大淘汰了函数调用时的麻烦。


第6章 hello进程管理

6.1 进程的概念与作用

进程的概念:进程即一个实行中步伐的实例。
进程的作用:使用进程这一抽象概念,系统可以更好地管理各个步伐的运行状态,可以同时独立地实行多个步伐,等等。
6.2 简述壳Shell-bash的作用与处理惩罚流程

Shell-bash的作用:shell作为一种可交互的步伐,可用于管理系统的前配景作业,可以让用户实行一些系统下令、发送信号、创建并管理进程等等。Shell相称于用户和内核之间的桥梁。
Shell-bash的处理惩罚流程:Shell接受到用户下令或读取脚本下令---------->Shell解析下令(对下令进行拆分解析)----------------->Shell根据解析效果调用相关步伐或实行相应下令。
一样平常而言,Shell下令中有一部分是内置下令。若出现内置下令,Shell有一套专门的机制用于处理惩罚这些内置下令。若为实行某一步伐,Shell会创建进程(fork),而后根据下令来将其置于前台/配景,而后实行该进程(execve)。一样平常而言,进程克制后会对其进行回收。
6.3 Hello的fork进程创建过程

Shell中输入./hello------------>Shell解析该下令------->Shell检查该文件是否存在,若存在则创建进程。
创建进程时,Shell会将hello分配到一个单独的进程组中。一样平常而言,fork函数创建进程会为该进程分配一个唯一的PID(进程号),各个进程的进程号各不相同,进程组号默以为该进程的进程号。创建出的进程为父进程的一个副本,但与父进程独立。Fork函数会为这些进程创建mm_struct,并且将进程虚拟页均标记为只读,并将这两个进程的区域布局标记位私有的写时复制。
6.4 Hello的execve过程

Shell创建hello进程----------->调用execve函数使hello进程运行。Execve运行过程如下:

[*]删除已存在的用户区域
[*]映射私有区域
[*]映射共享区域
[*]设置步伐计数器
6.5 Hello的进程实行

进程上下文切换的过程如下所示:
https://img-blog.csdnimg.cn/direct/0dbc6b4b348d4ada901d5fa413aa5d31.png
图66 进程的上下文切换
一样平常而言,系统并不只实行hello进程,一些系统级进程需要在内核同步实行。当一些事件如中断发生的时间,或是用户实行某种系统调用,用户态运行下的步伐需要把控制权转交给内核进行处理惩罚,此时系统会通过上下文切换的方式把当前用户态的状态生存,而后切换到内核态实行相应利用,期待利用实行完毕后再将步伐的控制权转交给用户代码,从而再切换回用户态继续实行当前步伐。
除了上下文调用,大多数进程采取并发机制。Hello步伐是在用户态下实行的。而此时利用系统仍然有其它进程需要实行,比方桌面、终端,或是其它的应用步伐,故利用系统会让这些进程轮流实行,而轮流的速率很快以至于产生了这些步伐在一起实行的假象,此为所谓的并发实行,即多个逻辑流的实行在时间上有所重叠,因而同时实行。各进程包罗hello在不同的时间片下并发的实行着。
6.6 hello的非常与信号处理惩罚

大概出现的非常:中断非常、缺页非常、陷阱(系统调用)
信号:SIGINT、SIGTSTP、SIGKILL、SIGCONT
Hello实行过程中,输入的随机字符串其并不会处理惩罚,而是等到步伐竣事后再进行处理惩罚:
https://img-blog.csdnimg.cn/direct/5809f354797b40248c5590847d7aa336.png
图67 随机键盘输入
缺页非常的处理惩罚方式:触发缺页处理惩罚步伐,从磁盘中读取页到物理内存中,进行页面调度。
陷阱:步伐中存在sleep函数,该函数会调用内核利用使步伐休眠一段时间,此时步伐会实行syscall指令,控制权被移交给陷阱的相关处理惩罚步伐,调用对应的内核步伐让进程休眠。
SIGINT:该信号会触发中断非常,此时步伐克制运行。
该信号可以通过Ctrl+C发送给进程。如图所示:
https://img-blog.csdnimg.cn/direct/096e4511aea14978a5eb367577cdd0a3.png
图68 Ctrl+C触发SIGINT
此时通过ps下令检察:
https://img-blog.csdnimg.cn/direct/0e23431816b74ef1908c3f8ff11de49e.png
图69 SIGINT下的ps
可以发现该进程不复存在(被回收)。
SIGTSTP:由终端发送的克制信号。该信号会使前台进程克制运行,直到收到SIGCONT为止。
SIGTSTP可以通过Ctrl+Z进行发送。如图所示:
https://img-blog.csdnimg.cn/direct/9a20d105a9e14d6d96a5c99dbb397d76.png
图70 Ctrl+Z发送SIGTSTP
此时通过ps下令检察:
https://img-blog.csdnimg.cn/direct/5af01361d31c4c44a433623cd0e8ba2a.png
图71 SIGTSTP下的ps
或通过pstree检察如下所示:
https://img-blog.csdnimg.cn/direct/1f9e772d7b5f4419af0ccaaadf2d4a82.png
图72 SIGTSTP下的pstree(部分)
T表现Terminated,此时可以通过fg下令使其重新运作(大概kill -18 [进程号],用于向进程发送SIGCONT信号)
通过jobs下令获取进程号并让其重新运作:
https://img-blog.csdnimg.cn/direct/9431e2dea192430d92a60fbe4c76ff1b.png
图73 jobs和fg下令
SIGCONT:见上,该信号可让克制进程重新运行。
SIGKILL:杀死进程,由kill发送,不可阻塞。如下所示:
Kill前:
https://img-blog.csdnimg.cn/direct/b56f78227b604718a69d384659261dc2.png
图74 kill前
Kill后:
https://img-blog.csdnimg.cn/direct/e5689bc36d2e4aecba7fb6819512d81e.png
图75 kill后

6.7本章小结

进程调度使得hello得以正常运行及回收,同时使得利用系统中的其它步伐合理有序的运行。

第7章 hello的存储管理

7.1 hello的存储器地点空间

逻辑地点:表现为【段标识符:段偏移量】,逻辑地点需要通过MMU(Memory Management Unit,内存管理单元)变换成物理地点进行寻址。在掩护模式下,段标识符为GDT(全局形貌符表)的索引。
线性地点:当地点空间中的整数是连续的,如许的地点空间为线性地点空间,对应的地点称为线性地点。逻辑地点颠末转化之后会变成线性地点。
虚拟地点:是一种线性地点,对应着虚拟内存中的某个位置。
物理地点:CPU用于访问物理内存时使用的地点,对应着物理内存上的一串字节序列。
可实行文件hello中存放的地点均为虚拟地点,详细调用的时间需要翻译成物理地点进行访存。
7.2 Intel逻辑地点到线性地点的变换-段式管理

Intel的段式管理方式实现了从逻辑地点到线性地点(虚拟地点)的变革。
利用系统在实模式下,通过运算将逻辑地点变换为线性地点,详细公式为:线性地点=段基址<<4+段偏移量。而在掩护模式下,线性地点的计算也接纳类似的方法,但是不再是段基址,而是段标记值(段形貌符)。
7.3 Hello的线性地点到物理地点的变换-页式管理

线性地点到物理地点的变换需要通过MMU来完成。Hello步伐想要访问一个物理页,首先须生成一个虚拟地点发送给MMU,MMU将其翻译为物理地点而后发送给内存,内存再返回相应的索引信息。
虚拟地点一样平常由虚拟页号VPN(Virtual Page Number)和虚拟页偏移量VPO(Virtual Page Offset)构成。访问内存的时间,VPN获取到物理页号PPN(第一个P是Physical),再将PPN与VPO串联得到了相应的物理地点。
7.4 TLB与四级页表支持下的VA到PA的变换

为了加快对内存的访问,系统引入了TLB(快表,Translation of Lookaside Buffer),它被存放在MMU的一个Cache中。
TLB的虚拟页号访问和上述类似,但是VPN又可以分为TLB索引(TLBI)和TLB标记(TLBT)。访问物理内存可以直接使用VPN进行翻译。通过TLB访问内存都是在MMU上进行的,速率比平常的虚拟页(在磁盘上)快。
https://img-blog.csdnimg.cn/direct/055b3a4632b34a46a6dd6145bcbd9551.png
图76 TLB虚拟地点构成
多级页表可以用来节省页表所占用的空间。前几层页表均指向下一层页表的基址,而最后一层页表指向物理内存。假如有某一级的页表为空,那么其后所有的页表均不存在。
通过多级页表的访问与单个页表的方式较接近,区别在于需要从一级页表取出二级页表的基址,而后根据该基址继续访问下一级页表,以此类推。但如许会大大节约空间的开销,而且速率上不比单个页表慢。
https://img-blog.csdnimg.cn/direct/854d48e358a0468aa9f4fecd581d359a.png
图77 k级页表的访问
7.5 三级Cache支持下的物理内存访问

步伐得到物理地点后开始对物理内存进行访问。首先访问三级Cache中是否存在相关的物理页。
访问Cache的地点和TLB类似,其地点布局如图所示:

https://img-blog.csdnimg.cn/direct/6bfb48a9d32742a78c3387642f69483e.png
图78 Cache地点的构成
CT为标记位,CI为组索引,CO为块偏移量。地点之所以如此设计,与Cache的布局有关。一样平常Cache的布局如下所示,包含S组,每组E行,每行B位缓存。
https://img-blog.csdnimg.cn/direct/845b8eff9da14b2e9ee89e4ea9ba8c56.png
图79 Cache一样平常布局
寻址方式如下:首先通过组索引找到对应的组,而后根据标记位找到对应的行,再根据块偏移找到该行缓存对应的起始位置。如许就完成了一次访问。
对Cache的访问也有失败的情况,称为缓存不掷中。不掷中的类型一样平常有三种:冷不掷中,辩论不掷中和容量不掷中。冷不掷中即缓存内并没有存放所需的索引条目,大概缓存为空。容量不掷中则是工作集(某阶段访问缓存块的某个相对不变的集合)超出了缓存的巨细而导致的。引发辩论不掷中的其中一种因素是多个不同地点映射到了同一组内的同一行中,造成了存取的时间大概会把原先的值覆盖掉。Cache访问的缓存不掷中导致需要寻找的值必须再次从低一级的缓存甚至磁盘中寻找,这会大大增加时间开销。
以上为Cache的读过程,Cache也可以进行写。写紧张有两种方式,直写和写回。直写即将缓存的字立刻写入低一层内存,写回则是在缓存需要更换某一块的时间才把该块写入低一层内存中,但是写回需要额外的修改位来标记块是否被修改过。
与上文类似,存在Cache的写不掷中,即缓存区找不到要写的数据。紧张有两种战略,其一是写分配,即将所需数据从低一级的内存读入Cache并更新对应的块;其二黑白写分配,即不读入Cache,直接写入低一层。一样平常而言,直写黑白写分配的,而写回是写分配的。
7.6 hello进程fork时的内存映射

系统实行hello步伐的时间,会调用fork函数创建进程。Fork函数会为该进程创建一个(代码、数据、栈帧状态、区域布局和页表等)一模一样但是独立于父进程的副本,即子进程,并为该子进程分配一个唯一的PID。同时,fork为该进程分配了虚拟内存并创建了当前进程的mm_struct(用于维护当前的进程状态)。Fork将这两个进程的区域布局均标记为私有的写时复制,并将这两个进程的每一页标记为只读。
7.7 hello进程execve时的内存映射

hello进程被创建后,系统实行execve函数运行该进程,过程如下:

[*]删除已存在的用户区域。
[*]映射私有区域。这是为新步伐的代码、数据、bss和栈区创建新的区域布局(均为私有写时复制)。代码和数据区被映射到.out文件的.text和.data区。bss区是哀求二进制零的,被映射到长度不为零的匿名文件上;栈区也是哀求二进制零的,被映射到长度为零的匿名文件上
[*]映射共享区域。hello中存在共享库函数如printf、getchar等,这些步伐通过libc.so动态链接到该步伐,对应的hello会讲这些步伐映射到虚拟地点空间的共享区域上。
[*]设置步伐计数器。execve需要设置当前的步伐计数器,使之指向hello步伐的入口。

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

缺页故障发生在缓存中找不到对应的页的时间。一样平常而言此时的虚拟页表对应条目标状态为未分配大概未缓存,有用位为0。缺页中断会触发缺页非常处理惩罚步伐进行页面调度利用,若物理内存已满则从物理内存中选择一个捐躯页进行更换。
现在的利用系统基本上使用按需页面调度方式处理惩罚缺页非常,即当真正使用该页面时才进行页面调度。
对于Linux系统,首先MMU会判断虚拟地点的正当性,若不正当则会触发缺页中断,产生段错误信息,从而克制该步伐,否则步伐继续实行;而后,MMU会判断该地点的内存访问是否正当,若非法则处理惩罚方式同上。若以上步骤都正当,则不会触发缺页中断,则会正常处理惩罚缺页非常。
7.9动态存储分配管理

动态内存分配是通过堆实现的,而该利用是由动态内存分配器来完成。分配器将堆视为不同巨细的内存块,每个内存块可视为虚拟内存片,其中空闲的虚拟内存片可以用于分配动态内存。
分配器有两种风格。显式分配器需要步伐显式地释放任何已分配的块,而隐式分配器则会自动回收步伐不需要的已分配块(通常这一机制也被称为垃圾网络)。显式分配可以通过malloc函数来实现,同时需要free函数将其释放。
一些分配器在分配内存的时间会发生碎片现象,也就是当前内存不能满意分配的哀求时,这部分内存就会变成碎片。碎片有分外部碎片和内部碎片。内部碎片的巨细和数量可以估量,而外部碎片由于内存的分散性,使得其量化极其困难,故大多数分配器都会尽大概的维持少量的大空闲块来避免这类碎片的发生。
分配器的实现通常接纳空闲链表的方式,这些空闲链表中记载着和块有关的信息,以区别空闲块和已分配块,以及区分块的边界。隐式空闲链表可以用一种线性的方式对已分配块和空闲块进行排列,实现方式十分简朴,但块分配和对快的总数是呈线性关系的,所以对于通用的分配器,隐式空闲链表是不恰当的。故有了显式空闲链表。这种链表为双向链表,把访问时间降低到了空闲块数量的线性时间,而其维护战略可以是LIFO(后进先出)的,也可以是按照地点顺序来维护的。但是显式空闲链表也有使内部碎片程度提高的风险。
当应用发起内存申请时,分配器会使用适当的放置战略来哀求空闲块,而后通过适当的分割来进行空闲块的分配,最后为了避免假碎片的产生,分配器必然要对相邻的空闲块进行合并利用。
如今一些高级语言,如Java,提供了垃圾回收机制,使得分配内存和释放内存变得更加方便。
7.10本章小结

虚拟内存为步伐提供了施展手脚的空间,动态内存分配办理了步伐的空间题目,fork和execve使得步伐能正常且独立地运作。而对于步伐逻辑流中发生的一系列事件,利用系统也有对应的处理惩罚方案。

第8章 hello的IO管理

8.1 Linux的IO装备管理方法

装备的模型化:文件
装备管理:unix io接口
在Linux/Unix中,一切装备都被模型化为文件进行管理,所有的读和写利用都会被视为文件的读和写。
8.2 简述Unix IO接口及其函数

Unix系统中常用的I/O函数紧张有open、read、write、lseek等。
函数open用于将给定的文件名转换成对应的文件形貌符,并且返回形貌符数字。其原型为:
int open(char *filename,int flags,mode_t mode)
其中,flags控制打开文件的方式,如只读方式、只写方式、可读可写方式等。参数mode则指定了新文件的访问权限位。
read函数用于读取文件中的内容,并把它存储到缓冲区中。该函数是按字节读取的,而对于字符的读取还可以接纳标准I/O库里面的fgets函数。
write函数用于将缓冲区里的字节写入对应文件中。而字符串的写入还可以接纳标准I/O库里的fputs函数。
lseek函数可以用于修改当前文件的位置。
除了上述函数以外,进行I/O利用还可以接纳RIO包,该包的函数是线程安全的,因而有着更好的健壮性。当然,C语言还有标准I/O库用于更换上述的函数。标准I/O库接纳了流的概念进行对应功能的实现。
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;
[*]    }

第一个参数为格式串,背面的省略号代表着可变参数。printf函数通过vsprintf函数将字符串分割成元字符放入buf中,并将格式串全部更换为参数列表中的参数,终极返回参数的个数。而后,printf函数再通过write函数把buf里的字符全部输出到终端上。由于write是系统调用,故此处需要使用syscall指令触发陷阱机制,调用相关的处理惩罚函数对write进行处理惩罚,从而实现printf函数字符串的输出。
字符显示驱动子步伐:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析

异步非常-键盘中断的处理惩罚:键盘中断处理惩罚子步伐。接受按键扫描码转成ascii码,生存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结

通过一系列的I/O接口,hello得以和文件进行交互,甚至可以和外部装备进行利用。Unix系统将装备抽象成文件的利用,使得各装备的交互大大简化了。
结论

用计算机系统的语言,逐条总结hello所经历的过程。
你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。
hello步伐从生到死,经历了预处理惩罚、编译、汇编、链接等利用,而后又经历了进程的创建、运行、克制、回收等利用,这其中不乏利用系统和硬件的倾力配合。
hello.c通过预处理惩罚,形态变得完整,而后通过汇编、编译的过程,hello步伐从汇编语言到呆板语言,正一步步地投入呆板的拥抱中。终极,hello.o通过链接器与其它.o文件水乳融会、通过动态链接与.so文件共享,终极生成了完整的hello步伐,如许hello就迈入了可实行的第一步。
紧接着,通过shell的调度,hello被纳入到新创建的进程中,并在execve的驱动下走上了实行的快车道。在这期间,非常控制流处理惩罚着hello运行过程中所发生的一系列事情,比方读和写、步伐的中断和克制等等。这一系列的过程使得hello得以正常运行。终极,hello完成了它的生命周期,shell美满的回收了它,属于hello的舞台就此落幕。
计算机系统便是如许由软硬件共同编写的华美乐章,每一个利用、每一条语句都是这个大乐章中不可或缺的音符。当然这个乐章并非美满,仍然需要我们去不断地美满它。
附件

附件如图所示:
https://img-blog.csdnimg.cn/direct/701c49ff19d241529f7f9ba21f73ed64.png
图80 附件
Makefile:简化一些过程使用的文件,如编译、预处理惩罚、实行等等。
Hello.i:hello.c预处理惩罚的产物
Hello.s:hello.i编译后的产物
Hello.o:hello.s汇编形成的产物
Hello:链接形成的产物
Hellodump.txt:hello.o反汇编(OBJDUMP,下同)形成的产物
Helloelf2.txt:hello反汇编产物。
Helloelf.txt:对hello.o使用readelf的产物
Linked_hello.txt:对hello使用readelf的产物


参考文献

  Randal E.Byrant & David R.O’Hallaron 深入理解计算机系统 原书第三版 
  汇编(一):risc-v汇编语法. vito https://zhuanlan.zhihu.com/p/588075416
  步伐的本质之二ELF文件的文件头、section header和program header. tanglinux. https://blog.csdn.net/npy_lp/article/details/102604380
  linux 栈回溯(x86_64 ) 小乐叔叔 https://zhuanlan.zhihu.com/p/302726082
  [转]printf 函数实现的深入剖析 https://www.cnblogs.com/pianist/p/3315801.html






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