ToB企服应用市场:ToB评测及商务社交产业平台
标题: 【CSAPP】步伐人生-Hello’s P2P [打印本页]
作者: 勿忘初心做自己 时间: 2025-1-11 23:04
标题: 【CSAPP】步伐人生-Hello’s P2P
计算机体系
大作业
题 目 步伐人生-Hello’s P2P
专 业 数学+计算机双学位
学 号 2023110224
班 级 23SXS11
学 生 苏一鸣
指 导 教 师 史先俊
计算机科学与技能学院
2024年5月
摘 要
本文以hello.c步伐为例,逐步的具体分析了计算机在生成hello可实行文件的预处理、编译、汇编、链接、进程管理等整个生命周期。本文将这个周期化为八个章节,由理论到实践,从概念作用出发,辅以实际操作及截图阐明,阐述了计算机体系的工作原理和体系布局。
关键词:生命周期;计算机体系;体系布局 ;阶段
目 录
第1章 概述
1.1 Hello简介
1.2 环境与工具
1.3 中间效果
1.4 本章小结
第2章 预处理
2.1 预处理的概念与作用
2.2在Ubuntu下预处理的命令
2.3 Hello的预处理效果解析
2.4 本章小结
第3章 编译
3.1 编译的概念与作用
3.2 在Ubuntu下编译的命令
3.3 Hello的编译效果解析
3.4 本章小结
第4章 汇编
4.1 汇编的概念与作用
4.2 在Ubuntu下汇编的命令
4.3 可重定位目的elf格式
4.4 Hello.o的效果解析
4.5 本章小结
第5章 链接
5.1 链接的概念与作用
5.2 在Ubuntu下链接的命令
5.3 可实行目的文件hello的格式
5.4 hello的虚拟地址空间
5.5 链接的重定位过程分析
5.6 hello的实行流程
5.7 Hello的动态链接分析
5.8 本章小结
第6章 hello进程管理
6.1 进程的概念与作用
6.2 简述壳Shell-bash的作用与处理流程
6.3 Hello的fork进程创建过程
6.4 Hello的execve过程
6.5 Hello的进程实行
6.6 hello的非常与信号处理
6.7本章小结
第7章 hello的存储管理
7.1 hello的存储器地址空间
7.2 Intel逻辑地址到线性地址的变换-段式管理
7.3 Hello的线性地址到物理地址的变换-页式管理
7.4 TLB与四级页表支持下的VA到PA的变换
7.5 三级Cache支持下的物理内存访问
7.6 hello进程fork时的内存映射
7.7 hello进程execve时的内存映射
7.8 缺页故障与缺页中断处理
7.9动态存储分配管理
7.10本章小结
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
8.2 简述Unix IO接口及其函数
8.3 printf的实现分析
8.4 getchar的实现分析
8.5本章小结
结论
附件
参考文献
第1章 概述
1.1 Hello简介
P2P:即从步伐(Program)到进程(Process)的转变,说的是hello.c这个C语言源代码变为运行时的进程这一过程。过程包含四个阶段:预处理阶段,编译阶段,汇编阶段,链接阶段,终极生成可实行文件。一旦准备就绪,便可在shell中启动它,而shell也将为其分配进程空间。
020:即From Zero-0 to Zero-0。hello.c颠末预处理、编译、汇编、链接得到可实行文件,并在操作体系的帮助下为其分配资源,使其成为一个progress,实现从无到有。在Hello实行结束后,操作体系将分配给他的资源全部收回,完成从有到无的过程。
1.2 环境与工具
硬件环境:处理器:13th Gen Intel(R) Core(TM) i7-13700H 2.40 GHz
RAM:32.00GB
体系:64位操作体系
软件环境:Windows11 64位,Ubuntu 20.04
开发与调试工具:Codeblocks、gcc、edb、Objdump
1.3 中间效果
hello.i 预处理后得到的文本文件
hello.s 编译后得到的汇编语言文件
hello.o 汇编后得到的可重定位目的文件
hello.elf 用readelf读取hello.o得到的ELF格式信息
hello1.elf 用readelf读取hello得到的ELF格式信息
hello.asm 反汇编hello.o得到的反汇编文件
hello1.asm 反汇编hello可实行文件得到的反汇编文件
hello 可实行文件1.4 本章小结
本章先说了hello的P2P,020流程,然后阐明了完成大作业的设备的硬件设置、软件平台、开发工具以及在完成大作业的过程中生成的各个中间效果文件的名称和功能。
第2章 预处理
2.1 预处理的概念与作用
概念:预处理是步伐编译流程中的首个步骤,在这一阶段,预处理器会对源代码文件进行初步的处理和转换以准备实际的编译。
作用:头文件包含:将所需的头文件内容插入到源代码中。
宏定义更换:辨认源代码中的宏定义,并在编译前将其更换为相应的代码片段。
条件编译:根据条件判断是否编译某段代码。
解释和空白符处理:删除源代码中的解释和多余的空白符。
2.2在Ubuntu下预处理的命令
2.3 Hello的预处理效果解析
打开hello.i文件与源步伐对比,发现预处理指令被扩展成了3000行左右代码,main函数主体保持不变,阐明在预处理过程中头文件内容插入到源代码中。
下图即为引用库的信息的一个实例:
在hello.i文件中找不到在源文件中的解释,这阐明预处理器确实会删除源代码中的解释和多余的空白符。
2.4 本章小结
本章先是阐明了预处理的概念与作用,同时用gcc命令生成hello.i文件,在对比中,验证了预处理的具体作用。
第3章 编译
3.1 编译的概念与作用
概念:将用高级步伐设计语言编写的源代码转化为与之等价的汇编语言步伐的过程。
作用:将高级语言编写的源代码转换为汇编语言,使得步伐能够在特定的计算机硬件上实行。同时,编译也有助于进步步伐的可移植性。编译的基本流程涵盖了词法分析、语法分析、语义分析、中间代码生成、代码优化和目的代码生成等多个关键阶段。
3.2 在Ubuntu下编译的命令
3.3 Hello的编译效果解析
hello.s内容如下:
.file "hello.c"
.text
.section .rodata
.align 8
.LC0:
.string "\347\224\250\346\263\225: Hello \345\255\246\345\217\267 \345\247\223\345\220\215 \346\211\213\346\234\272\345\217\267 \347\247\222\346\225\260\357\274\201"
.LC1:
.string "Hello %s %s %s\n"
.text
.globl main
.type main, @function
main:
.LFB6:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movl %edi, -20(%rbp)
movq %rsi, -32(%rbp)
cmpl $5, -20(%rbp)
je .L2
leaq .LC0(%rip), %rdi
call puts@PLT
movl $1, %edi
call exit@PLT
.L2:
movl $0, -4(%rbp)
jmp .L3
.L4:
movq -32(%rbp), %rax
addq $24, %rax
movq (%rax), %rcx
movq -32(%rbp), %rax
addq $16, %rax
movq (%rax), %rdx
movq -32(%rbp), %rax
addq $8, %rax
movq (%rax), %rax
movq %rax, %rsi
leaq .LC1(%rip), %rdi
movl $0, %eax
call printf@PLT
movq -32(%rbp), %rax
addq $32, %rax
movq (%rax), %rax
movq %rax, %rdi
call atoi@PLT
movl %eax, %edi
call sleep@PLT
addl $1, -4(%rbp)
.L3:
cmpl $8, -4(%rbp)
jle .L4
call getchar@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE6:
.size main, .-main
.ident "GCC: (Ubuntu 9.4.0-1ubuntu1~20.04) 9.4.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
/*以下按照大作业PPT中P4给出的参考C数据与操作进行排序*/
3.3.1数据:
字符串常量:section .rodata表明是只读段,包含LC0,LC1两个字符串常量。
局部变量:数据存在栈中,如循环中的i,参数argc和argv。
3.3.2赋值操作:
如将循环计数器i初始化为0,在汇编代码中通过movl指令实现。
3.3.3算术操作:
如循环中的i++,在汇编代码中通过addl指令实现。
3.3.5关系操作:
如比力argc是否即是5或判断i是否小于8,在汇编代码中通过cmpl指令实现。
3.3.6数组/指针/布局操作:
Hello中只有一个数组,即参数argv。而对数组argv[]只有读取的操作。利用%rbp和%rax来找到并访问数组中的各个元素,
3.3.7控制转移(指令):
根据关系操作设置的条件码,控制转移指令(如je、jle等)决定步伐的实行流程。比方,假如argc不即是5,步伐可能会跳转到错误处理代码;假如i小于即是8,步伐会继承循环。
3.3.8函数操作:
如main函数中的argc和argv。
利用call指令调用头文件中的printf,atoi等函数
假如函数有返回值(如main函数返回0),返回值会生存在%rax中。函数通过指令ret返回。
3.3.8 类型转换
类型转换在汇编代码中通常是隐式的,但在某些情况下(如使用atoi函数),可以明确看到从字符串到整数的转换。atoi函数继承一个字符串参数,并返回一个整数值,这个整数值随后可以在步伐中作为其他函数(如sleep)的参数使用。
3.4 本章小结
本章介绍了编译的大致过程,重点解析了编译器是怎么处理C语言的各个数据类型以及各类操作的。
第4章 汇编
4.1 汇编的概念与作用
概念:汇编是指汇编器将包含汇编语言的.s文件翻译为机器语言指令,并把这些指令打包成为一个可重定位目的文件的格式,生成目的文件.o文件。
作用:将高级语言(即汇编语言)转化为机器能够直接辨认并实行的代码文件。
4.2 在Ubuntu下汇编的命令
4.3 可重定位目的elf格式
分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特殊是重定位项目分析。
- ELF头:以一个l6字节的序列开始,这个序列形貌了生成该文件的体系的字的大小和字节序次。ELF头剩下的部分包含ELF头的大小、目的文件的类型、机器类型、节头部表的文件偏移,以及节头部表中条目的大小和数量。
(2)节表头:纪录ELF文件中各节位置,大小,偏移等信息。
(3)重定位节:重定位节纪录了可重定位文件中全部重定位条目的信息,包罗重定位类型偏移量等信息。这些信息用于在链接过程中计算出变量符号正确的地址,包含相对寻址和直接寻址。
- 符号表:符号表中存放着源文件中定义和引用的函数,全局变量,静态局部变量的信息,它帮助链接器正确解析符号引用,确保步伐能够正确链接和运行。
4.4 Hello.o的效果解析
hello.o反汇编效果如下:
hello.o: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 83 ec 20 sub $0x20,%rsp
c: 89 7d ec mov %edi,-0x14(%rbp)
f: 48 89 75 e0 mov %rsi,-0x20(%rbp)
13: 83 7d ec 05 cmpl $0x5,-0x14(%rbp)
17: 74 16 je 2f <main+0x2f>
19: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 20 <main+0x20>
1c: R_X86_64_PC32 .rodata-0x4
20: e8 00 00 00 00 callq 25 <main+0x25>
21: R_X86_64_PLT32 puts-0x4
25: bf 01 00 00 00 mov $0x1,%edi
2a: e8 00 00 00 00 callq 2f <main+0x2f>
2b: R_X86_64_PLT32 exit-0x4
2f: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
36: eb 53 jmp 8b <main+0x8b>
38: 48 8b 45 e0 mov -0x20(%rbp),%rax
3c: 48 83 c0 18 add $0x18,%rax
40: 48 8b 08 mov (%rax),%rcx
43: 48 8b 45 e0 mov -0x20(%rbp),%rax
47: 48 83 c0 10 add $0x10,%rax
4b: 48 8b 10 mov (%rax),%rdx
4e: 48 8b 45 e0 mov -0x20(%rbp),%rax
52: 48 83 c0 08 add $0x8,%rax
56: 48 8b 00 mov (%rax),%rax
59: 48 89 c6 mov %rax,%rsi
5c: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 63 <main+0x63>
5f: R_X86_64_PC32 .rodata+0x2c
63: b8 00 00 00 00 mov $0x0,%eax
68: e8 00 00 00 00 callq 6d <main+0x6d>
69: R_X86_64_PLT32 printf-0x4
6d: 48 8b 45 e0 mov -0x20(%rbp),%rax
71: 48 83 c0 20 add $0x20,%rax
75: 48 8b 00 mov (%rax),%rax
78: 48 89 c7 mov %rax,%rdi
7b: e8 00 00 00 00 callq 80 <main+0x80>
7c: R_X86_64_PLT32 atoi-0x4
80: 89 c7 mov %eax,%edi
82: e8 00 00 00 00 callq 87 <main+0x87>
83: R_X86_64_PLT32 sleep-0x4
87: 83 45 fc 01 addl $0x1,-0x4(%rbp)
8b: 83 7d fc 08 cmpl $0x8,-0x4(%rbp)
8f: 7e a7 jle 38 <main+0x38>
91: e8 00 00 00 00 callq 96 <main+0x96>
92: R_X86_64_PLT32 getchar-0x4
96: b8 00 00 00 00 mov $0x0,%eax
9b: c9 leaveq
9c: c3 retq
与第3章的 hello.s进行对照分析,效果如下:
- 对于每一条指令,为其增加了一个十六进制的表现形式,即该指令的机器语言编码。如sub指令:
- 反汇编文件中的全部操作数都改为十六进制。如上图中的$32被改为$0x20。
- 反汇编的跳转指令中,全部跳转的位置被表现为主函数+段内偏移量这样确定的地址,而不再是段名称。
- 对于函数调用,反汇编文件中对函数的调用与重定位条目相对应(如上图中的第18行,21行),而在.s文件中如下图所示:
4.5 本章小结
本节阐明了汇编的概念和作用,在汇编阶段过程中生成一个可重定位的文件,并生成ELF格式,具体解析ELF表的各个部分,同时辨析了反汇编代码与hello.s的差异之处。
第5章 链接
5.1 链接的概念与作用
概念:将分散的代码与数据片段汇集并融合成一个完整的文件的过程。这一文件能够被载入内存并启动实行。
作用:链接的存在使得分离编译成为可能。这意味着不必将大型应用步伐构造成单个庞大的源文件,而是可以将其划分为更小、更易于管理的模块。这些模块可以独立地进行修改和编译。当对其中一个模块做出修改时,只需重新编译该模块,并重新进行链接操作即可,无需对整个应用进行编译,大大进步了开发效率。
5.2 在Ubuntu下链接的命令
5.3 可实行目的文件hello的格式
分析hello的ELF格式,用readelf等列出其各段的基本信息,包罗各段的起始地址,大小等信息。
(1)ELF头:颠末对比发现,hello1.elf中的ELF头与hello.elf中的ELF头包含的信息种类基本是雷同的,二者都以形貌了生成该文件的体系的字的大小和字节序次的16字节序列Magic开始,剩下的部分包含帮助链接器语法分析和解释目的文件的信息。与hello.elf相比力,hello1.elf中的基本信息未发生改变(如Magic,类别等),而类型发生改变,步伐头大小和节头数量增加,而且获得了入口地址0x4010f0。
(2)节头:形貌了各个节的大小、偏移量等属性信息。链接器链接时,将各个文件的雷同段合并成位一个段,并据此更新符号地址等信息。
(3)步伐头:步伐头部分是一个布局数组,形貌了体系准备步伐实行所需的段或其他信息。
(4)符号表中生存着定位、重定位步伐中符号定义和引用的信息,全部重定位需要引用的符号都在其中声明。
(5)重定位节:这个节包含了一些重定位条目,用于在加载步伐时调整代码和数据段中的地址。
5.4 hello的虚拟地址空间
使用edb打开hello从Data Dump窗口观察hello加载到虚拟地址的情况,查看各段信息。如图:
5.5 链接的重定位过程分析
(以下格式自行编排,编辑时删除)
objdump -d -r hello 分析hello与hello.o的差异,阐明链接的过程。
联合hello.o的重定位项目,分析hello中对其怎么重定位的。
(1)链接后的反汇编文件hello2.asm中,多出了.plt,puts@plt,printf@plt,getchar@plt,exit@plt,sleep@plt等函数的代码。这是由于动态链接器将共享库中hello.c用到的函数加入可实行文件中。
(2) 在链接过程中,链接器解析了重定位条目,并计算相对间隔,修改了对应位置的字节代码为PLT 中相应函数与下条指令的相对地址,从而得到完整的反汇编代码。
在重定位的过程中,重定位节和符号定义链接器将全部类型雷同的节合并在一起作为可实行目的文件的节。然后修改全部对这些符号的引用,使得它们指向这个内存位置。
5.6 hello的实行流程
通过edb的调试,一步一步地纪录下call命令进入的函数。
(I)开始实行:_start、_libe_start_main
(2)实行main:_main、printf、_exit、_sleep、getchar
(3)退出:exit
5.6.2子步伐名或地址
步伐名 步伐地址
_start 0x4010f0
_libc_start_main 0x2f12271d
main 0x401125
_printf 0x4010a0
_sleep 0x4010e0
_getchar 0x4010b0
_exit 0x4010d0
5.7 Hello的动态链接分析
当步伐调用一个由共享库定义的函数时,由于编译器无法猜测这时候函数的地址是什么,编译体系提供了耽误绑定的方法,将过程地址的绑定推迟到第一次调用该过程时。耽误绑定主要通过GOT与PLT的写作来确定函数的地址。GOT是数据段的一部分,而PLT是代码段的一部分。其中,PLT数组生存着动态库函数的PLT条目,GOYT中除0、1、2外的每个条目对应于一个被调用的函数,其地址需要在运行时被解析。每个条目都有一个相匹配的PLT条目。
我们要观察.got.plt的变革,即从0x404000开始观察。
运行前的状态
开始运行的状态,可以看到本来为空的地址发生了变革。
5.8 本章小结
本章先是介绍了链接的基本概念和它所发挥的功能。接着展示了如何通过命令链接的方式生成名为hello的可实行文件,并具体解析了该文件在ELF格式下的内容布局。借助edb工具观察了其虚拟地址空间的布局。最后,深入剖析了重定位过程的细节、步伐的实行流程以及动态链接的工作原理。
第6章 hello进程管理
6.1 进程的概念与作用
概念:进程的经典定义就是一个实行中步伐的实例。
作用:进程为步伐提供了一种假象,步伐好像是独占的使用处理器和内存,处理器好像是无间断地一条接一条地实行我们步伐中的指令。进程作为一个实行中步伐的实例,体系中每个步伐都运行在某个进程的上下文中。
6.2 简述壳Shell-bash的作用与处理流程
壳Shell-bash的作用:Shell是一个交互型应用级步伐,也被称为命令解析器,它为用户提供一个操作界面,继承用户输入的命令,并调度相应的应用步伐。
处理流程:首先从终端读入命令,对命令进行分析,假如该命令为内置命令,则立即实行命令,否则调用fork()函数创建一个新的子进程,在该子进程的上下文中实行指定的步伐。判断该步伐为前台步伐还是后台步伐,假如为前台步伐则等候步伐实行结束,若为后台步伐则将其放回后台并返回。在过程中shell可以继承从键盘输入的信号并对其进行处理。
6.3 Hello的fork进程创建过程
在终端中输入./hello命令,shell接收后会对其进行解析,确认是一个可实行步伐,则父进程用fork函数去创建一个子进程,子进程与父进程有着雷同的代码段、数据段、堆、共享库以及栈段,但二者pid差异。之后父进程将子进程放入一个进程组中。
6.4 Hello的execve过程
在子进程被创建出来后,子进程会去调用execve函数来加载可实行文件到当进步程。之后将原有用户区域删除,将hello的代码段、数据段映射到新的空间,创造出新的区域,并加载hello中的共享库,最后实行hello。
6.5 Hello的进程实行
在终端中输入./hello命令后,操作体系进行解析,定位到相应的可实行文件,之后调用fork函数为其创建一个子进程。而由于fork是一个体系调用,需要操作体系内核的到场,以是此时处理器会从用户态立即转入到焦点态,控制流转入操作体系内核步伐,内核会将原进程目前的上下文暂时生存起来,然后通过进程调度步伐找到要切换的进程, 加载其被生存的上下文,将控制流交给该进程,处理器重新转入到用户态。在这一过程中操作体系会给每个进程分配时间片,决定其与运行时间。当时间片结束或 I/O 事件发生时,中断会触发从用户态到焦点态的切换,操作体系处理中断并可能进行进程调度。最后当进程结束时,操作体系会清理占用的资源,并发送相应的信号给父进程。
6.6 hello的非常与信号处理
(以下格式自行编排,编辑时删除)
hello实行过程中会出现哪几类非常,会产生哪些信号,又怎么处理的。
步伐运行过程中可以按键盘,如不停乱按,包罗回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,阐明非常与信号的处理。
非常类型
处理方式:
(1)中断非常:
(2)陷阱非常:
(3)故障非常:
(4)终止非常:
(1)不停乱按:
此时对步伐的实行并无影响
此时对步伐的实行并无影响
按下Ctrl + C,Shell进程收到SIGINT信号,Shell结束并回收hello进程。
(4)运行时按下Ctrl + Z
按下Ctrl + Z,Shell进程收到SIGSTP信号,Shell表现屏幕提示信息并挂起hello进程。
(5)ps jobs
对hello进程的挂起可由ps和jobs命令查看,可以发现hello进程确实被挂起而非被回收,且其job代号为1。
(5)pstree:
在Shell中输入pstree命令,可以将全部进程以树状图表现:
Kill:
输入kill命令,则可以杀死指定(进程组的)进程:
6.7本章小结
本章概述了进程的基本概念、它在计算机体系中的关键作用,以及shell的焦点功能及其处理流程。分析了hello可实行文件实行时进程的创建、回收过程,阐明了进程的创建,加载过程。并进行了一系列非常的模仿,并对模仿效果进行了分析。
第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址:在有地址变换功能的计算机中,访问指令给出的地址(操作数)叫逻辑地址,也叫相对地址。比方hello的反汇编步伐中的地址需要加上段基地址才是真正的地址。
线性地址:是逻辑地址到物理地址变换之间的中间层。比方hello的反汇编步伐中的部分地址是由绝对地址加上一个偏移量构成的。
虚拟地址是线性地址经太过页机制转换后得到的地址。表面上看扩大了地址空间。当hello步伐运行时,操作体系为其分配一个虚拟地址空间。Hello反汇编中可以看到都有固定的地址。
物理地址:是真正的内存地址,CPU可以直接将物理地址传送到与内存相连的地址信号线上,对实际存在内存中的数据进行访问。物理地址决定了数据在内存中真正存储在那边。是hello的实际地址或绝对地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
一个逻辑地址由两部分组成,段标识符和段内偏移量。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号。背面3位包含一些硬件细节。可以通过段标识符的前13位,直接在段形貌符表中找到一个具体的段形貌符,这个形貌符就形貌了一个段。一些全局的段形貌符,就放在“全局段形貌符表(GDT)”中,一些局部的,比方每个进程自己的,就放在所谓的“局部段形貌符表(LDT)”中,过这个段形貌符得到段的基址,与段内偏移地址相加得到的64位整数就是线性地址。
7.3 Hello的线性地址到物理地址的变换-页式管理
虚拟地址空间会被分为若干页,即分页机制。CPU对于一个线性地址会取它的高若干位,通过它们去存储在内存中的页表里查询对应的页表条目,得到这个线性地址对应的物理页起始地址,然后与线性地址的低位(页中的偏移)相加就是物理地址。
7.4 TLB与四级页表支持下的VA到PA的变换
当CPU生成一个虚拟地址(VA)时,这个地址会被传递给内存管理单元(MMU)。MMU随后会利用虚拟地址的高位部分作为TLB(Translation Lookaside Buffer)的标签(TLBT)和索引(TLBI),来在TLB中查找与虚拟地址匹配的物理地址。假如TLB中成功匹配到了相应的条目,那么MMU就能直接获取到对应的物理地址(PA),假如TLB中没有掷中,那么MMU就需要通过查询页表来获取物理地址。MMU利用虚拟地址中的VPN1部分作为偏移量,在第一级页表中查找相应的页表项(PTE)。找到PTE后,MMU继承按照雷同的步骤在第二级、第三级和第四级页表中进行查找。终极,在第四级页表中,MMU会找到物理页号(PPN),并将其与虚拟地址中的页内偏移量(VPO)组合起来,形成终极的物理地址(PA)。这个物理地址随后会被添加到页表缓存(PLT)中,以便后续快速访问。
7.5 三级Cache支持下的物理内存访问
得到物理内存后,将其拆分成标志位,组索引,块内偏移,然后到L1cache中探求,若未掷中则到L2cache,L3cache,直到内存,硬盘中取探求。找到后再逐级向上进行更新。下图是在每一级寻址的过程。
7.6 hello进程fork时的内存映射
当shell使用fork创建子进程时,内核为新的子进程创建各种数据布局,并分配给子进程一个唯一的PID,为了给它创建虚拟内存空间,内核创建了当进步程的mm_struct、区域布局和页表的原样副本,将两个进程的页面都标志为只读,并将两个进程中的每个区域布局都标志为私有的写时复制。这样,在新进程里,最开始的时候它的虚拟内存和原进程的虚拟内存映射雷同,但当这两个进程中的任意一个进行写操作时,写时复制机制就会创建新页面,这样两个进程的地址空间就在逻辑上私有了。
7.7 hello进程execve时的内存映射
进程实行execve加载可实行文件hello,先将当进步程虚拟内存空间中的用户部分的已存在的区域布局删除,再为新步伐hello的代码、数据、bss段和栈段区域创建新的区域布局,这些新的区域都是私有且写时复制的。代码和数据区域被映射为hello可实行文件中的.text和.data节,bss区域哀求二进制0故映射到匿名文件,栈和堆被初始化为空。然后execve会将hello链接的动态链接库(共享对象)映射到虚拟地址空间的共享区域内。终极跳转到hello的入口点。
7.8 缺页故障与缺页中断处理
缺页故障:当引用一个地址时,在内存中无法找到,则会触发一次缺页非常。
缺页中断:当出现缺页故障时,实行操作体系提供的缺页中断处理步伐,然后缺页中断处理步伐能够将存在磁盘上的页使用一定的更换计谋加载到物理内存,并更新页表。然后重新实行之前的指令。
7.9动态存储分配管理
动态内存管理的基本方法是使用动态内存分配器来管理进程的堆。堆是进程的虚拟内存区域,在未初始化的数据区域之后开始并向下增长。内核通过一个变量brk来维护堆的顶部位置。动态内存分配器维护进程虚拟地址空间中的的堆区域,它将堆视作一组差异大小的块的聚集来维护,每个块是一段一连的虚拟内存碎片。管理计谋:立即合并: 每次释放都合并 。耽误合并: 实验通过耽误合并,即直到需要才合并来进步释放的性能。malloc函数会向内存申请一块一连可用的空间,并返回一个指向该空间的指针(指针的类型为void*),假如开辟失败,则返回一个NULL,而参数size决定了malloc所要开辟空间的字节大小。这是为了给printf函数提供输出的缓存区间。
7.10本章小结
本章主要介绍了hello的存储器地址空间、段式管理、页式管理,虚拟地址VA到物理地址PA的转换、物理内存访问,分析了hello进程fork时的内存映射、hello进程、execve时的内存映射、缺页故障与缺页中断处理以及动态存储分配管理。
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模子化:在Linux体系中,文件被视为一个字节序列,为了将各种IO设备(如网络、磁盘、终端)进行统一管理,Linux接纳了一种模子化的方式,即将这些设备都抽象为文件,并通过读和写操作对这些文件进行输入和输出。
设备管理:为了实现设备的管理,Linux内核引入了一种简单而低级的应用接口,称为Unix I/O接口。通过Unix I/O接口,全部的输入和输出操作都可以被视为对相应文件的读和写操作。
8.2 简述Unix IO接口及其函数
Unix IO接口:Unix的I/O接口是操作体系提供的一组API(应用步伐接口),用于处理输入/输出操作。通过这些接口,步伐能够与外部设备(如磁盘、网络、终端等)进行数据互换。Unix的I/O接口主要基于文件形貌符,支持对文件、设备、套接字等的操作,提供了丰富的函数用于文件和数据的读写、控制和管理。
函数:
打开文件,open函数 int open(char* filename,int flags,mode_t mode);open函数是用来打开一个已存在的文件或创建一个新文件的函数。它将文件名(filename)作为参数,同时接收两个可选参数:flags和mode。
关闭文件,close函数 int close(int fd);进程通过调用close 函数关闭一个打开的文件,关闭一个已关闭的文件会出错。fd是要关闭的进程形貌符,返回操作效果。
读文件,read函数ssize_t read(int fd,void *buf,size_t n)read函数从形貌符为fd的当前文件位置复制最多n个字节到内存位置buf。返回值-1表现一个错误,而返回值0比怕是EOF。否则返回值表现的是实际传送的字节数量。
写文件,write函数 ssize_t wirte(int fd,const void *buf,size_t n)进程调用write函数实行输出,write函数从内存位置buf复制至多n个字节到形貌符为 fd 的当前文件位置。
8.3 printf的实现分析
Printf函数如下:
int printf(const char *fmt, ...)
{
int i;
char buf[256];
va_list arg = (va_list)((char*)(&fmt) + 4);
i = vsprintf(buf, fmt, arg);
write(buf, i);
return i;
}
vsprintf 会遍历 fmt 字符串,根据占位符逐个将后续参数填充到格式化字符串中,生成终极的字符数据。格式化后的字符串通常会传递给操作体系的 write 体系调用int0x80或syscall来输出到终端或其他输出设备。write 函数的作用是:从ASCII到字模库到表现vram(存储每一个点的RGB颜色信息)。表现芯片按照革新频率逐行读取vram,并通过信号线向液晶表现器传输每一个点(RGB分量)。通过这种凡事将数据从用户空间写入到内核空间的文件形貌符,终极将数据送往设备进行表现。
8.4 getchar的实现分析
getchar函数的作用是从从缓冲区中读入单个的字符。假如去读取成功,它的返回值是用户输入的第一个字符的ASCII码;假如读取失败,则返回-1,并将用户输入的字符回显到屏幕。
异步非常-键盘中断的处理:当用户按下键盘上的按键时,会触发键盘中断。键盘中断处理子步伐负责处理这些中断,将按键扫描码转换为ASCII码,并生存到体系的键盘缓冲区。getchar等调用read体系函数,通过体系调用读取按键ascii码,直到继承到回车键才返回。
8.5本章小结
本节介绍了Linux的I/O设备的基本概念和管理方法,还阐明了Unix的I/O接口及其对应的函数。最后对printf与getchar函数的实现原理进行分析。
结论
(1)预处理:对hello.c进行预处理,将文件调用的全部外部库文件展开合并,生成hello.i文件。
(2)编译:颠末词法分析,语法分析,语义分析以及优化,将hello.i文件翻译成为一个包含汇编语言的文件hello.s。
(3)汇编:将hello.s翻译成为一个可重定位目的文件hello.o。
(4)链接:将hello.o文件和可重定位目的文件和动态链接库链接起来,生成一个可实行目的文件hello。
(5)运行:在shel1中输入./hello 2023110224 苏一鸣 18937458059 3。
(6)创建进程:终端判断输入的指令不是shell内置指令,于是调用fork函数创建一个新的子进程。
(7)加载步伐:调用execve函数,启动加载器,映射虚拟内存,进入步伐入口后步伐开始载入物理内存,然后进入main函数。
(8)实行该进程,硬件和软件协同工作完成地址翻译和物理内存的访问,动态分配内存,处理信号和非常,进行上下文切换等操作。
(9)进程结束,操作体系结束并回收hello的进程。
通过这次大作业,我深刻感受到了计算机体系的细密与强大。每一个看似简单的任务背后,都蕴含着计算机内部复杂而精细的操作。这些操作不但体现了严谨的逻辑设计,也展示了当代工艺的精巧与聪明。
附件
文件名 功能
hello.c 源步伐
hello.i 预处理后得到的文本文件
hello.s 编译后得到的汇编语言文件
hello.o 汇编后得到的可重定位目的文件
hello.elf 用readelf读取hello.o得到的ELF格式信息
hello1.elf 用readelf读取hello得到的ELF格式信息
hello.asm 反汇编hello.o得到的反汇编文件
hello1.asm 反汇编hello可实行文件得到的反汇编文件
hello 可实行文件
参考文献:
- Randal E.Bryant David R.O'Hallaron.深入明确计算机体系(第三版).机器工业出版社,2024.
[2]https://blog.csdn.net/dong_daxia/article/details/95328479
[3]https://blog.csdn.net/dingdingdodo/article/details/107747509
[4] https://www.cnblogs.com/pianist/p/3315801.html .
[5] https://www.cnblogs.com/diaohaiwei/p/5094959.html
[6] https://www.cnblogs.com/fanzhidongyzby/p/3519838.html.
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) |
Powered by Discuz! X3.4 |