西河刘卡车医 发表于 2024-6-14 21:28:11

HIT-盘算机体系-程序人生-Hello’s P2P

https://img-blog.csdnimg.cn/direct/dbaf384a54244cfdb7010a46a7836283.jpeg#pic_center
盘算机体系
大作业
题 目 程序人生-Hello’s P2P
专 业 盘算机科学与技能
学 号 2022110773
班 级 2203103
学 生 李鹏展
指 导 教 师 史先俊
盘算机科学与技能学院
2023年12月
摘 要
本文重点分析hello程序的一生。Hello虽然功能简单,但在实现它的过程中却涉及到编译、链接、历程、虚拟内存等浩繁盘算机领域的紧张概念,因此梳理hello程序对于深入理解盘算机体系而言是十分有必要的。本文基于X86-64硬件环境,在Ubuntu下利用vs code、edb等工具完整地分析了hello从编写到运行停止的所有过程,在历程管理和内存管理部分进行了着重的分析。全文体系全面地回首了盘算机体系课程中的内容。
**关键词:**盘算机体系;盘算机体系结构;操作体系;
**
**
目 录
第1章 概述 - 4 -
1.1 Hello简介 - 4 -
1.2 环境与工具 - 4 -
1.3 中心结果 - 4 -
1.4 本章小结 - 5 -
第2章 预处理 - 6 -
2.1 预处理的概念与作用 - 6 -
2.2在Ubuntu下预处理的命令 - 6 -
2.3 Hello的预处理结果解析 - 6 -
2.4 本章小结 - 8 -
第3章 编译 - 9 -
3.1 编译的概念与作用 - 9 -
3.2 在Ubuntu下编译的命令 - 9 -
3.3 Hello的编译结果解析 - 9 -
3.4 本章小结 - 12 -
第4章 汇编 - 14 -
4.1 汇编的概念与作用 - 14 -
4.2 在Ubuntu下汇编的命令 - 14 -
4.3 可重定位目标elf格式 - 14 -
4.4 Hello.o的结果解析 - 17 -
4.5 本章小结 - 18 -
第5章 链接 - 19 -
5.1 链接的概念与作用 - 19 -
5.2 在Ubuntu下链接的命令 - 19 -
5.3 可实行目标文件hello的格式 - 19 -
5.4 hello的虚拟地点空间 - 21 -
5.5 链接的重定位过程分析 - 22 -
5.6 hello的实行流程 - 23 -
5.7 Hello的动态链接分析 - 24 -
5.8 本章小结 - 24 -
第6章 hello历程管理 - 26 -
6.1 历程的概念与作用 - 26 -
6.2 简述壳Shell-bash的作用与处理流程 - 26 -
6.3 Hello的fork历程创建过程 - 26 -
6.4 Hello的execve过程 - 27 -
6.5 Hello的历程实行 - 27 -
6.6 hello的异常与信号处理 - 28 -
6.7本章小结 - 31 -
第7章 hello的存储管理 - 32 -
7.1 hello的存储器地点空间 - 32 -
7.2 Intel逻辑地点到线性地点的变换-段式管理 - 32 -
7.3 Hello的线性地点到物理地点的变换-页式管理 - 32 -
7.4 TLB与四级页表支持下的VA到PA的变换 - 34 -
7.5 三级Cache支持下的物理内存访问 - 35 -
7.6 hello历程fork时的内存映射 - 36 -
7.7 hello历程execve时的内存映射 - 36 -
7.8 缺页故障与缺页中断处理 - 37 -
7.9动态存储分配管理 - 37 -
7.10本章小结 - 37 -
第8章 hello的IO管理 - 38 -
8.1 Linux的IO装备管理方法 - 38 -
8.2 简述Unix IO接口及其函数 - 38 -
8.3 printf的实现分析 - 38 -
8.4 getchar的实现分析 - 40 -
8.5本章小结 - 40 -
结论 - 40 -
附件 - 41 -
参考文献 - 42 -
第1章 概述

1.1 Hello简介

P2P过程(From Program to Process)的含义为从编写c程序(program)到在机器上运行历程(process)之间的转换过程。这之间包含以下几个阶段:预处理、编译、汇编、链接、加载、运行等。首先,编译器驱动程序运行预处理器(cpp)将原始的.c文件转为.i文件,再运行C编译器(cc1)将.i文件转为.s文件,之后运行汇编器(as)将.s文件转为.o文件,然后运行链接器程序(ld)将目标文件组合形成可实行目标文件。末了通过加载器将程序加载到内存中并运行,这样就完成了hello.c程序从program到process的转换。
020过程(From Zero to Zero)的含义是hello.c程序从开始运行(zero)到运行竣事(zero)的全过程。程序颠末编译得到可实行文件后,shell解析用户要运行hello的命令,之后调用fork函数创建子历程并在子历程使用体系调用execve启动加载器,删除子历程现有的虚拟内存段,并创建一组新的代码、数据、堆和栈段。然后将虚拟内存页映射到可实行文件中的片。程序在运行过程中,通过异常控制流将磁盘中的文件加载到内存中并完成运行过程。在程序运行竣事后历程停止,shell对子历程进行接纳,这样就竣事了hello程序的运行全过程。
1.2 环境与工具

硬件环境:Intel Core i7 12700H处理器
软件环境:Windows11 64位/VMware 11/Ubuntu 16.04 LTS 64位
开辟与调试工具:VS code
1.3 中心结果

