玛卡巴卡的卡巴卡玛 发表于 2024-9-12 09:11:56

hit 盘算机系统大作业——程序人生


摘  要
一个hello程序的生命周期是从一个源程序开始的,程序员通过编辑器编写并保存了一个.c文件,也就是hello.c文件。运行C预处理器(cpp)将hello.c文件进行预处理,生成一个hello.i文件。hello.i文件为一个修改了的源程序。运行C编译器(ccl)将此hello.i文件翻译为一个汇编语言的文件 hello.s。再运行汇编器( as)将hello.s文件翻译为一个可重定位目标文件 hello.o。最后运行链接器程序(1d),该程序将hello.o和系统各种目标文件链接起来,终极创建出一个可执行月标文件 hello。
关键词:预处理;编译;汇编;链接;进程;存储;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:程序员在Windows或Linux环境下,利用编辑器敲入C语言代码(Program),然后利用gcc编译器对C语言程序执行编译下令:hello.c文件先颠末预处理器cpp,生成hello.i文件,再颠末编译器ccl生成hello.s汇编程序,然后颠末汇编器as生成可重定位目标程序hello.o,最后通过链接器ld链接生成可执行文件hello。在Linux终端执行./hello下令,运行该可执行文件(Process)。
O2O:可执行文件hello执行后,shell通过execve函数和fork函数创建子进程并将hello载入,通过内存映射等为其分配本身的运行空间,CPU在流水线上依次执行每一条指令,内存管理系统利用假造内存地点从TLB或高速缓存中取出相应数据,假如出现不掷中则继续从其下一级缓存中读取数据。最后程序运行竣事后,shell接受到相应的信号,启动信号处理机制,对该进程进行接纳处理,释放其所占的内存并删除有关进程上下文(context),hello程序重新回归0,即From zero to zero。
要运行hello.c起首要对hello.c做四个步调:预处理,编译,汇编,链接,然后这些指令按照可执行目标程序的格式打包,并以二进制磁盘文件的情势保存起来,目标程序写可称为可执行目标文件。
https://i-blog.csdnimg.cn/blog_migrate/b45fd6aaa46bbb7aeb1c12e0498bbff6.png

图 1编译过程
                        
hello.c源程序已经被编译系统翻译成了hello,并被存放在磁盘上,运行可执行文件要把文件名输入到shell中。shell将加载并运行hello程序,然后等待程序停止。hello在屏幕上输出它的消息,然后停止。当在键盘上敲回车键时,shell程序就知道竣事了下令的输入,然后shell执行一系列指令来加载可执行的hello文件,这些指令将hello目标文件中的代码和数据从磁盘复制到主存。一旦目标文件hello中的代码和数据被加载到主存,处理器就开始执行hello程序的main程序中的机器语言指令,将字符串中的字节从主存复制到寄存器文件,再从寄存器文件中复制到表现装备,终极表现在屏幕上。
有两个并发的进程: shell 进程和hello进程.最开始,只有shell进程在运行,即等待下令行上的输入。当我们让它运行hello程序时,shell 通过调用一个专门的函数,即系统调用,来执行我们的请求,系统调用会将控制权传递给操作系统。操作系统保存shell进程的上下文,创建一个新的hello进程及其上下文,然后将控制权传给新的hello进程。hello 进程停止后,操作系统恢复shell进程的上下文,并将控制权传回给它,shell 进程会继续等待下一个下令行输入。

1.2 环境与工具

1.2.1 硬件环境

处理器:Intel® Core™ i7-9750H CPU @ 2.60GHz 2.59GHz
RAM:32.00GB
64位操作系统,基于x64的处理器
1.2.2 软件环境

Windows10 64位;Ubuntu 16.04

1.2.3 开发工具

Visual Studio 2010 64位以上;CodeBlocks 64位;vi/vim/gedit+gcc gdb

1.3 中心结果

表 1 生成文件
文件名
文件作用
hello.c
源代码文件
hello.i
预处理后生成的文件
hello.s
编译后生成的文件
hello.o
汇编后生成的可重定位目标程序
hello
链接之后生成的可执行程序
hello_o_asm.txt
hello.o的反汇编文件
hello_o_elf.txt
hello.o的ELF文件
hello_run_asm
hello的反汇编文件
hello_run_elf
hello的ELF文件
1.4 本章小结

本章进行了hello程序一生P2P,O2O的扼要概述,列出了本实行的硬件环境、软件环境、开发工具和本实行中生成的中心结果文件的名字和作用。



第2章 预处理

2.1 预处理的概念与作用


在C语言的程序中可包括各种以符号#开头的编译指令,这些指令称为预处理下令。预处理下令属于C语言编译器,而不是C语言的构成部分。通过预处理下令可扩展C语言程序设计的环境。
大多数预处理器指令属于下面3种类型:
1. 宏界说:#define 指令界说一个宏,#undef指令删除一个宏界说。
2. 文件包罗:#include指令导致一个指定文件的内容被包罗到程序中。
3. 条件编译:#if,#ifdef,#ifndef,#elif,#else和#dendif指令可以根据编译器可以测试的条件来将一段文本包罗到程序中或排除在程序之外。
预处理阶段:预处理器(cpp)根据以字符#开头的下令,修改源程序。好比第一行的#include<stdio.h>下令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入程序文本中。结果就得到了另一个C程序,通常是以.i作为文件拓展名。
2.2在Ubuntu下预处理的下令

https://i-blog.csdnimg.cn/blog_migrate/e762ec178191245bd974f2145e664d74.png
 

图 2 预处理下令

2.3 Hello的预处理结果剖析

2.3.1 文件增大
https://i-blog.csdnimg.cn/blog_migrate/3d21c0c4f315606d09ce1906935893aa.png
 

图 3 文件增大截图
整个文件变成了3091行

2.3.2源代码保存
https://i-blog.csdnimg.cn/blog_migrate/c4763da827df8f2836b2d8f3c2eb909c.png
 

图 4 源代码截图
hello.c程序原来的内容出现在最后,在此之前是#include <stdio.h>,
#include <unistd.h> ,#include <stdlib.h>的展开
2.3.3 模块化拼接
最开始的一段代码,是hello.c拼接的各种库文件。

