HIT 计统大作业 步伐人生
摘 要本文从Hello.c步伐开始,介绍了Hello步伐的一生:预处置处罚,编译,汇编,链接,假造地址映射,运行,访存,结束,被父进程回收。结合课本知识和资料,通过Ubuntu进行实行,采用gdb、edb、gcc等工具,明白这学期学习的csapp中有关步伐的预处置处罚、编译、汇编、链接等概念知识,认识步伐的进程管理、存储管理和IO管理。
关键词:预处置处罚,编译,汇编,链接,进程管理,假造地址,存储管理,IO操作
目 录
第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将其与引用到的库函数链接,天生可执行文件hello。再通过体系创建一个新进程而且把步伐内容加载,实现有步伐到进程的转化。
O2O(From Zero-0 to Zero-0)过程:
步伐运行前,shell调用execve函数将hello步伐加载到相应的上下文中,将步伐内容载入物理内存。然后调用main函数。步伐运行结束后,父进程回收进程,开释假造内存空间,删除相关内容。这就是hello.c的O2O过程。
1.2 环境与工具
图1.1 盘算机硬件环境
硬件环境:处置处罚器:11th Gen Intel® Core(TM) i7-11800H @ 2.30GHz
体系:64位操作体系
软件环境:Windows10 64位;Vmware Workstation Pro;Ubuntu 16.04 LTS 64位
开发工具:Visual Studio 2022;CodeBlocks 64位;vi/vim/gedit+gcc
1.3 中间效果
hello.c 源文件
hello.i 源文件预处置处罚所得文件
hello.s 汇编步伐
hello.o 可重定位文件
hello 可执行文件
hello.elf 可重定位文件hello.o的解析效果
hello.txt 可重定位文件hello.o的反汇编效果
Hello.elf 可执行文件hello的解析效果
Hello.txt 可执行文件hello的反汇编效果
1.4 本章小结
本章重要介绍了hello步伐的生命周期,对hello步伐进行分析的硬件软件环境,
以及实行和分析过程产生的文件。
第2章 预处置处罚
2.1 预处置处罚的概念与作用
预处置处罚的概念:预处置处罚器针对#开头的下令,对步伐员编写的原始C步伐进行添加(添加相应的代码),得到被修改了的源步伐,即后缀为.i的文件。
预处置处罚的作用:
1. 处置处罚头文件:#include下令告诉预处置处罚器读取头文件的内容,并将其直接插入步伐文本中。
2. 处置处罚宏界说:对于#define指令,进行宏更换,对于代码中所有利用宏界说的地方利用符号表现的实际值更换界说的符号
3. 处置处罚条件编译:根据可能存在的#ifdef来确定步伐需要执行的代码段。
4. 处置处罚特殊符号:比方#error等,预编译步伐可辨认一些特殊的符号,并在后续过程中进行合适的更换。
5. 删除源步伐中的注释部分。
2.2在Ubuntu下预处置处罚的下令
如图执行gcc -E hello.c -o hello.i,预处置处罚hello.c步伐并将预处置处罚效果定名为hello.i。
图2.1 预处置处罚指令
2.3 Hello的预处置处罚效果解析
留意到,经过预处置处罚后,文件的长度大大增加,这是预处置处罚器将头文件的内容复制进了源步伐文本文件的效果。如图头文件的长度高达3000行,源文件的文本出现在3000行之后。
图2.2:hello.i文件
而且可以发现,原本存在于源文件的注释也已经消散不见,说明预处置处罚器会在将库文件复制入源文件的同时将注释删除。
2.4 本章小结
本章介绍了预处置处罚的概念与作用,结合hello.i分析了预处置处罚器预处置处罚源步伐的过程,展示了在Ubuntu中预处置处罚hello.c的效果。
第3章 编译
3.1 编译的概念与作用
编译的概念:
编译是利用编译步伐从源语言编写的源步伐产生目的步伐的过程,也是用编译步伐产生目的步伐的动作。也就是说编译器会将通过预处置处罚产生的.o文件翻译成一个后缀为.s的汇编语言文件,是从高级步伐设计语言到呆板能明白的汇编语言的转换过程。
编译的作用:
将高级语言誊写的源步伐转换为一条条呆板指令,呆板指令和汇编指令逐一对应,使呆板更容易明白,为汇编做准备。
3.2 在Ubuntu下编译的下令
编译的下令:gcc -S hello.i -o hello.s
图3.1 编译指令
3.3 Hello的编译效果解析
3.3.1 数据
1.常量:
(1).数字常量:在汇编代码中,数字常量一般直接利用,如在该段代码中:
如果argc与4不相等,则会在提示信息后直接退出。可以发现,该段代码经过汇编后,数字常量4原封不动地在汇编代码中利用。
(2):字符串常量:可以留意到,在汇编代码中,字符串常量一般存储在汇编代码的某个段内,如在该段代码中:
两个printf利用的字符串分别被保存在了.LC0与.LC1段内
2.变量:
(1):局部变量:局部变量可以保存在栈中或保存在寄存器中。
可以留意到循环变量i在每次进入循环时都会对循环条件i<5进行判定,经编译器处置处罚成与将栈中地址%rbp-4处的值与4进行比力,因此可以得知循环变量被保存在栈中%rbp-4的位置:
3.3.2 赋值
在循环开始时将循环变量i赋值为零,在汇编代码中直接利用mov指令将保存了i的栈中内存地址为%rbp-4处赋值为0。
3.3.3 范例转换
可以留意到,atoi函数返回值为有符号整数int范例,而sleep函数接收参数范例为无符号整数unsigned int范例,因此在进行sleep函数调用传入参数时,需要进行隐式范例转换。
3.3.4 算术操作
1.利用printf函数打印argv数组中字符串时,需要进行参数传递,因此需将栈中argv元素通过栈基址寄存器%rbp与偏移量进行算术运算取出:
3.3.5 关系运算
1.如图,对于argc的判定,不即是4时输出错位提示信息并退出步伐,否则将进行条件跳转:
3.3.6 数组,指针,布局操作
1.源代码中数组操作出现了一次,即对于argv数组的操作,观察汇编代码可以发现argv储存的两个值都存放在栈中,argv的储存地址是%rbp-16,argv存储地址为%rbp-24:
3.3.7 函数调用
源代码中出现了多个函数调用的情况,在x86体系中函数参数储存第一至第六参数依次储存在%rdi、%rsi、%rdx、%rcx、%r8、%r9 寄存器中,其余的参数保存在栈中的某些位置。
[*]main函数:
---参数:传入参数为argc与argv,其中argv储存在栈中,argc 储存在%rdi 中。
---返回:源代码中返回语句为return 0,因此在汇编代码中末了为将%eax 设置为0并返回这一寄存器:
[*]printf函数:
---参数:第一次调用的时候传入了字符串参数首地址而且编译器将其转换为调用puts函数;第二次在for循环中调用时传入了argv和argv的地址。
---调用:第一次满意if条件的时候调用,第二次则为for循环条件满意的时调用。
3. sleep函数:
---调用:将atoi函数返回值存入寄存器%edi中作为参数传递。
---返回:同样将返回值保存在%eax中。
[*]atoi函数:
---调用:将argv保存在寄存器%rax中作为参数进行传递
---返回:将返回值保存在%eax中,并将返回值作为参数传递给sleep函数.
[*]getchar函数
---调用:在for循环结束后调用。getchar函数不需要参数,因此不需要寄存器进行参数传递。
---返回:将返回值保存在%eax中。
[*]exit函数:
---调用:在argc不即是4时进入if语句块与main函数结束时调用。传入参数保存在%edi中。
---返回:将返回值保存在%eax中。
3.4 本章小结
本章重要介绍了在将修改了的源步伐文件转换为汇编步伐的时候重要发生的 变化,以及汇编代码文件中利用到的操作,即源代码各语句对应的汇编代码中的指令对应的展示。
第4章 汇编
4.1 汇编的概念与作用
汇编的概念:
汇编步伐是指把汇编语言誊写的步伐翻译成与之等价的呆板语言步伐的翻译步伐。汇编步伐输入的是用汇编语言誊写的源步伐,输出的是用呆板语言表现的目的步伐。也就是说,汇编器会把输入的汇编指令文件重新打包成可重定位目的文件,并将效果保存成.o文件。它是一个二进制文件,包罗步伐的指令编码。
汇编的作用
将汇编代码转换为呆板指令,使其在链接后能被呆板辨认并执行.
4.2 在Ubuntu下汇编的下令
4.3 可重定位目的elf格式
4.3.1 天生elf文件下令
输出指令: readelf -a hello.o > hello.elf 天生elf文件
4.3.2 ELF头
ELF头(ELF header)以一个16字节的序列开始,这个序列描述了天生该文件的体系的字的大小和字节顺序。ELF头剩下的部分包罗了帮助链接器语法分析和解释目的文件的信息,其中包罗ELF头的大小、目的文件的范例(如可重定位、可执行或者共享的)、呆板范例(如x86-64)、节头部表(section header table)的文件偏移,以及节头部表中条目的大小和数量。不同节的位置和大小是有节头部表描述的,其中目的文件中每个节都有一个固定大小的条目(entry)
4.3.2 节头表
描述了.o文件中每一个节的信息,比方每个节的名字(存储在字符串表中对应的位置),该节在可重定位文件中的位置,该节的范例(包罗符号表,字符串表),以及该节所占字节大小。目的文件中的每一个节都有一个固定大小的条目。具体内容如图所示:
4.3.3 重定位表
保存的是.text节中需要被修正的信息;任何调用外部函数或者引用全局变量的指令都需要被修正;调用外部函数的指令需要重定位;引用全局变量的指令需要重定位; 调用局部函数的指令不需要重定位;在可执行目的文件中不存在重定位信息。本步伐需要被重定位的是printf、puts、exit、sleepsecs、getchar、sleep和.rodata中的.L0和.L1。
.rela.eh_frame节是.eh_frame节重定位信息。
4.3.4 符号表
符号表描述了各符号(全局变量,函数…)的信息,包罗:符号名称(存储在字符串表中对应的位置),符号范例(为函数还是节),是全局的还是局部的。
4.4 Hello.o的效果解析
利用下令objdump -d -r hello.o > hello.txt 得到hello.o的反汇编效果hello.txt
分析hello.o的反汇编,并与hello.s进行对照,可以发现:
1. 两者进制表现不同:hello.s反汇编之后对于数字的表现为十进制,而hello.o反汇编之后数字的表现为十六进制。
2. 分支转移效果形式不同:对于条件跳转,hello.s反汇编中给出的是段的名字,比方.L2等来表现跳转的地址,而hello.o由于已为可重定位文件,对于每一行都已经分配了相应的地址,因此跳转下令后跟着的是需要跳转部分的目的地址。
3. 函数调用操作数不同:hello.s中,call指令后跟的是需要调用的函数的名称,而hello.txt反汇编代码中call指令利用的是main函数的相对偏移地址。同时可以发现在hello.o反汇编代码中调用函数的操作数都为0,即函数的相对地址为0,因为在链接天生可执行文件后才会天生其确定的地址,所以这里的相对地址都用0代替。
4.5 本章小结
本章对汇编过程进行了一个简单的叙述。经过汇编器之后,天生了一个可重定位的文件,为下一步链接做好了准备。并通过与hello.s的反汇编代码的比力,更加深入的明白了在汇编过程中发生的变化。
第5章 链接
5.1 链接的概念与作用
链接的概念:
链接是将各种代码和数据片段网络并合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行于编译时,也就是在源代码被翻译成呆板代码时;也可以执行于加载时,也就是在步伐被加载器加载到内存并执行时;甚至执行于运行时,也就是由应用步伐来执行。
链接的作用:
链接器在软件开发过程中饰演着一个关键的脚色,因为它们使得分离编译成为可能。我们不用将一个大型的应用步伐构造为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其它文件。
5.2 在Ubuntu下链接的下令
利用下令:
图5.1 链接下令
5.3 可执行目的文件hello的格式
图5.2 解析elf文件下令
5.3.1 ELF头
ELF头剩下的部分包罗帮助链接器语法分析和解释目的文件的信息:包罗ELF头的大小、目的文件的范例、呆板范例、节头部表的文件偏移,以及节头部表中条目的大小和数量
图5.3 ELF头
5.3.2 节头表
描述了文件中每一个节的信息,比方每个节的名字(存储在字符串表中对应的位置),该节在可重定位文件中的位置,该节的范例(包罗符号表,字符串表),以及该节所占字节大小。目的文件中的每一个节都有一个固定大小的条目:
图 5.4 节头表
5.3.3 步伐头表
首先显示这是一格可执行目的文件,共有12个表项,其中有4个可装入段(Type=LOAD),VirtAddr和PhysAddr分别是假造地址和物理地址,值相同。Align是对齐方式,这里4个可装入段都是4K字节对齐。以第一个可装入段为例,表现第0x00000~0x005bf字节,映射到假造地址0x400000开头的长度为0x5c0字节的区域,按照0x1000=4KB对齐,具有只读(Flags=R)权限,是只读代码段。
图 5.5 步伐头表
5.4 hello的假造地址空间
利用edb打开hello可执行文件,可以在edb的Data Dump窗口看到hello的假造空间分配情况:
图5.6 hello假造地址分配
可以发现这一段步伐的地址是从0x401000开始的,而且该处有ELF的标识, 可以判定从可执行文件时加载的信息。接下来可以分析其中的一些具体的内容:其中PHDR保存的是步伐头表;INTERP保存了步伐执行前需要调用的解释器;LOAD记载步伐目的代码和常量信息;DYNAMIC储存了动态链接器所利用的信息;NOTE记载的是一些辅助信息;GNU_EH_FRAME保存异常信息;GNU_STACK利用体系栈所需要的权限信息;GNU_RELRO保存在重定位之后只读信息的位置。
5.5 链接的重定位过程分析
下令:objdump -d -r hello > Hello.txt
图5.7 反汇编hello下令
图5.8 反汇编效果
分析Hello.txt与hello.txt这两个反汇编效果,可以发现:
1.在链接过程中,hello中加入了代码中调用的一些库函数,比方体系函数_init,库函数getchar,puts,printf,atoi等,同时每个函数都有对应的假造地址。
2.对于全局变量的引用,由于hello.o中还未对全局变量进行定位,因此 hello.o 中用0加上%rip的值来表现全局变量的位置,而在hello中,由于已经进行了定位, 因此全局变量的的值利用一个确切的值加上%rip表现全局变量的位置。
3.hello中无hello.o中的重定位条目,而且跳转和函数调用的地址在hello中都变成了假造内存地址。这是由于hello.o中对于函数还未进行定位,只是在.rel.text 中添加了重定位条目,而hello进行定位之后自然不需要重定位条目。
4.地址访问:在链接完成之后,hello中的所有对于地址的访问或是引用都调用的是假造地址地址。
结合Hello.txt与盘算机体系知识,可知链接重要分为两个过程:符号解析和重定位。
符号解析:目的文件界说和引用符号,符号解析将每个符号引用和一个符号界说关联起来。
重定位:编译器和汇编器天生从0开始的代码和数据节。链接器通过把每个符号界说与一个内存位置关联起来,从而重定位这些节,然后修改所有对这些符号的引用,使得它们指向这个内存位置。链接器利用汇编器产生的重定位条目的详细指令,不加甄别地执行这样的重定位。
5.6 hello的执行流程
根据反汇编代码可以看出执行函数及假造内存地址如下:
(1) _start
(2) __libc_start_main
(3) __GI___cxa_atexit
(4) __internal_atexit
(5) __lll_cas_lock
(6) __new_exitfn
(7) __libc_csu_init
(8) _init
(9) _setjmp
(10) __sigsetjmp
(11) __sigjmp_save
(12) main
(13) puts@plt
(14)exit
(15) _dl_runtime_resolve_xsavec
(16) _dl_fixup
(17) _dl_lookup_symbol_x
(18) do_lookup_x
(19) _fini
(20)__libc_csu_fini
图5.9 edb分析hello执行过程
5.7 Hello的动态链接分析
当步伐调用一个由共享库界说的函数时,由于编译器无法预测这时候函数的地 址是什么,因此这时,编译体系提供了延迟绑定的方法,将过程地址的绑定推迟到第一次调用该过程时。通过GOT和过程链接表PLT的协作来解析函数的地址。在加载时,动态链接器会重定位GOT中的每个条目,使它包罗正确的绝对地址,而PLT中的每个函数负责调用不同函数。那么,通过观察edb,便可发现dl_init 后.got.plt节发生的变化。首先可以观察elf中.got.plt节的内容:
edb查看发现:
图5.10 执行_init前地址
图5.13 执行_init后地址
5.8 本章小结
本章重要介绍了链接的概念与作用,链接可分为符号界说和重定位,相识了可执行文件的ELF格式,分析了hello的假造地址空间,重定位过程,执行过程,动态毗连过程,对链接有了更深的明白。
第6章 hello进程管理
6.1 进程的概念与作用
进程的概念:
经典界说就是一个执行中步伐的实例。广义界说是进程是一个具有一定独立功能的步伐关于某个数据集合的一次运行运动。它是操作体系动态执行的基本单元,在传统的操作体系中,进程既是基本的分配单元,也是基本的执行单元。
进程的作用:
进程作为一个执行中步伐的实例,体系中每个步伐都运行在某个进程的上下文中,上下文是由步伐正确运行所需的状态构成的。这个状态包罗存放在内存中的步伐的代码和数据,它的栈、通用目的寄存器的内容、步伐计数器、环境变量以及打开文件描述符的集合。
6.2 简述壳Shell-bash的作用与处置处罚流程
作用:shell执行一系列的读/求值步调,然后终止。读步调读取来自用户的一个下令行,求值步调解析下令行,并根据解析效果运行步伐。
处置处罚流程:
1.Shell首先从下令行中找出特殊字符(元字符),在将元字符翻译成间隔符号。元字符将下令行分别成小块tokens。Shell中的元字符如下所示:SPACE , TAB , NEWLINE , & , ; , ( , ) ,< , > , |
2.步伐块tokens被处置处罚,检查看他们是否是shell中所引用到的关键字。
3.当步伐块tokens被确定以后,shell根据aliases文件中的列表来检查下令的第一个单词。如果这个单词出现在aliases表中,执行更换操作而且处置处罚过程回到第一步重新分割步伐块tokens。
4.Shell对~符号进行更换。
5.Shell对所有前面带有$符号的变量进行更换。
6.Shell将下令行中的内嵌下令表达式更换成下令;他们一般都采用$(command)标志法。
7.Shell盘算采用$(expression)标志的算术表达式。
8.Shell将下令字符串重新分别为新的块tokens。这次分别的依据是栏位分割符号,称为IFS。缺省的IFS变量包罗有:SPACE , TAB和换行符号。
9.Shell执行通配符* ? [ ]的更换。
10.shell把所有从处置处罚的效果中用到的注释删除,而且按照一定顺序实行下令的检查
6.3 Hello的fork进程创建过程
父进程通过调用fork函数创建一个新的运行的子进程。调用fork函数后,新 创建的子进程几乎但不完全与父进程相同:子进程得到与父进程假造地址空间相同 的(但是独立的)一份副本,包罗代码、数据段、堆、共享库以及用户栈,子进程获 得与父进程任何打开文件描述符相同的副本,这意味着当父进程调用fork时,子进程可以读写父进程中打开的任何文件。fork被调用一次,却返回两次,子进程返回0,父进程返回子进程的PID。父进程和新创建的子进程之间最大的区别在于它们有不同的PID。
6.4 Hello的execve过程
exceve函数在当前进程的上下文中加载并运行一个新步伐。exceve函数加载 并运行可执行目的文件,并带参数列表和环境变量列表。只有当出现错误时,exceve才会返回到调用步伐。所以,与fork一次调用返回两次不同,在exceve调用一次 并从不返回。当加载可执行目的文件后,exceve调用启动代码,启动代码设置栈, 将可执行目的文件中的代码和数据从磁盘复制到内存中,然后通过跳转到步伐的 第一条指令或入口点来运行该步伐,由此将控制传递给新步伐的主函数。
6.5 Hello的进程执行
进程调理:即使在体系中通常有很多其他步伐在运行,进程也可以向每个步伐提供一种假象,好像它在独占地利用处置处罚器。如果想用调试器单步执行步伐,我们会看到一系列的步伐计数器(PC)的值,这些值唯一的对应于包罗在运行时动态链接到步伐的共享对象中的指令。这个PC的序列叫做逻辑控制流,或者简称逻辑流。进程是轮流实用处置处罚器的,每个进程执行它的流的一部分,然后被抢占,然后轮到其他进程。
在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被 抢占了的进程,这种决策就叫做调理,是由内核中称为调理器的代码处置处罚的。当内核选择一个新的进程运行,我们说内核调理了这个进程。在内核调理了一个新的进程运行了之后,它就抢占了当前进程,并利用上下文切换机制来将控制转移到新的进程。
内核模式变化到用户模式:操作体系内核利用上下文切换来实现多任务。内核为每个进程维持一个上下文,它是内核重启被抢占的进程所需的状态,包罗通用目的寄存器、浮点寄存器、步伐计数器、用户栈、状态寄存器、内核栈和各种内核数据布局的值。
进程执行到某些时刻,内核可决定抢占该进程,并重新开启一个先前被抢占了 的进程,这种决策称为调理。内核调理一个新的进程运行后,通过上下文切换机制来转移控制到新的进程:(1)保存当前进程上下文;(2)规复某个先前被抢占的进程被保存的上下文;(3)将控制转移给这个新规复的进程。当内核代表用户执行体系调用时,可能会发生上下文切换,这时就存在着用户态与焦点态的转换。如下图所示:
图6.1 上下文切换
6.6 hello的异常与信号处置处罚
异常类别:
图6.3 异常范例
处置处罚方式:
[*]中断:
图6.4 中断异常处置处罚
[*]陷阱和体系调用:
图6.4 陷阱和体系调用异常处置处罚
[*]故障:
图6.5 故障异常处置处罚
[*]终止:
图6.6 终止异常处置处罚
不绝乱按效果:将屏幕的输入缓存到缓冲区。乱码被认为是下令,不影响当前进程的执行。
1、hello执行中可能出现的异常:
(1)中断:异步发生的。在执行hello步伐的时候,由处置处罚器外部的I/O设备的信号引起的。I/O设备通过像处置处罚器芯片上的一个引脚发信号,并将异常号放到体系总线上,来触发中断。这个异常号标识了引起中断的设备。在当前指令完成执行后,处置处罚器留意到中断引脚的电压变高了,就从体系总线读取异常号,然后调用得当的中断处置处罚步伐。在处置处罚步伐返回前,将控制返回给下一条指令。效果就像没有发生过中断一样。
(2)陷阱:陷阱是有意的异常,是执行一条指令的效果,hello执行sleep函数的时候会出现这个异常。
(3)故障:由错误引起,可能被故障处置处罚步伐修正。在执行hello时,可能出现缺页故障。
(4)终止:不可规复的致命错误造成的效果,通常是一些硬件错误,好比DRAM或者 SRAM位被破坏时发生的奇偶错误。
2.键盘上操作导致的异常:
(1)运行时输入回车或乱输入:
(3)运行时输入Ctrl+Z:
输入ps,监视后台步伐:
输入jobs,显示当前停息的进程:
输入pstree,以树状图形式显示所有进程:
输入fg,使停止的进程收到SIGCONT信号,重新在前台运行:
输入kill,-9表现给进程4166发送9号信号,即SIGKILL,杀死进程:
6.7本章小结
本章重要介绍了hello可执行文件的执行过程,包罗进程创建、加载和终止,以及通过键盘输入等过程。从创建进程到进程并回收进程,这一整个过程中需要各种各样的异常和中断等信息。步伐的高效运行离不开异常、信号、进程等概念,正是这些机制支持hello能够顺利地在盘算机上运行。
第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址:逻辑地址指由步伐产生的与段相关的偏移地址部分,也叫相对地址。要经过寻址方式的盘算或变换才得到内存储器中的实际有效地址,即物理地址。从hello的反汇编代码中看到的地址,它们需要通过盘算,通过加上对应段的基地址才能得到真正的地址,这些便是hello中的逻辑地址。
线性地址:是逻辑地址到物理地址变换之间的中间层。步伐hello的代码会产生逻辑地址,hello的反汇编文件中看到的地址(即逻辑地址)中的偏移量,加上对应段的基地址,便得到了hello中内容对应的线性地址。
假造地址:偶然我们也把逻辑地址称为假造地址。因为与假造内存空间的概念类似,逻辑地址也是与实际物理内存容量无关的,是hello中的假造地址。
物理地址:是指出现在CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终效果地址。在hello的运行中,在访问内存时需要通过CPU产生假造地址,然后通过地址翻译得到一个物理地址,并通过物理地址访问内存中的位置。
7.2 Intel逻辑地址到线性地址的变换-段式管理
逻辑地址由段选择符和偏移量构成,线性地址为段首地址与逻辑地址中的偏 移量构成。其中,段首地址存放在段描述符中。而段描述符存放在描述符表中,也就是GDT(全局描述符表)或LDT(局部描述符表)中。
---段式管理特点:
1.段式管理以段为单位分配内存,每段分配一个连续的内存区。
2.由于各段长度不等,所以这些存储区的大小不一。
3.同一进程包罗的各段之间不要求连续。
4.段式管理的内存分配与开释在作业或进程的执行过程中动态进行。
(以下格式自行编排,编辑时删除)
7.3 Hello的线性地址到物理地址的变换-页式管理
页式管理是一种内存空间存储管理的技术,页式管理分为静态页式管理和动 态页式管理。将各进程的假造空间分别成多少个长度相等的页(page),页式管理把 内存空间按页的大小分别成片或者页面(page frame),然后把页式假造地址与内存地址建立逐一对应页表,并用相应的硬件地址变换机构,来解决离散地址变换问题。页式管理采用哀求调页或预调页技术实现了表里存存储器的同一管理。
线性地址(假造地址)由假造页号VPN和假造页偏移VPO构成。首先,MMU 从线性地址中抽取出VPN,而且检查TLB,看他是否因为前面某个内存引用缓存 了PTE的一个副本。TLB从VPN中抽取出TLB索引和TLB标志,查找对应组中 是否有匹配的条目。若掷中,将缓存的PPN返回给MMU。若不掷中,MMU需从页表中的PTE中取出PPN,若得到的PTE无效或标志不匹配,就产生缺页,内核 需调入所需页面,重新运行加载指令,若有效,则取出PPN。末了将线性地址中的VPO与PPN毗连起来就得到了对应的物理地址。
图7.1 Core i7 地址翻译
7.4 TLB与四级页表支持下的VA到PA的变换
每次CPU产生一个假造地址,MMU(内存管理单元)就必须查阅一个PTE(页表条目),以便将假造地址翻译为物理地址。在最糟糕的情况下,这会从内存多取一次数据,代价是几十到几百个周期。如果PTE碰巧缓存在L1中,那么开销就会下降1或2个周期。然而,很多体系都试图消除即使是这样的开销,它们在MMU 中包罗了一个关于PTE的小的缓存,称为翻译后备缓存器(TLB)。
假造地址VA假造页号VPN和假造页偏移VPO构成。若TLB掷中时,所做操作与7.3中相同;若TLB不掷中时,VPN被分别为四个片,每个片被用作到一个页表的偏移量,CR3寄存器包罗L1页表的物理地址。VPN1提供到一个L1 PTE的偏移量,这个PTE包罗L2页表的基地址。VPN2提供到一个L2 PTE的偏移量, 依次类推。末了在L4页表中对应的PTE中取出PPN,与VPO毗连,形成物理地址PA。
图7.3 多级页表管理
7.5 三级Cache支持下的物理内存访问
MMU将物理地址发给L1缓存,缓存从物理地址中取出缓存偏移CO、缓存组索引CI以及缓存标志CT。若缓存中CI所指示的组有标志与CT匹配的条目且有效位为1,则检测到一个掷中条目,读出在偏移量CO处的数据字节,并把它返回给MMU,随后MMU将它传递给CPU。若不掷中,则在下一级cache或是主存中寻找需要的内容,储存到上一级cache后再一次哀求读取。
图7.4 存储器层次布局
7.6 hello进程fork时的内存映射
当fork函数被当前进程调用时,内核为新进程创建各种数据布局,并分配给他一个唯一的pid。
为了给这个新进程创建假造内存,体系创建了当前进程的mm_struct、区域结 构和页表的原样副本。它将两个进程中的每个页面都标志为只读,并将两个进程中的每个区域布局都标志为私有写时复制。
当fork重新进程返回,新进程现在的假造内存刚好和调用fork时存在的假造 内存相同。当这两个进程中的任一个厥后进行写操作时,写时复制机制就会创建新页面,也就为每个进程保持了私有地址空间的抽象概念
7.7 hello进程execve时的内存映射
execve函数在当前进程中加载并运行包罗在可执行目的文件hello中的步伐,用hello步伐有效地更换了当出息序。加载并运行hello需要:
(1)删除已存在的用户区域
(2)映射私有区域:为新步伐hello的代码、数据、bss和栈区域创建新的区域布局。所有这些新的区域都是私有的、写时复制的。
(3)映射共享区域:如果hello步伐与共享对象(或目的)链接,那么这些对象都是动态链接到这个步伐的,然后再映射到用户假造地址空间中的共享区域内。
(4)设置步伐计数器(PC),指向代码的入口点
7.8 缺页故障与缺页中断处置处罚
页面掷中完全是由硬件完成的,而处置处罚缺页是由硬件和操作体系内核协作完 成的:
1.处置处罚器天生一个假造地址,并将它传送给MMU
2.MMU天生PTE地址,并从高速缓存/主存哀求得到它
3.高速缓存/主存向MMU返回PTE
4.PTE中的有效位是0,所以MMU出发了一次异常,传递CPU中的控制到操作体系内核中的缺页异常处置处罚步伐。
5.缺页处置处罚步伐确认出物理内存中的捐躯页,如果这个页已经被修改了,则把它换到磁盘。
6.缺页处置处罚步伐页面调入新的页面,并更新内存中的PTE
7.缺页处置处罚步伐返回到原来的进程,再次执行导致缺页的下令CPU将引起缺页的假造地址重新发送给MMU。因为假造页面已经换存在物理内存中,所以就会掷中。
图7.5 缺页操作
7.9动态存储分配管理
界说:一种内存管理方法。对内存空间的分配、回收等操作在进程执行过程中进行,以便更好地适应体系的动态需求,提高内存利用率。
分配器的基本风格:
1.显示分配器:要求应用显示地开释任何已分配的块。2.隐式分配器:要求分配器检测一个已分配的块何时不再被步伐所利用,那么就开释这个块。隐式分配器也叫做垃圾网络器。
基本方法与策略:
1.带边界标签的隐式空闲链表分配器管理带边界标志的隐式空闲链表的每个块是由一个字的头部、有效载荷、可能的额外添补以及一个字的尾部构成的。当一个应用哀求一个k字节的块时,分配器搜索空闲链表,查找一个符合大小的空闲块来放置这个哀求块。分配器有三种放置策略:初次适配、下一次适配合最佳适配。在开释一个已分配块的时候需要思量是否能与前后空闲块合并,淘汰体系 中碎片的出现。
2.显示空间链表管理显式空闲链表是将空闲块构造为某种形式的显式数据布局。因为根据界说,步伐不需要一个空闲块的主体,所以实现这个数据布局的指针可以存放在这些空闲块的主体内里。如,堆可以构造成一个双向链表,在每个空闲块中,都包罗一个前驱与一个后继指针。放置策略与上述放置策略一致。
7.10本章小结
本章重要介绍了hello进程在执行的过程中的假造内存与物理内存之间的转 换关系,以及一些支持这些转换的硬件或软件机制。同时介绍了在发生缺页异常的时候体系将会如那边理这一异常。末了介绍了动态内存分配的作用以及部分方法与策略。
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模子化:所有IO设备都被模子化为文件,所有的输入和输出都能被当做相应文件的读和写来执行。
设备管理:Linux内核有一个简单、低级的接口,成为Unix I/O,是的所有的输入和输出都能以一种同一且一致的方式来执行。
8.2 简述Unix IO接口及其函数
8.2.1 Unix I/O 接口
(1)打开文件。一个应用步伐通过要求内核打开相应的文件,来宣告它想 要访问一个 I/O 设备,内核返回一个小的非负整数,叫做描述符,它在 后续对此文件的所有操作中标识这个文件,内核记载有关这个打开文件的所有信息。应用步伐只需记住这个描述符。
(2)Linux Shell创建的每个进程都有三个打开的文件:标准输入、标准输出、标准错误。
(3)改变当前的文件位置。对于每个打开的文件,内核保持着一个文件位 置 k,初始为 0,这个文件位置是从文件开头起始的字节偏移量,应用 步伐能够通过执行 seek,显式地将改变当前文件位置 k。
(4)读写文件。一个读操作就是从文件复制 n > 0 个字节到内存,从当前文件位置 k 开始,然后将 k 增加到 k + n。给定一个大小为 m 字节的文件,当 k >= m 时,执行读操作会触发 EOF,应用步伐能检测到它。类似地,写操作就是从内存中复制 n > 0 个字节到一个文件,从当前文件位置 k 开始,然后更新 k。
(5)关闭文件。内核开释文件打开时创建的数据布局,并将这个描述符规复到可用的描述符池中去。
8.2.2 Unix I/O 函数
(1) open函数:int open(char *filename,int flags,mode_t node);
将filename转换为一个文件描述符,而且返回描述符数字。返回的描述符总是在进程中当前没有打开的最小描述符。flags 参数指明了进程打算如何访问这个文件,mode参数指定了新文件的访问权限位。
(2) close函数:int close(int fd);
关闭一个打开的文件,当关闭已关闭的描述符会出错。
(3) read函数:ssize_t read(int fd,void *buf,size_t n);
从描述符为fd的当前文件位置赋值最多n个字节到内存位置buf。返回值-1表现一个错误,而返回值0表现EOF。否则,返回值表现的是实际传送的字节数量。
(4) write函数:ssize_t write(int fd,const void *buf,size_t n);
从内存位置buf复制至多n个字节到描述符fd的当前文件位置。
(5) lseek函数:off_t lseek(int fd, off_t offset, int whence);
应用步伐显示地修改当前文件的位置。
(6) stat函数:int stat(const char *filename,struct stat *buf);
以文件名作为输入,并填入一个stat数据布局的各个成员。
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;
}
第一行目的是让argv指向第一个字符串;第二句的作用是格式化,并返回要打印的字符串的长度,第三句的作用是调用write函数将buf的前i个字符输出到终端,调用了unix I/O。
从vsprintf天生显示信息,到write体系函数,到陷阱-体系调用 int 0x80或syscall等.字符显示驱动子步伐:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
getchar函数内部调用了read函数,通过体系调用read读取存储在键盘缓冲区的ASCII码,直到读到回车符才返回。不过read函数每次会把所有的内容读进缓冲区,如果缓冲区原来非空,则不会调用read函数,而是简简单单的返回缓冲区中最前面的元素
异步异常-键盘中断的处置处罚:键盘中断处置处罚子步伐。接受按键扫描码转成ascii码,保存到体系的键盘缓冲区。
8.5本章小结
本章重要介绍了Linux的I/O设备管理方法、Unix IO接口及其函数,并分析了printf函数和getchar函数的实现。
结论
hello的一生是简单的但是又蕴含着每一个c语言步伐执行前的必经之路:
1.预处置处罚,hello.c文件通过cpp的预处置处罚,得到了扩展后的源步伐文件hello.i
2.编译,hello.i通过编译器的处置处罚,被翻译成了汇编语言步伐hello.s
3.汇编,在汇编器as的处置处罚下,hello.s天生了可重定位文件hello.o
4.链接,链接器将重定位目的文件链接为可执行目的文件hello
5.天生子进程,在shell中输入指定下令shell调用fork函数为hello天生进程。
6.execve加载并运行hello步伐,将它映射到对应假造内存区域,并依需求载入物理内存。
7.hello将在cpu流水线中执行每一条指令
8.步伐运行结束后,父进程会对其进行回收,内核把它从体系中清除。这样,hello就结束了它的一生。在盘算机体系的设计与实现过程中所必须要满意的就是准确步伐的执行必须能输出准确的效果,在这一基础上进行一定的优化能够让步伐执行的更快,包罗cache,流水线,超标量等设计都是基于这些的。在完成大作业的过程中相当于回首了一遍这学期的学习内容,对于盘算机体系设计与实现也有了更深切的感悟。
附件
列出所有的中间产物的文件名,并予以说明起作用。
hello.c 源文件
hello1.i 源文件预处置处罚所得文件
hello1.s 汇编步伐
hello1.o 可重定位文件
hello 可执行文件
hello1.elf 可重定位文件hello.o的解析效果
hello1.txt 可重定位文件hello.o的反汇编效果
Hello1.elf 可执行文件hello的解析效果
Hello1.txt 可执行文件hello的反汇编效果
参考文献
《深入明白盘算机体系》Randal E.Bryant David R.O’Hallaron 机械工业出书社
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]