hello1(采用指定编译选项得到的可实行目标文件)
hello.i(hello.c预处理后的文件)
hello.s(hello.i编译后的文件)
hello.o(hello.s汇编后得到文件)
hello(hello.o颠末链接得到的可实行目标文件)
elf.txt(使用readelf查看hello.o的结果)
dump_hello.txt(hello.o反汇编得到的文件)
dump_hello_exe.txt(hello可实行文件反汇编得到的文件)
1.4 本章小结

本章扼要介绍了hello.c程序的P2P过程和020过程,描述了程序从开始编译到实行竣事的大致流程。同时给出了硬件环境、软件环境和开辟工具等。末了列出了完本钱次作业过程中所生成的中心文件。
第2章 预处理

2.1 预处理的概念与作用

预处理概念:对源文件进行文本操作而不进行分析,实行去解释、宏替换、条件编译、头文件展开等操作生成后缀为.i的文件。
预处理作用:美满代码、帮助编译、代码简化。在C程序中可以使用#include包含头文件,这样会便捷程序的编写,但会使单独的C程序失去完整性。预处理器可以将头文件中的内容插入到源文件中,实现美满代码的功能。同样的,程序员在编写过程中还可能会使用宏界说以简化编程的复杂性,使用条件编译以实现特定的功能。这些操作都会使编译变得复杂。而预处理器在编译之前就可以办理这些问题,从而提高编译的效率。而预处理器去除解释的功能显然可以实当代码的简化过程。
2.2在Ubuntu下预处理的命令

Ubuntu下预处理命令:gcc -E -o hello.i hello.c
https://img-blog.csdnimg.cn/direct/6497510d1c5a4919a1cfbbde92f9c327.png#pic_center2.2.1 Ubuntu下预处理
2.3 Hello的预处理结果解析

原始C程序为18行,颠末预处理后扩展为3092行。扩展后文件的末尾为源程序中main函数代码。在这之上则是包含的头文件的展开,主要包罗4个部分:头文件路径、数据范例重界说、外部函数声明、摆列范例(常量)。
https://img-blog.csdnimg.cn/direct/bdc8c681ec79435187210463c811ca9e.png#pic_center
2.3.1 预处理后文件中的源代码
头文件路径主要出现在文件的头部,但在下文必要的地方也可能会出现。数据范例的重界说部分关联了C语言尺度数据范例与头文件中界说的数据范例。其余主要为外部函数的声明,这一部分占据了插入代码的最大部分。另外也有相当一部分内容为库函数中大量出现的常量,并用摆列范例进行归类。
https://img-blog.csdnimg.cn/direct/92b4e072bf8240269a61fa4a3a467b54.png#pic_center
2.3.2 预处理后文件中的部分头文件路径
https://img-blog.csdnimg.cn/direct/d24055e53d014ee6a53d4727ef1ecdd8.png#pic_center
2.3.3 预处理后文件中的部分范例重界说
https://img-blog.csdnimg.cn/direct/45cc8626440243a6ae244daac22f92d4.png#pic_center
2.3.4 外部函数声明
https://img-blog.csdnimg.cn/direct/1c3c7d88967e454cb985687d20613c92.png#pic_center
2.3.5 库函数常量和摆列范例
2.4 本章小结

本章对预处理的概念和作用进行了扼要介绍,之后给出了在Ubuntu下进行预处理的命令,末了具体地解析了预处理的结果,讨论了预处理后文件各个部分的含义和作用。
第3章 编译

3.1 编译的概念与作用

概念:预处理得到的仍然是C语言程序,编译就是将C语言转化为汇编语言。
作用:汇编将C语言转化为汇编语言,这是从高级抽象到底层实现之间不可缺少的过程,是机器能够运行程序的前提。
3.2 在Ubuntu下编译的命令

Ubuntu下编译命令:gcc -S hello.i -o hello.s
https://img-blog.csdnimg.cn/direct/a49bdb8636f842b3a6ea868a2d2d6313.png#pic_center
3.2.1 Ubuntu下编译命令
3.3 Hello的编译结果解析