https://i-blog.csdnimg.cn/blog_migrate/3c92f1b3c6b425f696cf826ee8551e2f.png
 

图 5 拼接的库文件


中心的某段代码,对很多内部的函数进行声明。
https://i-blog.csdnimg.cn/blog_migrate/4ef4a49ca7d31f99ae3e3dd97b2932cd.png
 

图 6 中心代码

程序的源代码在文件的末尾。
https://i-blog.csdnimg.cn/blog_migrate/d5b134ef5b4bd13b0b5b916f2037a1d6.png
 

图 7 源代码

2.4 本章小结

本章介绍了预处理的概念和作用,同时介绍了Ubuntu环境下预处理的下令,并对预处理生成的hello.i文件进行了扼要分析。


第3章 编译

3.1 编译的概念与作用       

编译:就是把代码转化为汇编指令的过程,汇编指令只是CPU干系的,也就是说C代码和python代码,代码逻辑假如雷同,编译完的结果实在是一样的。编译程序所要作得工作就是通过词法分析和语法分析,在确认全部的指令都符合语法规则之后,将其翻译成等价的中心代码表示或汇编代码。
在hello的一生中,编译器将文本文件 hello.i 翻译成文本文件 hello.s。
C编译器ccl通过词法分析和语法分析,将正当指令翻译成等价汇编代码,颠末编译过程,编译器将文本文件hello.i翻译成汇编语言文件hello.s。在hello.s中,以文本的情势形貌了一条条低级机器语言指令。

3.2 在Ubuntu下编译的下令

https://i-blog.csdnimg.cn/blog_migrate/b2f62e86c1827b14e260ebdf0bb04a4d.png
 

图 8 编译下令
3.3 Hello的编译结果剖析

https://i-blog.csdnimg.cn/blog_migrate/2a217872d8286d78e5910bd5e1779266.png
 

图 9 C语言源程序
3.3.1整数和字符串常量

https://i-blog.csdnimg.cn/blog_migrate/e049f6879f7f0ec0d188980f426b5f5f.png
 https://i-blog.csdnimg.cn/blog_migrate/67b6b33e1a8dcfd435aca9e38936cd0a.png
 

图 10 常量4保存位置

在语句中,常量4保存位置在.text节
https://i-blog.csdnimg.cn/blog_migrate/5efe84e2bb55446a4e9af0c73e92ac80.png
 https://i-blog.csdnimg.cn/blog_migrate/6336158ec9dd9e7b2055c467b6bad0e7.png
 
 


图 11 常量0和5保存位置
数字0和5也被存在.text节中

https://i-blog.csdnimg.cn/blog_migrate/9dc50d094b893e3e463993d2dcc3db61.png
https://i-blog.csdnimg.cn/blog_migrate/3564fb81d38ac62e6f9e5c2773469a46.png 
 

图 12 函数保存位置
printf()、scanf()中的字符串则被存储在.rodata节中
3.3.2 变量
全局变量:
已经初始化而且初始值非零的全局变量储存在.data节,它的初始化不需要汇编语句,而是通过假造内存请求二进制零的页直接完成的。
局部变量:
局部变量存储在寄存器或栈中。程序中的局部变量i界说
https://i-blog.csdnimg.cn/blog_migrate/2fb2452d06a79b80cde91fabc7169fbb.png
 https://i-blog.csdnimg.cn/blog_migrate/fbc48d8ba54920590372223d70fec22f.png
 


图 13 变量i保存位置
表示i被保存在%rsp的位置上
3.3.3 算数操作
https://i-blog.csdnimg.cn/blog_migrate/9e59bcd88a459cecf005be8cee5e55bb.png
 
https://i-blog.csdnimg.cn/blog_migrate/5b15f05c5b7ee22f0a1ac0c3e9c3ab46.png
 


图 14 算数操作
栈上存储的i变量加一





3.3.4关系操作和控制转移
https://i-blog.csdnimg.cn/blog_migrate/b59e67acc2d04c53ee67f3c31e19957b.png
https://i-blog.csdnimg.cn/blog_migrate/10b3c06602808451b0c980a411a1af31.png 
 
图 15 判断argc
将程序传入参数argc是否即是4,je用于判断cmpl产生的条件码,看是否相等
https://i-blog.csdnimg.cn/blog_migrate/931cbfc78dd9cdd9a84952c4ac571932.png
https://i-blog.csdnimg.cn/blog_migrate/bcf34fb85b46dd5f2917c5a10cc405c4.png 
 




图 16 for循环中的循环条件
这里采取了初始化– 跳转– 循环判断的模式。jle用于判断cmpl产生的条件码,若后一个操作数的值小于即是前一个,则重新执行循环;
3.3.5 数组/指针/布局操作
主函数main的参数中有指针数组char *argv[]
https://i-blog.csdnimg.cn/blog_migrate/55cf782de7f1349ad09ca11f051fae90.png
https://i-blog.csdnimg.cn/blog_migrate/713d74121af618708c46ba598e5c6beb.png 
 



图 17 main函数
char *占8个字节,argc表示传入的长度
把argc存在%edi中
把argv存在%rsi中
https://i-blog.csdnimg.cn/blog_migrate/426233633d135a97d254e9e48d5d82e6.png
 

图 18 汇编代码
M[%rbp-32+16]和M[%rbp-32+8],分别得到argv和argv两个字符串的首地点
3.3.6函数操作
X86-64中,过程调用传递参数规则:第1~6个参数一次储存在%rdi、%rsi、%rdx、%rcx、%r8、%r9这六个寄存器中,剩下的参数保存在栈当中。

https://i-blog.csdnimg.cn/blog_migrate/ee6a831ee82e062345729f562dcc9644.png
https://i-blog.csdnimg.cn/blog_migrate/489d9acdf5792bbcca683fabba6ef09b.png 
 






图 19 函数操作代码
把argc存在%edi中
把argv存在%rsi中
https://i-blog.csdnimg.cn/blog_migrate/83f32693191e41e52db17b68f193271b.png
https://i-blog.csdnimg.cn/blog_migrate/8b49582c66a29fc5edfd1da9a5149035.png 
 


图 20 print函数干系代码
传入了字符串参数的首地点,条件满足后调用







https://i-blog.csdnimg.cn/blog_migrate/a61fc6f0a65ea05db3b788a05509d8cf.png
https://i-blog.csdnimg.cn/blog_migrate/e2b8b81043912146fd2144763a155977.png 
 图 21 for函数代码
https://i-blog.csdnimg.cn/blog_migrate/9c7eaf7580b3b760e859d21cd3279ca8.png
https://i-blog.csdnimg.cn/blog_migrate/8b18826d275258bfccca37b68a440085.png 
 


图 22 exit函数代码
参数传入1
https://i-blog.csdnimg.cn/blog_migrate/1bca6b64213f946ccac8be0c4b23f1fb.png

 https://i-blog.csdnimg.cn/blog_migrate/f4d691eb0a8284102f43cabe40a9639d.png
 


图 23 sleep函数代码
随着for循环被调用
https://i-blog.csdnimg.cn/blog_migrate/a8862ba3e4fa1974fb7ae24c547c963e.png
https://i-blog.csdnimg.cn/blog_migrate/0802cdfac6d1509d7ca0c40dad0527a4.png 
 



图 24 getchar函数代码




3.4 本章小结

本章介绍了编译的概念以及过程。通过hello函数分析了c语言怎样转换成为汇编代码。介绍了汇编代码怎样实现变量、常量、传递参数以及分支和循环。

第4章 汇编

4.1 汇编的概念与作用

概念:汇编器(as)将.s 文件翻译成机器语言指令,把这些指令打包成一种叫可重定位目标程序的格式,并将结果保存在目标文件(后缀为.o)中。
作用:将汇编代码转换成真正机器可以读懂的二进制代码。
4.2 在Ubuntu下汇编的下令



https://i-blog.csdnimg.cn/blog_migrate/dd2720c97157d7148f0e3b051d676399.png图 25 汇编下令
 
4.3 可重定位目标elf格式

分析hello.o的ELF格式,用readelf等列出其各节的根本信息,特别是重定位项目分析。
4.3.1 readelf下令:
https://i-blog.csdnimg.cn/blog_migrate/c6dfd2b2ea004550ec55d0409e5113b2.png
 
图 26 readelf下令

4.3.2 ELF头:
包罗了系统信息,编码方式,ELF头大小,节的大小和数量等等一系列信息。ELF头内容如下:
ELF头以一个16字节的序列开始,这个序列形貌了生成该文件的系统的字的大小和字节顺序。ELF剩下的部分包罗帮助连接器语法分析息争释目标文件的信息。包括ELF头的大小、目标文件的类型等。
一个范例的ELF可重定位目标文件包罗下面几个节:
.txt:已编译程序的机器代码。
.rodata:只读数据。
.data:已初始化的全局和静态局部C变量。
.bss:未初始化的全局和静态C变量,以及全部被初始化为0的全局或静态变量。
.symtab:一个符号表,它存放在程序中界说和引用的函数和全局变量的信息。
.rel.text:一个.text节中位置的列表。
.rel.data:被模块引用或界说的全部全局变量的重定位信息。
.debug:一个调试符号表,其条目是程序中界说的局部变量和类型界说,程序中界说和引用的全局变量以及原始的C源文件。
https://i-blog.csdnimg.cn/blog_migrate/553a11c798fb2bef4fa1772548ad1692.png
 
图 27 ELF头截图
4.3.3节头表
形貌了.o文件中出现的各个节的类型、位置、所占空间大小等信息。
https://i-blog.csdnimg.cn/blog_migrate/7ff25e8bde8a0438d4d08f552d5dc279.png
 
图 28 节头表截图
4.3.4 重定位节:
各个段引用的外部符号等在链接时需要通过重定位对这些位置的地点进行修改。链接器会通过重定位节的重定位条目盘算出正确的地点。
hello.o需重定位:.rodata中的模式串,puts,exit,printf,slepsecs,sleep,getchar等符号。

https://i-blog.csdnimg.cn/blog_migrate/a98b527ec4fe908e56b82c861408347a.png
 
图 29 重定位节
4.3.5符号表:
.symtab存放在程序中界说和引用的函数和全局变量的信息。
https://i-blog.csdnimg.cn/blog_migrate/76d24c60b40d64adbf520a6864d791e2.png
 
图 30 符号表










4.4 Hello.o的结果剖析

https://i-blog.csdnimg.cn/blog_migrate/aea03c9c66b66535a29ae1ebf067d500.png
 

图 31 反汇编hello.o

https://i-blog.csdnimg.cn/blog_migrate/aa08d5af83788bc3834d693c4a39555e.png
 
图 32 反汇编文件


与第三章的hello.s对比,起首在指令前增加了其十六进制表示,即机器语言。其次在操作数上,hello.s中操作数为十进制,而hello.o的反汇编中操作数为十六进制。在条件跳转语句上,hello.o的反汇编文件用的是相对偏移量,而hello.s中是函数名。
在数的表示上,hello.s中的操作数体现为十进制,而hello.o反汇编代码中的操作数为十六进制。说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。
在控制转移上,hello.s使用.L2和.LC1等段名称进行跳转,而反汇编代码使用目标代码的假造地点跳转。不过现在留下了重定位条目,跳转地点为零。它们将在链接之后被填写正确的位置。
在函数调用上,hello.s直接call函数名称,而反汇编代码中call的是目标的假造地点。但和上一条的情况类似,只有在链接之后才能确定运行执行的地点,现在目的地点是全0,并留下了重定位条目。

4.5 本章小结

本章介绍了汇编。颠末汇编器,汇编语言转化为机器语言,hello.s文件转化为hello.o可重定位目标文件。我们研究了可重定位目标文件elf格式,打仗了了readelf下令、elf头、节头部表、重定位节、符号表。我们对比hello.s和hello.o,分析了汇编语言到机器语言的变化。