本节将从常量与变量、算术操作与关系操作、函数调用、数组内存引用和控制转移这五个方面对hello的实现进行分析。首先展示编译结果。
https://img-blog.csdnimg.cn/direct/f82e0e55493f4800b39d2fc359dbf962.png#pic_center
https://img-blog.csdnimg.cn/direct/c7f5988d64004cdda0e0f801c89de9d7.png#pic_center
https://img-blog.csdnimg.cn/direct/f3cf8337e84d43bd8291ce05129498b1.png#pic_center
3.3.1 汇编代码
3.3.1 常量、变量
C源程序中的常量被存储在.rodata只读数据地区。程序hello.c中使用到的常量有两个printf的输出字符串参数,分别被标记为.LC0和.LC1。如上图5~11行。
在hello.c中的变量只有局部变量,而局部变量存储在栈中。例如,在hello程序中,局部变量i位于栈中rbp-4的位置。
3.3.2算术操作与关系操作
在hello.c中出现了算术操作“++”,这一操作通过add来完成,在汇编代码中对应于第53行,“addl $1, -1(%rbp)”。
出现了关系操作“!=”,这一操作通过cmp来完成,对应于汇编代码中“cmpl $7, -4(%rbp)”。
https://img-blog.csdnimg.cn/direct/abeff32c013b45b1b7ff9374e2579339.png#pic_center
3.3.2 运行时栈结构
3.3.3 参数传递、函数调用、返回
在hello.c中函数调用出现7次:main(), printf(), exit(), printf(), sleep(), atoi(), getchar()。其中函数的传参、调用、返回原理一致,过程也相似。在x86-64中,函数参数优先以寄存器来传递,当参数数目超过6个时以栈来传递。本例中各个函数都是寄存器传参,寄存器传递参数的顺序为:%rdi, %rsi, %rdx, %rcx, %r8, %r9(以上假设参数为64位,若不是64位而为x位则取同一寄存器的低x位)。
在进入main()后,参数argc和argv已经分别存入到寄存器%edi和%rsi中,之后又赋值到栈中,具体见图3.3.1中22、23行及图3.3.2栈结构。对于除main()之外的别的函数,都有相应的传参语句,如29行为exit()传参,37行41行43行为第二处printf()传参,51行为sleep()传参,49行为atoi()传参。
函数调用通过call实现,返回通过ret实现。
3.3.4 数组操作与内存引用
在hello.c程序中出现了数组argv[][],这是作为main()函数参数的指针数组。他的寻址语句有3处:35、36行,38、39行,46、47行。64位体系机器地点为8字节,因此通过加8、加16、加24分别可以得到main()函数的第2、3、4个参数的指针。
https://img-blog.csdnimg.cn/direct/e7309ecef84a4106ad291f6e00506ec9.png#pic_center
https://img-blog.csdnimg.cn/direct/1fec12d182c948849a720b1acf7cc67d.png#pic_center
https://img-blog.csdnimg.cn/direct/46544a0785c546fc9d9a734655818e5d.png#pic_center
3.3.3 指针数组的寻址
3.3.5 控制转移
在hello.c中的控制转移有两处,第一处为if条件判定,通过cmpl和je两句汇编指令实现。第二处为for循环,对-4(%rbp)处的局部变量i使用cmpl判定并结合jle跳转,在每轮循环中i自加1(53行)。
https://img-blog.csdnimg.cn/direct/49b91667ef0048aca8f286951ff0eb2d.png#pic_center
3.3.4 for循环控制结构
3.4 本章小结

本章简单介绍了编译的概念与作用,并给出了在Ubuntu下的编译命令。之后具体地对hello.c的汇编代码进行了分析,介绍了hello的汇编实现方式。从常量与变量、算术操作与关系操作、函数调用、数组内存引用和控制转移这五个方面对hello的实现进行了剖析。
第4章 汇编

4.1 汇编的概念与作用

概念:汇编是指汇编器(as)将文本格式的汇编代码转化为二进制文件的过程,其中一项紧张的工作是向文件中添加符号表。
作用:汇编过程将代码转化为二进制使得文件可以被机器识别,添加符号表使文件成为可重定位目标文件(文件后缀从.s转换为.o)
4.2 在Ubuntu下汇编的命令

Ubuntu下汇编命令为:gcc -c -o hello.o hello.s
https://img-blog.csdnimg.cn/direct/174e8b5369514cfda7edef68be25619f.png#pic_center
4.2.1 Ubuntu下汇编命令
4.3 可重定位目标elf格式

可重定位目标文件hello.o的ELF格式包罗ELF头、.text、.rodata、.data、.bss、.sy mtab、.rel.text、.rel.data、.debug、.line、.strtab、节头部表这些节。ELF头中包含体系的字大小、目标文件范例、机器范例、节头部表文件偏移等部分。使用readelf查看得到的信息已导出到附录文件elf.txt中。
https://img-blog.csdnimg.cn/direct/ac954e6316d94552b5b2b057ae6e3f3f.png#pic_center
4.3.1 ELF格式基本信息
.text中为已编译程序的机器代码,.rodata中包含只读数据,如printf语句的格式串和开关语句的跳转表,.data中包含已初始化的全局和静态C变量,.bss中未初始化的全局和静态C变量。
.symtab为符号表,存放程序中界说的函数和全局变量的信息,.rel.text包含有代码的重定位条目,.rel.data中包含有已初始化数据的重定位条目。.debug为调试符号表,.line为源程序行号与机器指令之间的映射,.strtab为字符串表。
https://img-blog.csdnimg.cn/direct/c7632f5ed7a74df99aa09292cc02caf2.png#pic_center
4.3.2 ELF头内容
https://img-blog.csdnimg.cn/direct/0e9ff207d9194ba9ab45464f2c041fd7.png#pic_center
4.3.3 各个节的基本信息
https://img-blog.csdnimg.cn/direct/ab27ae3781a546ab8044238c1d1b3eea.png#pic_center
4.3.4 符号表中各条目
https://img-blog.csdnimg.cn/direct/f4106bdd803745e9bfb5c1b9d558b747.png#pic_center
4.3.5 重定位节.rela.text各条目
由上图可以看出,这一ELF文件中包含8个重定位条目。每个重定位条目包罗偏移量、信息、范例、符号值、符号名称和加数。其中偏移量为这一重定位条目对应的符号引用在节中的偏移量。范例决定了重定位时盘算的方式,常用的有R_X86_64_PC32和R_X86_64_32,前者对应与PC相对引用,后者对应于绝对引用。上图中R_X86_64_PLT32为动态链接中的过程链接表条目。
4.4 Hello.o的结果解析

首先使用obidump对hello.o进行反汇编并导出到dump_hello.txt中,之后进行对比。机器语言的指令由操作数和操作码组成,只是以十六进制数表现,与汇编语言基本满意一一对应的关系。反汇编与汇编文件主要有以下三点区别:

[*]反汇编以十六进制表现操作数,而汇编文件以十进制表现操作数。
[*]汇编文件中跳转语句jmp以段名表现跳转目标位置,反汇编文件中则为目标位置与PC的相对距离。
[*]汇编文件中call后面为函数名,而反汇编文件中call后面为PC相对地点。
https://img-blog.csdnimg.cn/direct/65cc6045fd0143ee8594af2652206b13.png#pic_center
https://img-blog.csdnimg.cn/direct/d9d29cb42c6744cd873679127b0331bf.png#pic_center
4.4.1 反汇编代码
4.5 本章小结

本章扼要介绍了汇编的概念和作用,同时给出了Ubuntu下的汇编命令。之后分析了ELF格式的结构,包罗ELF头、符号表条目和重定位条目等。末了比力了hello.s汇编文件与hello.o文件反汇编得到的代码,给出了二者之间的不同。
第5章 链接

5.1 链接的概念与作用

链接的概念:链接(linking)是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可以被加载到内存中并实行。链接由链接器自动实行,将可重定位目标文件或共享文件转化为可实行目标文件。
链接的作用:链接使分离编译成为可能,这样我们在开辟大型软件中可以将源文件分解为更小、更好管理的模块,可以独立地修改和编译这些模块,从而提高开辟和维护效率。
5.2 在Ubuntu下链接的命令

使用ld的链接命令,应截图,展示汇编过程! 注意不但连接hello.o文件
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/ed5e24e7f55a43ccbae95cd345daaf08.png#pic_center
5.2.1 Ubuntu下链接命令
5.3 可实行目标文件hello的格式

相比与可重定位目标文件,可实行目标文件的节更多。每个节的信息依然分为:名称、大小、范例、地点和偏移量四个部分。但是在各个目标文件相同的节归并之后,各节的信息进行了更新,尤其是地点部分,各节已经分配好了虚拟地点。
https://img-blog.csdnimg.cn/direct/d3175637027146bd9d9fa07ff4526c71.png#pic_center
5.3.1 ELF头信息
https://img-blog.csdnimg.cn/direct/7521a549c44c44efae5788458a807355.png#pic_center
https://img-blog.csdnimg.cn/direct/5a63bef4d329477b80be6d13ba73a93a.png#pic_center
5.3.2 ELF格式hello各节信息
5.4 hello的虚拟地点空间

使用edb加载hello,查看本历程的虚拟地点空间各段信息,并与5.3对照分析阐明。
https://img-blog.csdnimg.cn/direct/af858036e66d43c786fbdd581229edaf.png#pic_center
https://img-blog.csdnimg.cn/direct/ccbaa2d633fe4d5780062176e69f14d0.png#pic_center
5.4.1 edb查看虚拟内存信息
加载器实行加载时会在代码段、数据段与虚拟内存之间建立映射关系(建立映射的有.init, .text, .rodata, .data, .bss以及别的动态链接部分)。在5.3的节头部表中可以看到,.text节的起始地点为0x4010f0,.data起始地点为0x404048。在edb中查看这两处地点。
https://img-blog.csdnimg.cn/direct/aefe8781349741be871caeabb65f1d14.png#pic_center
5.4.2 虚拟内存中.text节
https://img-blog.csdnimg.cn/direct/4eda8a1f490b4677be382e5db8c35d51.png#pic_center
5.4.3 虚拟内存中.data节
5.5 链接的重定位过程分析

使用objdump反汇编可实行程序hello后得到信息已导出到dump_hello_exe.txt文件中。将它与dump_hello.txt文件进行对比,发现hello.o反汇编后只有.text一节,而hello反汇编之后则有5节。另外,hello.o反汇编中的重定位条目也已经替换为了虚拟内存地点,包罗内存引用与函数调用,如下图所示。
链接器在完成符号解析之后,就要归并各个目标文件中的相同节并进行重定位。它首先赋予各节和各符号虚拟内存空间地点,之后根据重定位条目修改代码区与数据区中出现的符号引用,将占位符改为虚拟地点的算法根据重定位条目中的范例项来选取。
https://img-blog.csdnimg.cn/direct/f9d404c4606c427ba1147ca30dd1a3fe.png#pic_center
5.5.1 hello.o反汇编中的重定位条目
https://img-blog.csdnimg.cn/direct/d045a08cd9c64b8db8848dd5e71d4fe2.png#pic_center
5.5.2 hello反汇编中已替换为虚拟内存地点
5.6 hello的实行流程

使用edb将断点打在0x4010f0(_start)处,开始调试,查看程序实行的流程。
https://img-blog.csdnimg.cn/direct/a9cb590ffa6e4a3194acef88c012285f.png#pic_center
https://img-blog.csdnimg.cn/direct/cc2b2b5aa3924867976283df5ab2f156.png#pic_center
5.6.1 使用edb查看hello的实行流程
调用与跳转的各个子程序名:
Ld-2.27.so! _dl_start
Ld-2.27.so! _dl_init
_start
_libc_start_main
_GI__cxa_atexit
_new_exitfn
_setjmp
__sigsetjmp
__sigjmp_save
_libc_csu_int
Init
Main
Puts
Printf
Atoi
Sleep
Getchar
__GI_IO_puts
__strlen_avx2
_IO_new_file_xsputn
IO_valodate_vtable
Exit
__GI_exit
__run_exit_handlers
5.7 Hello的动态链接分析