第5章 链接

5.1 链接的概念与作用

概念:链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。
作用:链接可以使得分离编译成为可能。不消将一个大型的应用程序组织为一个巨大的源文件,而是可以将它分解成更小、更好管理的模块,可以独立地修改和编译这些模块,当我们改变这些模块中的一个时,只需简朴地重新编译它,并重新链接应用,而不必重新编译其他文件。
5.2 在Ubuntu下链接的下令

https://i-blog.csdnimg.cn/blog_migrate/99e856c400729e2ee37b9387928acf8c.png
 
图 33 链接下令
5.3 可执行目标文件hello的格式


     https://i-blog.csdnimg.cn/blog_migrate/cd3f40019a93f7a192724f3aaee9820d.png
 
图 34 hello的ELF格式












5.3.1 ELF文件头:

https://i-blog.csdnimg.cn/blog_migrate/435d6023f5dc62ed93ce71b9814dcefa.png
 
图 35 ELF头
5.3.2 节头:
形貌了各个节的大小、偏移量和其他属性。链接器链接时,会将各个文件的雷同段合并成一个大段,而且根据这个大段的大小以及偏移量重新设置各个符号的地点。
https://i-blog.csdnimg.cn/blog_migrate/dfe0dde4b9485d3ff2945a8f6fa79d13.png
 
图 36 节头

5.4 hello的假造地点空间


使用edb加载hello, data dump窗口可以查看加载到假造地点中的hello程序。program headers告诉链接器运行时加载的内容并提供动态链接需要的信息。

可以看出假造地点空间起始地点为0x400000
https://i-blog.csdnimg.cn/blog_migrate/30b1c57ddb19745bcfb0b9171149a046.png
 
图 37 elf文件

由elf文件可看出.inerp偏移量为0x2e0,在edb对应位置找到:


https://i-blog.csdnimg.cn/blog_migrate/acd8e4fd487dff8725e38ee257d44394.png
https://i-blog.csdnimg.cn/blog_migrate/ab1f9f1c6930e4726a69dc1740eb816c.png 
 
图 38 .inerp偏移量





同样可以在edb中找到.text段和.rodata 段等:


https://i-blog.csdnimg.cn/blog_migrate/8711ac106c77cb967970f4fd4fa5797b.png
 https://i-blog.csdnimg.cn/blog_migrate/0cc53903f74fe4627f390e3d59bd479a.png
 

图 39 .text段
https://i-blog.csdnimg.cn/blog_migrate/66cbbc7d89d1bd934edf6210d7118478.png
https://i-blog.csdnimg.cn/blog_migrate/5c5d94a3460f98c243d5b0c37c34c7fb.png 
 



图 40 .rodata 段


程序包罗PHDR,INTERP,LOAD,DYNAMIC,NOTE,GNU_STACK,GNU_RELRO几个部分,其中PHDR 保存程序头表。INTERP 指定在程序已经从可执行文件映射到内存之后,必须调用的解释器。LOAD 表示一个需要从二进制文件映射到假造地点空间的段。其中保存了常量数据、程序的目标代码等。DYNAMIC 保存了由动态链接器使用的信息。NOTE 保存辅助信息。GNU_STACK:权限标志,用于标志栈是否是可执行。GNU_RELRO:指定在重定位竣事之后哪些内存地区是需要设置只读。


                https://i-blog.csdnimg.cn/blog_migrate/f886312e40c3acdef176f5ea301ea783.png
 

图 41 假造内存分配表示图
5.5 链接的重定位过程分析


https://i-blog.csdnimg.cn/blog_migrate/3ff574472bed096e3d46ce48e48e34c7.png
 
https://i-blog.csdnimg.cn/blog_migrate/c336ae92355d656b73fc7b26cfd0e819.png
 
图 42 重定位文件




5.5.1 新增函数:
链接参加了在hello.c中用到的库函数,如exit、printf、sleep、getchar等函数。

https://i-blog.csdnimg.cn/blog_migrate/5d56ec63d93393effba531423e0ee544.png
 
图 43 库函数

5.5.2 新增节:
hello中增加了.init和.plt节,和一些节中界说的函数。
https://i-blog.csdnimg.cn/blog_migrate/5543079d6f5aee0ab1c41bcbe1b81a4c.png
 
图 44 新增节
5.5.3 函数调用地点:
hello实现了调用函数时的重定位,因此在调用函数时调用的地点已经是函数确切的假造地点。

https://i-blog.csdnimg.cn/blog_migrate/f6534aad269f52ffc629f1c43edebdc8.png
 
图 45 调用的假造地点


5.5.4控制流跳转地点:
hello实现了调用函数时的重定位,因此在跳转时调用的地点已经是函数确切的假造地点。

https://i-blog.csdnimg.cn/blog_migrate/6b837a2d6833d092df0d751d2f4136a1.png
 
图 46 假造地点
5.5.5 链接的过程:
链接就是链接器(ld)将各个目标文件(各种.o文件)组装在一起,文件中的各个函数段按照一定规则累积在一起。从.o提供的重定位条目将函数调用和控制流跳转的地点填写为终极的地点。

5.6 hello的执行流程

从加载hello到_start,到call main,以及程序停止的全部过程如下:
_dl_start 地点:0x7f894f9badf0
_dl_init 地点: 0x7f894f9cac10
_start 地点:0x4010f0
_libc_start_main 地点:0x7fce59403ab0
_cxa_atexit 地点:0x7f38b81b9430
_libc_csu_init 地点:0x4011c0
_setjmp 地点:0x7f38b81b4c10
_sigsetjmp 地点:0x7efd8eb79b70
_sigjmp_save 地点:0x7efd8eb79bd0
main 地点:0x401125 
若argc != 4
puts 地点:0x401090
exit 地点:0x4010d0
此时输出窗口打印出“用法: Hello 学号 姓名 秒数!”,程序竣事
若argc == 4
printf 地点:0x4010a0
atoi  地点:0x4010c0
sleep 地点:0x4010e0 (以上三个函数在循环体中执行5次)
此时窗口打印5行“Hello 2021112078 ljs”
https://i-blog.csdnimg.cn/blog_migrate/4e9faf262839489ec5f850e89c29e017.png
 
图 47 程序输入示例

 
getchar 地点:0x4010b0
等待用户输入回车,输入回车后:
_dl_runtime_resolve_xsave 地点:0x7f5852241680
_dl_fixup 地点:0x7f5852239df0
_uflow 地点:0x7f593a9a10d0
exit 地点:0x7f889f672120
程序停止。
5.7 Hello的动态链接分析

在elf文件中可以找到:
https://i-blog.csdnimg.cn/blog_migrate/3374365212ddabbbe359d836c372f262.png
 
图 48 elf文件
进入edb查看:
https://i-blog.csdnimg.cn/blog_migrate/778a6524ec62223bc27b29676ab108b3.png
 
https://i-blog.csdnimg.cn/blog_migrate/f12bda85327335dfd513bc9ce63f1f5e.png
 
图 49 edf查看文件
动态链接的根本头脑是把程序按照模块拆分成各个相对独立的部分,在程序运行时才将它们链接在一起形成一个完备的程序,在调用函数时,编译器没有办法猜测这个函数运行时的地点,正常的方法是为该引用生成一条重定位记录,然后动态链接器在程序加载的时候再剖析它。

延时绑定是通过GOT和PLT实现的,根据hello.elf可知,GOT起始表位置为0x404000
5.8 本章小结

本章主要介绍了链接的过程,利用edb、gdb、objdump等工具对链接的ELF文件、假造地点空间、重定位过程等进行具体分析,同时对hello程序的执行流程以及动态链接也做了扼要介绍。至此,hello程序已经从程序代码转换成了一个可执行文件。

第6章 hello进程管理

6.1 进程的概念与作用

概念:进程的经典界说就是一个执行中程序的实例。系统中的每个程序都运行在某个进程的上下文中。上下文是由程序正确运行所需的状态构成的。这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件形貌符的聚集。
作用:进程为程序提供了一种假象,程序好像是独占的使用处理器和内存。处理器好像是无间断地一条接一条地执行我们程序中的指令。
6.2 简述壳Shell-bash的作用与处理流程

shell是一个交互型的应用级程序,也被称为下令剖析器,它为用户提供一个操作界面,接受用户的输入下令,并调度相应的应用程序。
起首从终端读入输入的下令,对输入的下令进行剖析,假如该下令为内置下令,则立即执行下令,否则调用fork创建一个新的子进程,在该子进程的上下文中执行指定的程序。判断该程序为前台程序还是背景程序,假如为前台程序则等待程序执行竣事,若为背景程序则将其放回背景并返回。在过程中 shell可以接受从键盘输入的信号并对其进行处理。
6.3 Hello的fork进程创建过程

https://i-blog.csdnimg.cn/blog_migrate/264833ff1e33ef1d160184574e811796.png
 
图 50 fork进程调用过程
6.4 Hello的execve过程

Execve的参数包括需要执行的程序(通常是argv)、参数argv、环境变量envp。 1. 删除已存在的用户地区(自父进程独立)。
2. 映射私有区:为Hello的代码、数据、.bss和栈地区创建新的地区布局,全部这些地区都是私有的、写时才复制的。
3. 映射共享区:好比Hello程序与标准C库libc.so链接,这些对象都是动态链接到Hello的,然后再用户假造地点空间中的共享地区内。
4. 设置PC:exceve做的最后一件事就是设置当前进程的上下文中的程序计数器,使之指向代码地区的入口点。
5. execve在调用乐成的情况下不会返回,只有当出现错误时,比方找不到需要执行的程序时,execve才会返回到调用程序。
https://i-blog.csdnimg.cn/blog_migrate/d322df8e0bc4b5ee6f3be34345630cee.png
 
图 51 execve过程
6.5 Hello的进程执行

6.5.1 逻辑控制流和时间片:
进程的运行本质上是CPU不断从程序计数器 PC 指示的地点处取出指令并执行,值的序列叫做逻辑控制流。操作系统会对进程的运行进行调度,执行进程A->上下文切换->执行进程B->上下文切换->执行进程A->… 如此循环往复。 在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占了的进程,这种决策就叫做调度,是由内核中称为调度器的代码处理的。当内核选择一个新的进程运行,我们说内核调度了这个进程。在内核调度了一个新的进程运行了之后,它就抢占了当前进程,并使用上下文切换机制来将控制转移到新的进程。在一个程序被调运行开始到被另一个进程打断,中心的时间就是运行的时间片。
6.5.2 用户模式和内核模式:
用户模式的进程不允许执行特殊指令,不允许直接引用地点空间中内核区的代码和数据。
内核模式进程可以执行指令集中的任何下令,而且可以访问系统中的任何内存位置。
6.5.3 上下文:
上下文就是内核重新启动一个被抢占的进程所需要恢复的原来的状态,由寄存器、程序计数器、用户栈、内核栈和内核数据布局等对象的值构成。
6.5.4 调度的过程:
在对进程进行调度的过程,操作系统主要做了两件事:加载保存的寄存器,切换假造地点空间。
6.5.5 用户态与核心态转换:
为了能让处理器安全运行,需要限定应用程序可执行指令所能访问的地点范围。因此划分了用户态与核心态。
核心态可以说是拥有最高的访问权限,处理器以一个寄存器当做模式位来形貌当前进程的特权。进程只有故障、停止或陷入系统调用时才会得到内核访问权限,其他情况下始终处于用户权限之中,包管了系统的安全性。

6.6 hello的异常与信号处理

6.6.1 异常类型与处理方式
表2:异常类型
类型
缘故原由
同步/异步
返回行为
停止
来自I/O装备的信号
异步
总是返回下一条指令
陷阱
故意的异常
同步
总是返回下一条指令
故障
潜伏可恢复的错误
同步
可能返回当前指令
停止
不可恢复的错误
同步
不会返回

6.6.2 发送信号
按下Ctrl+Z:进程收到 SIGSTP 信号, hello 进程挂起。用ps查看其进程PID,可以发现hello的PID是3377;再用jobs查看此时hello的背景 job号是1,调用 fg 将其调回前台。