动态链接器使用过程链接表(PLT)和全局偏移量表(GOT)来实现函数的动态链接。GOT中存储着函数的目标地点,PLT使用GOT中的地点跳转到目标函数。在加载时,动态链接器会重定位 GOT 中的每个条目,使其包含目标的虚拟地点。
https://img-blog.csdnimg.cn/direct/07c201616336464d83739ca680985dcc.png#pic_center
5.7.1 动态条目查看位置
https://img-blog.csdnimg.cn/direct/f7f88e1abf034fab9c498cb72cdcbaf6.png#pic_center
5.7.2 dl_init前条目
https://img-blog.csdnimg.cn/direct/35c9fac491594eb086530b869f7e8445.png#pic_center
5.7.3 dl_init实行后条目
由上图可以看出,选中地区的数据发生了变化,dl_init在实行后,修改了GOT生存的指向已加载共享库的链表地点和动态链接器在ld-linux.so模块中的入口,使得程序在接下来能够通过PLT和GOT进行动态链接。
5.8 本章小结

本章介绍了链接的概念和作用,并给出了Ubuntu下的链接命令。之后具体论述了可实行文件hello的结构,对hello的虚拟地点空间进行了阐明。然后具体地分析了链接的重定位过程以及hello的实行流程,末了对hello的动态链接过程做了扼要的分析。
第6章 hello历程管理

6.1 历程的概念与作用

历程的概念:历程就是一个实行中的程序的实例,是一段可实行程序关于某个数据集合的一次运行活动。它是操作体系动态实行的基本单元。
历程的作用:历程为应用程序提供了两个关键的抽象:独立的逻辑控制流和私有的地点空间。这两个抽象使程序似乎独占处理器和内存空间,从而使程序运行中的管理过程更加的方便高效。
6.2 简述壳Shell-bash的作用与处理流程

shell的作用:shell负责各历程的创建、程序加载运行,具有前后台控制、作业调用、信号的发送与管理等功能。
shell的处理流程:shell首先读取用户的命令行,将该命令行转存到参数列表之后对其进行求值解析。如果是内置命令则直接运行,如果不是的话,则建立一个新的历程并在新的历程中加载程序。shell通过fork函数创建新历程并通过execve函数将可实行文件的.text、.data、.bss等段加载到当前历程的内存空间,之后调用加载器跳转到_start处开始运行程序。shell负责全程管理子历程与父历程在运行时的信号处理,直到该命令行的所有任务都已完成,之后开始下一轮的交互行为。
6.3 Hello的fork历程创建过程

在shell中输入命令行./hello 2022110773 李鹏展 1后,shell将命令行转存到参数列表并进行解析。
https://img-blog.csdnimg.cn/direct/8a696d46b9b94c438623abd308483707.png#pic_center
6.3.1 输入命令行运行程序
shell在判定输入命令不是内置命令后,调用fork函数创建一个子历程。fork函数的原型为pid_t fork(void); 新创建的子历程中fork函数返回的PID为0,而在父历程中fork函数返回的为子历程的PID。除此之外,子历程与父历程信息基本相同。子历程得到与父历程用户级虚拟地点空间相同但独立的一份副本,包罗代码和数据段、堆、共享库以及用户栈。子历程还得到与父历程任何打开文件描述符相同的副本,这阐明子历程可以读写在父历程中打开的文件。
6.4 Hello的execve过程

在使用fork创建新历程之后,shell调用execve函数在当前的历程上下文中加载并运行hello。execve函数的原型为:int execve(const char *filename, const char *argv[], const char *envp[]); argv变量指向一个以null末端的指针数组,其中有4个指针,分别指向4个字符串:“./help”, “2022110773”, “李鹏展”, “1”。envp变量指向一个以null末端的指针数组,其中每个指针指向一个环境变量字符串,每个串都是形如“name = value”的名字-值对。
https://img-blog.csdnimg.cn/direct/08c4fda022824eafb1bbbb9c3482278c.png#pic_center
6.4.1 参数列表
函数execve通过调用加载器loader创建内存映像,之后在程序头部表的引导下将可实行文件的片映射到代码段和数据段。然后加载器跳转到程序的入口点_start函数并由_start函数调用体系启动函数__libc_start_main,__libc_start_main函数会初始化实行环境,调用用户层的main函数,处理main函数的返回值并在必要的时候把控制返回给操作体系内核。
6.5 Hello的历程实行

内核会为每一个历程维持一个上下文,也就是内核重新启动一个被抢占的历程所需的状态,包罗寄存器值、栈、页表、历程表等信息。在运行hello时,子历程同样拥有一个独立的上下文,当实行体系调用或发生中断时,内核会抢占hello历程,生存hello历程上下文并实行另外的历程。体系通常会产生周期性定时器中断以划分时间片,每当时间片竣事时都会发生上下文切换。
当hello程序实行体系调用时,会从用户态变化为内核状态并实行体系调用程序,实行竣事时会检查并处理信号,然后从内核态变化为用户态,继续实行用户程序。
https://img-blog.csdnimg.cn/direct/17370a27cfb047b3be3e8149f2db1c8b.png#pic_center
6.5.1 历程上下文切换
6.6 hello的异常与信号处理