https://i-blog.csdnimg.cn/blog_migrate/585b7c730779729ea69f0c563487ad61.png
 


图 52 输入ctrl+z

Ctrl+C:进程收到 SIGINT 信号,竣事 hello。在ps中查询不到其PID,在job中也没有表现,可以看出hello已经被彻底竣事。
https://i-blog.csdnimg.cn/blog_migrate/63983adfb461c5c5a2022ebdf4739d68.png
 
图 53 输入ctrl+c






中途乱按:只是将屏幕的输入缓存到缓冲区。乱码被认为是下令。
https://i-blog.csdnimg.cn/blog_migrate/2cbaf9b9e34030d8a2468f86faba6be3.png
 
图 54 中途乱按

Kill下令:挂起的进程被停止,在ps中无法查到到其PID。

https://i-blog.csdnimg.cn/blog_migrate/21075d6c74cc8c1cde24af79cfcdbc17.png
 
图 55 输入Kill下令



6.7本章小结

本章介绍了有关进程管理的多个概念。介绍了shell的作用和处理流程,以及利用fork创建子进程、利用execve加载进程的方法。展示hello程序执行的具体过程,以及异常信号的处理机制。

第7章 hello的存储管理

7.1 hello的存储器地点空间

7.1.1 逻辑地点
逻辑地点(Logical Address)是指由程序产生的与段干系的偏移地点部分。在这里指的是hello.o中的内容。
7.1.2 线性地点
线性地点(Linear Address)是逻辑地点到物理地点变换之间的中心层。程序hello的代码会产生段中的偏移地点,加上相应段的基地点就生成了一个线性地点。
7.1.3 假造地点
CPU启动保护模式后,程序hello运行在假造地点空间中。注意,并不是全部的“程序”都是运行在假造地点中。CPU在启动的时候是运行在实模式的,Bootloader以及内核在初始化页表之前并不使用假造地点,而是直接使用物理地点的。
7.1.4 物理地点
放在寻址总线上的地点。放在寻址总线上,假如是读,电路根据这个地点每位的值就将相应地点的物理内存中的数据放到数据总线中传输。假如是写,电路根据这个地点每位的值就在相应地点的物理内存中放入数据总线上的内容。物理内存是以字节(8位)为单位编址的。

7.2 Intel逻辑地点到线性地点的变换-段式管理

在 Intel 平台下,逻辑地点(logical address)是 selector:offset 这种情势,selector 是 CS 寄存器的值,offset 是 EIP 寄存器的值。假如用 selector 去 GDT( 全局形貌符表 ) 里拿到 segment base address(段基址) 然后加上 offset(段内偏移),这就得到了 linear address。我们把这个过程称作段式内存管理。
一个逻辑地点由段标识符和段内偏移量构成。段标识符是一个16位长的字段(段选择符)。可以通过段标识符的前13位,直接在段形貌符表中找到一个具体的段形貌符,这个形貌符就形貌了一个段。
全局的段形貌符,放在“全局段形貌符表(GDT)”中,一些局部的段形貌符,放在“局部段形貌符表(LDT)”中。
给定一个完备的逻辑地点段选择符+段内偏移地点,看段选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地点和大小。拿出段选择符中前13位,可以在这个数组中,查找到对应的段形貌符,就得到了其基地点。Base + offset = 线性地点。
https://i-blog.csdnimg.cn/blog_migrate/1728ab3006f93bb9832d861ef7b56c3e.png
 
图 56 地点盘算过程




7.3 Hello的线性地点到物理地点的变换-页式管理


页式管理是一种内存空间存储管理的技能,页式管理分为静态页式管理和动态页式管理。将各进程的假造空间划分成多少个长度相等的页(page),页式管理把内存空间按页的大小划分成片大概页面(page frame),然后把页式假造地点与内存地点创建一一对应页表,并用相应的硬件地点变换机构,来解决离散地点变换题目。页式管理接纳请求调页或预调页技能实现了内外存存储器的统一管理。
https://i-blog.csdnimg.cn/blog_migrate/4d963e3c8fd2160f35f0b32f9046ccb2.png
 
图 57 假造地点存储原理

7.4 TLB与四级页表支持下的VA到PA的变换

7.4.1翻译后备缓冲器
每次CPU产生一个假造地点,MMU(内存管理单元)就必须查阅一个PTE(页表条目),以便将假造地点翻译为物理地点。在最糟糕的情况下,这会从内存多取一次数据,代价是几十到几百个周期。假如PTE碰巧缓存在L1中,那么开销就会下降1或2个周期。然而,许多系统都试图消除即使是这样的开销,它们在MMU中包括了一个关于PTE的小的缓存,称为翻译后备缓存器(TLB)。


7.4.2 多级页表:
将假造地点的VPN划分为相等大小的差别的部分,每个部分用于寻找由上一级确定的页表基址对应的页表条目。
https://i-blog.csdnimg.cn/blog_migrate/d6ba9df927b737c1dca4005d2622b16e.png
 
图 58 使用k级页表的地点翻译
7.4.3 VA到PA的变换
处理器生成一个假造地点,并将其传送给MMU。MMU用VPN向TLB请求对应的PTE,假如掷中,则跳过之后的几步。MMU生成PTE地点(PTEA).,并从高速缓存/主存请求得到PTE。假如请求不乐成,MMU向主存请求PTE,高速缓存/主存向MMU返回PTE。PTE的有效位为零, 因此 MMU触发缺页异常,缺页处理程序确定物理内存中的牺牲页 (若页面被修改,则换出到磁盘——写回计谋)。缺页处理程序调入新的页面,并更新内存中的PTE。缺页处理程序返回到原来进程,再次执行导致缺页的指令。
在多级页表的情况下,无非就是不断通过索引 – 地点 – 索引 - 地点重复四次进行寻找。

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