异常分为4种:中断、陷阱、异常、停止。程序hello实行过程中会产生以下几类异常:体系定时器中断、体系调用处的陷阱、缺页故障。除此之外,还可能产生向硬件中断这样的异常(如键盘中断)。当产生定时器中断时,体系会实行上下文切换,调度别的历程;当遇到体系调用时,会从用户态变化为内核态并实行fork、execve等体系调用函数,之后回到下一条指令处;当遇到缺页故障时,内核会实行缺页处理程序,实行完毕后回到原来指令处再次实行。
https://img-blog.csdnimg.cn/direct/e94af470a7fb48a6a78a62275876684f.png#pic_center
6.6.1 异常处理流程
实行hello过程中可能产生的信号主要有3种:SIGINT、SIGTSTP、SIGCHLD。键盘输入Ctrl+C可以向前台历程发送信号SIGINT,当历程收到SIGINT信号后会停止;输入Ctrl+Z可以向前台历程发送SIGTSTP信号,历程收到后会停止(挂起);当子历程停止时,内核会向它的父历程发送SIGCHLD,父历程收到后会接纳它已停止的子历程。
https://img-blog.csdnimg.cn/direct/a5fcf307bf3444dd96e9501872e34d85.png#pic_center
6.6.2 按Ctrl+Z
https://img-blog.csdnimg.cn/direct/2408cc444930473e8beeacf557330130.png#pic_center
6.6.3 继续按fg
https://img-blog.csdnimg.cn/direct/23edfb2bc31841ce92751d7844a9fea3.png#pic_center
6.6.4 按下Ctrl+C
https://img-blog.csdnimg.cn/direct/08cd61761d794b408e305f3eac6c03ca.png#pic_center
6.6.5 乱按不回车
https://img-blog.csdnimg.cn/direct/09c4ba3637d74e73b13a5cfd5732c66c.png#pic_center
6.6.6 乱按并回车
https://img-blog.csdnimg.cn/direct/28eb5b44a8b445bfa6fd6519c3bc1c0e.png#pic_center
6.6.7 停止时输入命令ps、jobs
https://img-blog.csdnimg.cn/direct/e45096ae03004562a7ef124cdedd3f54.png#pic_center
6.6.8 停止时输入命令pstree部分结果
https://img-blog.csdnimg.cn/direct/52ddcf18dfea40098a83f15ed783667a.png#pic_center
6.6.9 停止时输入命令kill停止hello历程
6.7本章小结

本章首先扼要介绍了历程的概念和作用,并对Shell的作用和处理流程加以解释。之后具体分析了fork创建历程的过程和execve加载程序的过程。接下来解释了hello程序的历程实行过程,末了具体分析了hello的异常与信号处理机制。
第7章 hello的存储管理

7.1 hello的存储器地点空间

逻辑地点:在程序编写过程中使用的段基址加段偏移情势的地点表现。
线性地点:由非负连续整数地点构成的有序集合{0,1,2,……}。
虚拟地点:在一个带虚拟内存的体系中,CPU能够生成的具有N=2^n个地点的地点空间,这样的地点即为虚拟地点。
物理地点:盘算机主存被组织成一个由M个连续的字节大小的单元组成的数组,每个字节都有的一个唯一的物理地点。
7.2 Intel逻辑地点到线性地点的变换-段式管理

在Intel8086中,有4个段寄存器用来生存段地点:CS,DS,SS,ES,分别为代码段寄存器、数据段寄存器、堆栈段寄存器和附加段寄存器。在程序运行时内存的段起始位置和大小会被确定,各个寄存器的值也会被确定,同时有关段的管理属性也会确定下来。
Intel的段式管理分为实模式和保护模式。在实模式下逻辑地点为CS:EA,到线性地点的转换方式为:CS左移4位再与EA相加。在保护模式下则相对复杂,必要以段描述符为下标,通过查全局描述符表(GDT)和局部描述符表(LAT)得到段地点,之后与偏移地点相加就得到了线性地点。
7.3 Hello的线性地点到物理地点的变换-页式管理

盘算机体系通常将虚拟地点作为作为页式管理的工具,通过地点翻译完成从虚拟地点(线性地点)到物理地点的变换。

[*]页表
页表是一个存放在物理内存中的数据结构,以页表条目(PTE)作为基本单元。虚拟地点空间中的每个页在页表中一个固定偏移量处都有一个PTE。
https://img-blog.csdnimg.cn/direct/ecb73020d1914812815e5feeb9012827.png#pic_center
7.3.1 页表条目结构

[*]基本的地点翻译过程
第一步:处理器生成一个虚拟地点并传送给内存管理单元MMU。
第二步:MMU结合页表基址寄存器PTBR生成页表条目地点,并向高速缓存大概主存哀求获取这一地点上的内容。
第三步:高速缓存或主存向MMU返回页表条目信息PTE。
第四步:MMU根据页表条目中的物理页号和虚拟地点中的页内偏移构造出物理地点,并传送给高速缓存或主存。
第五步:高速缓存或主存将该地点下的数据传送给处理器。
在不发生缺页的环境下,通过以上5个步调就可以将虚拟地点(线性地点)转化为物理地点,完成页式管理的过程。如果MMU收到页表条目后发现缺页,则会引发异常,内核会调用异常处理程序完成页面的加载并更新页表条目PTE,之后返回原来历程,重新实行指令,CPU再次向MMU发送虚拟地点。
https://img-blog.csdnimg.cn/direct/385ec17faa454709bf12c257ee87d257.png#pic_center
7.3.2 不缺页环境下的地点翻译
https://img-blog.csdnimg.cn/direct/7b7fd6731d394e3f8c46c9f4161e0c97.png#pic_center
7.3.3 缺页时的地点翻译
7.4 TLB与四级页表支持下的VA到PA的变换