CPU发送一条假造地点,随后MMU按照7.4所述的操作得到了物理地点PA。根据cache大小组数的要求,将PA分为CT(标志位)CI(组索引),CO(块偏移)。根据CI寻找到正确的组,依次与每一行的数据比力,有效位有效且标志位一致则掷中。假如掷中,直接返追念要的数据。假如不掷中,就依次去L2,L3,主存判断是否掷中,掷中时将数据传给CPU同时更新各级cache的储存。


https://i-blog.csdnimg.cn/blog_migrate/2e0039b2a06cef8c18ae753fba8dc107.png
 
图 59 物理内存访问
7.6 hello进程fork时的内存映射

当fork函数被当前进程调用时,内核为新进程创建各种数据布局,并分配给它一个唯一的PID,同时为这个新进程创建假造内存,创建当前进程的mm_struct、地区布局和页表的原样副本。它将两个进程中的每个页面都标志位只读,并将两个进程中的每个地区布局都标志为私有的写时复制。
当fork在新进程中返回时,新进程现在的假造内存刚好和调用fork时存在的假造内存雷同。当这两个进程中的任一个厥后进行写操作时,写时复制机制就会创建新页面。
https://i-blog.csdnimg.cn/blog_migrate/c9d30ca68acfea7aad988a91a5ac19bf.png
 
图 60 内存映射
7.7 hello进程execve时的内存映射

exeeve 函数调用驻留在内核地区的启动加载器代码,在当前进程中加载并运行包罗在可执行目标文件 hello中的程序,用hello程序有效地替代了当出息序。加载并运行hello需要以下几个步调;
(1)删除已存在的用户地区。删除当前进程假造地点的用户部分中的已存在的地区布局。
(2)映射私有地区。为新程序的代码、数据、bss和栈地区创建新的地区布局,全部这些新的地区都是私有的、写时复制的。代码和数据地区被映射为hello文件中的.text 和l.data区,bss地区是请求二进制零的,映射到匿名文件,其大小包罗在
hello 中,栈和堆地点也是请求二进制零的,初始长度为零。
(3)映射共享地区。hello程序与共享对象libc.so链接,libc.so是动态链接到这个程序中的,然后再映射到用户假造地点空间中的共亭地区内。
(4)设置程序计数器。cxccve做的最后一件事情就是设置当前进程上下文的程序计数器,使之指向代码地区的入口点。如图所示:

https://i-blog.csdnimg.cn/blog_migrate/982f491aca7ae18bb1929b4397b48fb3.png
 
图 61 execve时的内存映射




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

假如程序执行过程中发生了缺页故障,则内核调用缺页处理程序。处理程序执行如下步调:

(1)查抄假造地点是否正当,如界不正当则触发一个段错误,停止这个进程。
(2)查抄进程是否有读、写或执行该地区页面的权限,假如不光有则触发保护异常,程序停止。
(3)两步查抄都无误后,内核选择一个牺牲页面,假如该页面被修改过则将其交换出去,换入新的页面并更新页表。然后将控制转移给hello进程,再次执行触发缺页故障的指令。

https://i-blog.csdnimg.cn/blog_migrate/8be1930317851d031a6b38ecda465cd4.png
 
图 62 缺页处理













7.9动态存储分配管理

动态储存分配管理使用动态内存分配器(如malloc)来进行。动态内存分配器维护着一个进程的假造内存地区,称为堆。分配器将堆视为一组差别大小的块的聚集。每个块就是一个一连的假造内存页,要么是已分配的,要么是空闲的。
已分配的块显式地保存为供应用程序使用。
空闲块保持空闲,直到它显式地被应用所分配。
一个已分配的块保持已分配的状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。动态内存分配主要有两种根本方法与计谋:

7.9.1 带边界标签的隐式空闲链表分配器管理
带边界标志的隐式空闲链表的每个块是由一个字的头部、有效载荷、额外填充以及一个字的尾部构成。
隐式空闲链表:空闲块通过头部的大小字段隐含地连接着。分配器遍历堆中全部的块,间接地遍历整个空闲块的聚集。
当一个应用请求一个k字节的块时,分配器搜索空闲链表,查找一个足够大的可以放置所请求块的空闲块。分配器有三种放置计谋:首次适配、下一次适配和最佳适配。分配器在面对释放一个已分配块时,可以合并相邻的空闲块,其中一种简朴的方式,是利用隐式空闲链表的边界标志来进行合并。
7.9.2 显式空间链表管理
显式空闲链表是将堆的空闲块组织成一个双向链表,在每个空闲块中,都包罗一个前驱与一个后继指针。进行内存管理。在显式空闲链表中。可以接纳后进先出的顺序维护链表,将最新释放的块放置在链表的开始处,也可以接纳按照地点顺序来维护链表,其中链表中每个块的地点都小于它的后继地点,在这种情况下,释放一个块需要线性时间的搜索来定位合适的前驱。
7.10本章小结


本章介绍了存储器地点空间、段式管理、页式管理,VA 到 PA 的变换、物理内存访问, hello 进程fork时和execve 时的内存映射、缺页故障与缺页停止处理、包括隐式空闲链表和显式空闲链表的动态存储分配管理。


第8章 hello的IO管理

8.1 Linux的IO装备管理方法

Linux将文件全部的I/O装备都模子化为文件,乃至内核也被映射为文件。这种将装备优雅地映射为文件的方式,允许Linux内核引出一个简朴、低级的应用接口,称为Unix I/O。Linux就是基于Unix I/O实现对装备的管理。

装备的模子化:全部IO装备都被模子化为文件,全部的输入和输出都能被当做相应文件的读和写来执行。
装备管理:Linux 内核有一个简朴、低级的接口,成为Unix IO,是的全部的输入和输出都能以一种统一且一致的方式来执行。
8.2 简述Unix IO接口及其函数

8.2.1 Unix lO接口

(1)打开文件:一个应用程序通过要求内核打开相应的文件,来宣告它想要访间一个I/O装备。内核返回一个小的非负整致,叫做形貌符,他在后续对此文件的全部操作中标识这个文件。
(2)改变当前的文件位置。对于每个打开的文件,内核保持着一个文件位置k,初始为0。这个文件位置是从文件开头起始的字节偏移量。
(3)读写文件。一个读操作就是从文件复制n个字节到内存,从当前文f位置k 开始,然后增加k到k+n。给定一个大小为m字节的文件,k>=m时执行型读操作会触发一个EOF的条件。
(4)关闭文件。内核释放文件打开时创建的数据布局,并将这个形貌符恢复到可用的形貌符地点中。






8.2.2 Unix IO函数
(1)int open(char* filename,int flags,mode_t mode)
进程通过调用open函数来打开一个存在的文件或是创建一个新文件的。open函数将filename转换为一个文件形貌符,而且返回形貌符数字,返回的形貌符总是在进程中当前没有打开的最小形貌符,flags参数指明了进程计划怎样访问这个文件,mode参数指定了新文件的访问权限位。
(2)int close(fd)
进程通过调用close函数关闭一个打开的文件, fd是需要关闭的文件的形貌符。(3 )ssize_t read(int fd,void *buf,sizc _t n)
rcad函数从形貌符为fd的当前文件位置赋值最多n个字节到内存位置 buf。返回值-1表示一个错误,0表示EOF,否则返回值表示的是实际传送的字节数量。(4)ssize_t wirte(int fd,const void *buf,size_t n)
write 函数从内存位置 buf复制至多n个字节到形貌符为fd的当前文件位置。
https://i-blog.csdnimg.cn/blog_migrate/533c102a0f98edffeb8ca0e6e8a51450.png
 
图 63 文件处理进程表














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;
}
va_list是字符指针, (char*)(&fmt+4)长示fmt后的第一个参数的地点。vsprintl函数返回值是要打印出来的字符中的长度,其作用是格式化,产生格式化的输出并保存在 buf中。最后的write函数即为写操作,把 buf中的i个元素的值写到终端。、

在write函数中,追踪之后的结果如下:
write:
mov eax,_NR_write
mov ebx,
mov ecx,
int INT_VECTOR_SYS_CALL
一个int INT_VECTOR_SYS_CALL表示要通过系统来调用sys_call函数。在write函数中可以理解为其功能为表现格式化了的字符串。
字符表现驱动了程序:从ASCII 到字模库到表现 vram(存储每一个点的RGB颜色信息)。表现芯片按照革新频率逐行读取 vram,并通过信号线向液晶表现器传输每一个点(RGB分量)。







8.4 getchar的实现分析

当程序调用getchar时,程序等待用户按键,用户输入的字符被存放在键盘缓冲区中直到用户按回车(回车也在缓冲区中)。
当用户键入回车之后,getchar才开始从stdio流中每次读入一个字符。getchar函数的返回值是用户输入的第一个字符的ascii码,如出错返回-1,且将用户输入的字符回显到屏幕。如用户在按回车之前输入了不止一个字符,其他字符会保存在键盘缓存区中,等待后续getchar调用读取。也就是说,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完为后,才等待用户按键。
异步异常-键盘停止的处理:当用户按键时触发键盘终端,操作系统将控制转移到键盘停止处理子程序,停止处理程序执行,接受按键扫描码转成ascii码,保存到系统的键盘缓冲区,表现在用户输入的终端内。当停止处理程序执行完毕后,返回到下一条指令运行。




8.5本章小结

本章介绍了 Linux 的 I/O 装备的根本概念和管理方法,以及Unix I/O 接口及其函数。最后分析了printf 函数和 getchar 函数的工作过程。








结论

起首由程序员将hel1o 代码从键盘输入,依次要颠末一下步调:
1、预处理(cpp)。将 hello.c进行预处理,将文件调用的全部外部库文件合并展开,生成一个颠末修改的hello.i文件。
2、编译(ccl)。将 hello.i文件翻译成为一个包罗汇编语言的文件 hello.s 。
3、汇编(as)。将hello.s翻译成为一个可重定位目标文件hello.o。
4、链接(ld)。将 hello.o文件和可重定位目标文件和动态链接库链接起来,生成一个可执行目标文件 hello。
5、运行。在shell 中输入./hello 2021112078 ljs 2。
6、创建进程。终端判断输入的指令不是shell内置指令,于是调用fork函数创建一个新的子进程。
7、加载程序。shell 调用execve函数,启动加载器,映射假造内存,进入程序入口后程序开始载入物理内存,然后进入main函数。
8、执行指令:CPU为进程分配时间片,在一个时问片中,hello享有CPU资源,顺序执行本身的控制逻辑流。
9、访问内存: MMU将程序中使用的假造内存地点:通过页表映射成物理地点。10、动态申请内存:printf会调用malloc向动态内存分配器申请堆中的内存。11、信号管理:当程序在运行的时候我们输入Ctrl+c,内核会发送SIGINT信号给进程并停止前台作业。当输入Ctr1+z时,内核会发送SIGTSTP信号给进程,并将前台作业停止挂起。
12、停止:当了进程执行完成时,内核安排父进程接纳了进程,将了进程的退出状态传递给父进程。内核删除为这个进程创建的全部数据布局。
通过本次实行,我深切感受到盘算机系统的精细和强盛,每一个简弟的任务都需要盘算机的各种复杂的操作来完成,这背后体现出了严谨的逻辑和现代工艺的精巧。







附件

表3 :附件文件

文件名
文件作用
hello.c
源代码文件
hello.i
预处理后生成的文件
hello.s
编译后生成的文件
hello.o
汇编后生成的可重定位目标程序
hello
链接之后生成的可执行程序
hello_o_asm.txt
hello.o的反汇编文件
hello_o_elf.txt
hello.o的ELF文件
hello_run_asm
hello的反汇编文件
hello_run_elf
hello的ELF文件



参考文献

  https://blog.csdn.net/qq_47582681/article/details/118109899
  哈工大2021春CSAPP大作业-程序人生(Hello’s P2P)_MincooLee的博客-CSDN博客
 https://zhidao.baidu.com/question/646759751623911205.html
  https://blog.csdn.net/qq_52943402/article/details/124833002
  编译的根本概念_董大虾的博客-CSDN博客
 编译到底是在干什么_编译的作用_coding丁的博客-CSDN博客


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