[*]TLB加速从VA到PA的地点翻译
TLB是一个小的、虚拟寻址的缓存,每一行中都生存有单个PTE组成的块。使用TLB做内存中页表的高速缓存可以加速MMU对页表条目的获取。在CPU产生虚拟地点并发送给MMU后,MMU从TLB中取出相应的PTE,如果TLB中不存在那么还是从内存中页表中取出。之后过程与上一节中基本的地点翻译过程雷同。
https://img-blog.csdnimg.cn/direct/37c5bcdfa3334188b32c7ad5c04295db.png#pic_center
7.4.1 TLB加速地点翻译

[*]多级页表镌汰内存存储页表的开销
假设页表条目大小为4字节的话,那么32位体系虚拟地点空间对应的所有页表大小为4MB,对内存而言是一种浪费。如果是64位体系,内存乃至无法装载所有的页表。由于虚拟内存中有大量未使用的地区,因此使用多级页表可以镌汰内存在存储页表方面的开销。
使用多级页表管理时,虚拟地点被分为k个VPN和1个VPO。以36位VPN四级页表为例,每个VPN被划分为四个9位的片,每个片又被用作到一个页表的偏移量。例如,VPN1提供到一级页表的偏移,一级页表中条目包含了二级页表的基地点。VPN2、VPN3、VPN4同样如此。以这种方式,就可以根据虚拟地点的不同位来确定各级页表,终极确定页表条目。对于未被使用的地区,则不在上级页表中设置页表,从而大大降低了内存存储页表的开销。
https://img-blog.csdnimg.cn/direct/91f84778adc64101b3b90d4d39ff05eb.png#pic_center
7.4.2 四级页表支持下的地点翻译
7.5 三级Cache支持下的物理内存访问

三级cache支持下物理内存访问在上图7.4.2中同样有所体现。MMU在将物理地点发送给缓存后,缓存从物理地点中抽取缓存偏移(CO)、缓存组索引(CI)以及缓存标记(CT)。缓存根据CI查找组,之后将CT与组内块中数据依次进行比力,命中则直接从缓存中取出,否则就依次在L2、L3、主存中查找,查找到后更新各级缓存。
https://img-blog.csdnimg.cn/direct/d8b142127d5e4eb4bf25de6e976f6b0f.png#pic_center
7.5.1 使用物理地点读取缓存示例
7.6 hello历程fork时的内存映射

当fork函数被当前历程调用,内核会为新历程创建各种数据结构并分配唯一的PID,创建的数据结构包罗:mm_struct、地区结构、页表副本。之后内核将两个历程中的每个页表标为只读,并将每个地区标为写时复制,当这两个历程中任一个进行写操作时,都会触发一个保护故障。由于被标为写时复制,故障处理程序会创建一个新副本并更新页表条目指向这个新副本,建立新的内存映射。
7.7 hello历程execve时的内存映射

execve函数在当前历程中加载并运行包含在可实行目标文件中的程序,必要以下几个操作:

[*]删除已存在的用户地区,删除当前历程虚拟地点的用户部分中的已存在的地区结构。
[*]映射私有地区,为新程序的代码、数据、bss 和栈地区创建新的地区结构,所有这些新的地区都是私有的、写时复制的。代码和数据地区被映射为 hello 文件中的 .text 和 .data 区,bss 地区是哀求二进制零的,映射到匿名文件,其大小包含在 hello 中,栈和堆地点也是哀求二进制零的,初始长度为零。
[*]映射共享地区, hello 程序与共享对象 libc.so 链接,libc.so 是动态链接到这个程序中的,然后再映射到用户虚拟地点空间中的共享地区内。
[*]设置程序计数器(PC),execv() 做的末了一件事情就是设置当前历程上下文的程序计数器,使之指向代码地区的入口点。
7.8 缺页故障与缺页中断处理

当发生缺页的环境下,体系分为6个步调来处理,其中1~3步和7.3中介绍的前3步相同。第四步将CPU的控制传递到缺页处理程序。第五步中缺页处理程序选择物理内存中的牺牲页,若以修改则写回磁盘。第六步中缺页处理程序调入新的页面并更新内存中的PTE。具体过程参见图片7.3.3。
7.9动态存储分配管理

动态内存分配器维护着一个历程的虚拟内存地区,称为堆。体系之间虽然会有细节不同,但是都具有一些共同的特点。假设堆是一个哀求二进制零的地区,它紧接在未初始化的地区后开始,并向上生长。对于每个历程,内核维护着变量brk,它指向堆的顶部。
分配器将堆视为一组不同大小的块的集合来维护。每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块表现地保留为供应用程序使用。空闲块可用来分配。空闲块保持空闲,直到它表现地被应用所分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序实行的,要么是内存分配器自身隐式实行的。
分配器有两种基本风格。两种风格都要求应用表现地分配块。它们的不同之处在于由哪个实体来负责释放已分配的块。
1.显式分配器,要求应用显式地释放任何已分配的块。
2.隐式分配器,要求分配器检测一个已分配块何时不再被程序所使用,那么就释放这个块。隐式分配器也叫做垃圾收集器,而自动释放未使用的已分配块的过程叫做垃圾收集。
7.10本章小结

本章首先介绍了hello的几种存储器地点空间,包罗逻辑地点、线性地点、虚拟地点和物理地点。之后具体地分析了逻辑地点到线性地点以及线性地点到物理地点的变换过程。接下来分析了使用TLB加速地点翻译的原理以及多级页表(四级页表)节流页表存储空间的策略。然后具体阐释了三级Cache支持下的物理内存访问过程。在7.6和7.7节,扼要阐明确hello历程中fork和execve函数实行时的内存映射。在7.8节再次分析了缺页故障和缺页中断的处理策略。末了在7.9节介绍了动态存储分配的基本方法与策略。
第8章 hello的IO管理

8.1 Linux的IO装备管理方法

装备的模型化:文件
装备管理:Unix I/O接口
一个Linux文件就是一个m个字节的序列:B0,B1,B2……Bk……。所有的I/O装备都会被模型化为文件,所有的输入和输出都被当尴尬刁难相应文件的读和写来实行。这种将装备映射为文件的方式,允许Linux内核引出一个简单、低级的应用接口,即为Unix I/O。
8.2 简述Unix IO接口及其函数

Unix I/O的基本接口包罗:打开文件、关闭文件、读文件、写文件。

[*]打开文件
int open(char* filename,int flags,mode_t mode),历程调用 open 函数来打开一个存在的文件或是创建一个新文件的。open 函数将 filename 转换为一个文件描述符,并且返回描述符数字,返回的描述符总是在历程中当前没有打开的最小描述符,flags 参数指明确历程打算怎样访问这个文件,mode 参数指定了新文件的访问权限位。

[*]关闭文件
int close(fd),fd 是必要关闭的文件的描述符,close 返回操作结果。

[*]读文件
ssize_t read(int fd,void *buf,size_t n),read 函数从描述符为fd的当前文件位置赋值最多n个字节到内存位置 buf。返回值 -1 表现错误,0 表现 EOF,否则返回值表现的是现实传送的字节数目。

[*]写文件
ssize_t wirte(int fd,const void *buf,size_t n),write 函数从内存位置 buf 复制至多 n 个字节到描述符为 fd 的当前文件位置。
8.3 printf的实现分析

首先查看printf函数的界说:
https://img-blog.csdnimg.cn/direct/f1c1b91e23b64162b469ce122b404845.png#pic_center
8.3.1 printf函数的界说
https://img-blog.csdnimg.cn/direct/cc77f12c8a2c44c9a3ff620a06a940b0.png#pic_center
8.3.2 函数vsprintf界说
函数vsprintf将参数格式并存入buf中,返回值为格式化数组长度。之后write函数将参数存入寄存器并使用int 21h调用sys_call。体系调用通过总线将字符串中字节从寄存器中复制到显存中,此时显存存储字符的ASCII码值。
接下来字符表现驱动子程序通过ASCII码在字模库中找到点阵信息并存入到vram中。显卡会以肯定革新频率逐行读取vram并通过信号线向液晶表现器传输每一个点作为RGB分量。颠末以上步调,就将hello中要表现的字符打印出来了。
8.4 getchar的实现分析

异步异常-键盘中断的处理:用户从键盘中输入,触发中断,内核将控制转移到键盘中断处理子程序。处理程序接受按键扫描码并转成ascii码,之后生存到体系的键盘缓冲区。
getchar等调用read体系函数,通过体系调用在内核态下读取按键ascii码,直到接受到回车键才返回,返回后回到当前历程成功读取字符。
8.5本章小结

本章首先扼要介绍了Linux的IO装备的管理方法,即以读写文件的方式处理与装备的IO。之后分条列举了Unix的IO接口并给出了对应的函数。末了分析了printf函数和getchar函数的实现过程。
结论

在对hello程序一生各个阶段进行具体的分析之后,有必要对各个过程进行总结。在hello的一生中,会履历一下过程:

[*]编程:形成C语言文件hello.c。
[*]预处理:hello.c去解释、宏替换、条件编译、头文件展开之后形成hello.i。
[*]编译:将hello.i编译为文本情势的汇编代码文件hello.s。
[*]汇编:将hello.s转化为可重定位目标文件hello.o。
[*]链接;调用链接器将hello.o与别的目标文件链接形成可实行目标文件hello。
[*]加载:shell调用fork创建子历程,调用execve加载hello到历程上下文中。
[*]运行:在被内核调度到的时候,进入CPU实行。
[*]访存:不但必要从虚拟地点转换到物理地点,还要履历Cache中的查找。
[*]异常:在hello运行时可能会履历中断、陷阱、故障、停止等。
[*]信号:键盘输入Ctrl+Z、Ctrl+C可以让历程停止、停止。
[*]竣事:shell接纳子历程,然后有内核删除hello历程相关的所有数据结构。
颠末梳理hello程序的一生,我重新梳理了相关的多方面的知识,在这一过程中对盘算机体系的熟悉有了很大的提高。
附件

列出所有的中心产物的文件名,并予以阐明起作用。
hello1:采用指定编译选项得到的可实行目标文件
hello.i:hello.c预处理后的文件
hello.s:hello.i编译后的文件
hello.o:hello.s汇编后得到文件
hello:hello.o颠末链接得到的可实行目标文件
elf.txt:使用readelf查看hello.o的结果
elf_exe.txt:使用readelf查看hello的结果
dump_hello.txt:hello.o反汇编得到的文件
dump_hello_exe.txt:hello可实行文件反汇编得到的文件
参考文献

Randal E.Bryant等.深入理解盘算机体系(原书第3版). 北京:机器工业出版社 2016.7:2.
Printf函数实现的深入剖析. https://www.cnblogs.com/pianist/p/3315801
理解链接之链接的基本概念. https://www.jianshu.com/p/4c660bea1fa9

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