马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
计算机系统
大作业
题 目 步伐人生-Hello’s P2P
专 业 数据科学与大数据技能
学 号 2021111662
班 级 2103501
学 生 盖嘉怡
指 导 教 师 史先俊
计算机科学与技能学院
2023年4月
摘 要
本文基于《深入了解计算机系统》一书,详细论述了hello步伐的完整生命周期。第一到五章介绍了hello步伐在Linux环境下从源文件到预处理、编译、汇编、链接到最后实行的过程,第六到九章详细介绍了步伐实行过程中的进程管理、内存空间管理和I/O与CPU信息交互,以上各章内容有助于加深对计算机系统的明白。
关键词:计算机系统;步伐的生命周期;进程管理;内存和空间管理;I/O与计算机文件交互
目 录
第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简介
根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。
1.1.1Hello的P2P
P2P是指from program to process,即从步伐到进程。Hello步伐经预处理、编译、汇编、链接天生hello.out可实行文件。在shell中输入./hello ,shell会将其当做一个可实行文件去实行。之后会调用fork()函数,在当进步程中创建一个子进程,子通过调用函数execve( )在当前子进程的上下文中运行并加载hello步伐,至此完成了从.c源步伐到进程的转换。
1.1.2Hello的020
020是指from zero_0 to zero_0,即刚开始步伐并不存在于内存上,在运行竣事后,占用的相应内存空间会被开释,也不会在内存空间保存,因此是从0到0的过程,详细过程如下:
创建好子进程后,子进程通过调用execve()函数,调用启动加载器。由于该子进程和父进程共享相同的假造地址空间,在子进程的上下文中运行新的hello步伐需要先删除子进程的假造内存段,创建一个新的代码段、数据段、栈和堆段,并完成从可实行文件到假造内存空间的映射。在此期间,I/O设备与主存进行信息交配合步伐的实行。步伐运行竣事后,子进程终止,由父进程回收,开释步伐在运行过程中占用的内存空间。
1.2 环境与工具
列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开发与调试工具。
硬件环境:
CPU:AMD Ahlon Silver 3050U with Radeon Graphics
X64 CPU;2.30GHz;8G RAM;512GHD Disk
软件环境:Windows 10 64位
调试工具:Codeblocks 64位;vi/vim/gedit+gcc
VMware Workstation 17 Player
1.3 中心结果
文件名
| 作用
| main.c
| 源步伐
| main.i
| 预处理文件
| main.s
| 汇编文件
| main.o
| 可重定位的目标步伐
| main
| 可实行步伐
| main_o.elf
| 用readelf读取main.o文件的ELF格式信息
| main.asm
| main.o反汇编文件
| main.txt
| main文件反汇编
| main2.elf
| 用readelf读取main文件的ELF格式信息
|
1.4 本章小结
本章从宏观上介绍了hello步伐从预处理、编译、汇编、连接到最后实行竣事的全部过程。并列出了步伐实行过程中的软硬件环境以及用到的相干调试工具。
第2章 预处理
2.1 预处理的概念与作用
2.2.1 预处理的概念
预处理是指在源文件编译前由预处理器完成的预备工作,主要是对预处理指令进行剖析,预处理后会得到一个同名的.i文件。
2.2.2 预处理的作用
(1)宏定义(宏展开):
将步伐中全部用宏定义的数据更换为原始常量数据,如#define num 10,预处理时会将全部步伐中出现的num用10代替。
(2)文件包含:
将全部的头文件对应的源步伐插入到步伐文本中,如#include<stdio.h>。
(3)条件编译:
给编译制定一个编译条件,当满足此条件时,源代码部门才会被编译,若不满足,则会将相应的代码从源步伐文本中移除。比方:#ifdef COMPILETIME,只有在对COMPLETIME进行宏定义时才会对下面的步伐进行编译,可运用于库打桩技能中。
2.2在Ubuntu下预处理的命令
(图2.3.1)
2.3 Hello的预处理结果剖析
预处理后得到的.i文件比原来的文件长度增加很多,主要由以下几部门构成:
(图2.3.1)
(图2.3.2)
(图2.3.3)
(图2.3.4)
2.4 本章小结
本章主要介绍对源步伐的预处理操纵,并对预处理文件的内容做了较为详细的介绍。
第3章 编译
3.1 编译的概念与作用
3.1.1编译的概念
编译是指将预处理文件在编译器的处理下天生相应的汇编语言步伐的过程。
3.1.2编译的作用
(1)进行根本的语法检查,如果存在语法错误,编译器会给出错误提示。
(2)将C语言代码翻译成相应的汇编代码,根据设定的编译器品级对步伐进 行初步的优化来提升步伐性能。
3.2 在Ubuntu下编译的命令
3.3 Hello的编译结果剖析
(1)函数堆栈开辟(如图3.3-1)
首先将基址指针%rbp压栈,之后通过移动%rsp指针为函数开辟存储空间,对应汇编代码为:subq $32,%rsp,代表参数分配了50byte(32为16进制数)的空间。
(2)参数传递(如图3.3-1)
%edi存放函数的第一个参数,将其生存在首地址为-20(%rbp)的栈空间 中。%rsi存放函数第二个参数,将其生存在首地址为-32(%rbp)的栈空间中。
其中-20(%rbp),-32(%rbp)分别代表比基址指针%rbp地点地址小0x20byte和小0x32byte的地址,类似于C语言中的指针变量。函数中参数一样平常都用%rbp加上相应偏移量访问。
(图3.3-1)
将第一个参数与4比较,若相称会跳转到.L2部门,否则会顺序实行。其中je代表相称则跳转。
“leaq .LC0(%rip),%rax”是取地址指令,表示将.LC0(%rip)的地址存到%rax中。
(4)赋值指令(如图3.3-2)
“movq %rax,%rdi” 是赋值操纵,将%rax中的内容存到%rdi寄存器中。
(5)Call指令负责调用函数(如图3.3-2)
步伐中调用了以下函数:
call puts@PLT
call exit@PLT
call printf@PLT
call sleep@PLT
call getchar@PLT
在函数调用过程中,会将返回地址压栈,之后将PC赋值为跳转到的函数的地址,在实行完调用的函数即函数return后,如果有返回值,会将返回值生存在%rax中,然后将返回地址pop出,将其赋值给PC,从而实现回到调用函数出息序实行的位置。
(图3.3-2)
subq $32,%rsp指令代表将指针%rsp代表的地址值减50(0x32),即实现了指针的移动。
addq $8,%rax指令代表将寄存器%rax中的数据加8并生存在%rax中。
(图3.3-3)
%edx代表32位数据,%rax代表64位数据,一样平常由寄存器存储的是局部变量
$0,$1等由“$”加数字构成的代表常数,addq 背面的q代表对64位数据进行操纵,addl l代表对32位数据操纵。
而一些常量数据存储在只读数据区,如字符串“Hello %s %s\n”(如图3.3-4).
(图3.3-4)
汇编文件由以下几部门构成:
1.源文件(.file)
2.代码区(.text)
3.只读变量区(.section .rodata)
4.对齐方式(.align)
(图3.3-5)
3.4 本章小结
本章实现了将预处理文件天生汇编文件,分析了汇编文件中的内容,对于main.c中涉及到的汇编指令进行了详细的表明。
第4章 汇编
4.1 汇编的概念与作用
4.1.1汇编的概念
汇编是指汇编语言文件转化成可重定位的二进制目标文件的过程。
4.1.2汇编的作用
将main.s文件中的汇编指令翻译成呆板可以“明白”的二进制指令,并将这些指令打包天生main.o的可实行文件。
4.2 在Ubuntu下汇编的命令
Ubuntu下汇编的命令:gcc -m64 -no-pie -fno-PIC -c main.s -o main.o
(图4.2)
4.3 可重定位目标elf格式
天生elf格式文件并打开的指令:
(图4.3)
4.3.1 elf头
ELF头以一个16字节的序列开始,这个序列形貌了天生该文件的系统的字的大小和字节顺序。ELF头剩下的部门包含帮助链接器语法分析和表明目标文件的信息。其中包罗ELF头的大小、目标文件的范例(可重定位、可实行或者共享的)、呆板范例(如x86-64)、节头部表的文件偏移,以及节头部表中条目标大小和数量,如图4.3.1所示。
(图4.3.1)
4.3.2 节头部表
节头部表用于形貌目标文件中节的信息:名称、范例、地址、偏移量等(如图4.3.2所示)。
(图4.3.2)
4.3.3重定位节
在链接器完成了符号剖析之后会将相同范例的节进行归并,并对其重新分配内存空间,本步伐中重定位的节有.rodata,puts,exit,atoi,sleep,getchar。
并且重定位有两种范例,一种为R_X86_64_PC32,使用32位PC相对地址的引用。在本步伐中.rodata接纳这种重定位方式。另一种是R_X86_64_PLT32,使用32位绝对地址进行重定位,本例中puts,exit,atoi,sleep,getchar接纳这种重定位方式(如图4.3.3所示)。
(图4.3.3)
4.3.4符号表
记录步伐中引用的符号的信息,步伐中共有两种符号,全局符号,局部符号。全局符号包罗非静态函数和全局变量,全局符号中由其他函数定义的符号被称作外部符号。局部符号为前有static修饰的函数和变量,main.o中共定义了11种符号,如图4.3.4所示。
(图4.3.4)
4.4 Hello.o的结果剖析
Objdump反汇编运行结果如图4.4所示:
(图4.4)
4.4.1呆板语言的构成
呆板语言由01二进制指令构成,可以被呆板直接辨认,无需翻译,可以对计算机内部硬件直接控制并完成相应操纵。呆板语言一样平常由操纵码和地址码构成,操纵码控制完成何种运算,地址码给出对哪个存储单元的数据进行运算。
4.4.2呆板语言与汇编语言的映射关系
一条呆板语言对应一条汇编语言步伐,汇编步伐是呆板步伐的符号化表示,可读性更好,而呆板语言则是汇编语言与呆板交互的一种形式。
4.4.3 objdump反汇编文件与汇编文件比对
(1)文件包含的内容:
objdump文件中只包含.text段(图4.4.3_1),而main.s文件中除了代码段还 含有.file,.rodata,.section,.align部门的数据(图4.4.3_2)。
(图4.4.3_1)
(图4.4.3_2)
在main.s文件中跳转指令后加的地址是段地址,而objdump反汇编.asm文件中跟的是相应函数的地址。
(图4.4.3_3 .asm文件中跳转指令)
(图4.4.3_3 .s文件中跳转指令)
在.asm文件调用的函数均给出了函数的地址,.s文件中则是给出了函数的名字,如图4.4.3_4所示。
(图4.4.3_4)
.asm文件中给出了需要重定位的符号的信息:偏移量、符号名称、剖析范例(若符号为函数名还会有PC跳转的偏移),而.s文件中没有,如图4.4.3_5。
(图4.4.3_5)
4.5 本章小结
本章利用汇编器将汇编文件转化成可重定位的目标文件,利用readelf指令检察了可重定位目标文件格式,认识了可重定位目标文件各个部门记录的信息。通过将main.o文件反汇编天生的.asm文件与.s文件比对,更清楚地知道了汇编器对.s文件进行了哪些处理。
第5章 链接
5.1 链接的概念与作用
5.1.1链接的概念
链接是指链接器(ld)将各种代码和数据片断收集并组合成一个单一可实行目标文件的过程。
5.1.2链接的作用
- 链接使得分离编译成为大概,步伐员可以将大的步伐拆分成小的模块,独立地修改编译小的模块,然后由链接器进行链接,方便步伐的修改调试。
- 链接还可以实现共享库,淘汰步伐的代码量。
5.2 在Ubuntu下链接的命令
在Ubuntu下链接的命令:ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/crt1.omain.o/usr/lib/x86_64-linux-gnu/crti.o/usr/lib/x86_64-linux-gnu/libc.so /usr/lib/x86_64-linux-gnu/crtn.o -o main。
5.3 可实行目标文件hello的格式
5.3.1 ELF头
ELF头以一个16字节的Magic序列开始,它形貌了天生该文件的系统的字的大小和字节顺序。ELF头剩下的部门包含帮助链接器语法分析和表明目标文件的信息。其中包罗ELF头的大小、字节顺序、目标文件的范例(可重定位、可实行或者共享的)、呆板范例(如x86-64)、节头部表的文件偏移、入口点地址、步伐头出发点,以及节头部表中条目标大小和数量,如图5.3.1所示。
(图5.3.1)
5.3.2 节头
节头表用于形貌目标文件中节的信息:名称、范例、地址、偏移量等(如图5.3.2所示)
(图5.3.2)
5.3.3 步伐头表
只有可实行步伐才有步伐头表,包含页面大小、假造地址内存段、段大小等信息(图5.3.3)
(图5.3.3)
5.3.4 段节和Dynamic section
(图5.3.4)
5.3.5 重定位节
(图5.3.5)
5.3.6 符号表
(图5.3.6)
5.4 hello的假造地址空间
使用edb加载hello,发现假造内存从0x400000开始。ELF文件存储在低地址部门,如图5.4.1_1所示。
(图5.4.1_1)
通过检察节头表可以知道各段在假造内存的起始地址,.text在0x4010f0处,.rodata在0x402000处,.data在0x404080处,由于此步伐中没有需要剖析的符号,.symtable的偏移量为0(代码段如图5.4.1_2所示,数据段如图5.4.1_3所示)。
(图5.4.1_2)
(图5.4.1_3)
5.5 链接的重定位过程分析
objdump -d -r main.o > main.asm
objdump -d -r main > main.txt
main.o的反汇编文件存储在main.asm中,main的反汇编文件存在main.txt中。
通过对比main.asm与main.txt文件,发现以下差别之处:
main.asm中含有代码段的反汇编步伐,而main.txt文件中含有其他段的代码数据:Disassembly of section .init、Disassembly of section .plt、Disassembly of section .plt.sec、Disassembly of section .text、Disassembly of section .fini(如图5.5所示)。
在main.txt文件中每次函数的调用和跳转指令所给的地址都是直接给出的假造内存的地址或者是相对于PC的偏移量,而在main.asm中给出的是标志过的待重定位的地址或者是相对main函数的偏移地址(如图5.5所示)。
(图5.5)
通过分析以上差别之处,我们可以得出结论,链接器主要完成两项任务:符号剖析和重定位。待全部符号完成剖析后,链接器将全部可以归并的节归并,并将每个带剖析的符号和归并后的节与一个假造内存地址对应。最后将步伐中全部的符号应用修改为重新分配的假造地址,并存储在rel.data节中。
5.6 hello的实行流程
使用edb实行hello,说明从加载hello到_start,到call main,以及步伐终止的全部过程。请列出其调用与跳转的各个子步伐名或步伐地址。
实行过程:
加载器跳到_start,之后调用系统步伐_libc_start_main准备实行main函数。
在main函数中会依次实行以下函数:_printf,_exit,_sleep,_getchar。
exit。
调用的函数及其对应地址如表5.6所示:
函数名称
| 函数入口地址
| _start
| 0x4010f0
| _init
| 0x401000
| puts@plt
| 0x401090
| printf@plt
| 0x4010a0
| getchar@plt
| 0x4010b0
| atoi@plt
| 0x4010c0
| exit@plt
| 0x4010d0
| sleep@plt
| 0x4010e0
| .plt
| 0x401020
| _fini
| 0x4011c0
| (表5.6)
5.7 Hello的动态链接分析
分析hello步伐的动态链接项目,通过edb调试,分析在dl_init前后,这些项目标内容变化。
动态链接使我们在调用一个共享库定义的函数可以在运行时找到函数地址。由于定义共享库的共享模块可以在运行时加载到任何一个位置,编译器无法对其位置进行猜测,需要借助GNU编译系统的延迟捆绑技能办理,将过程地址的绑定推迟到第一次调用该过程中。
延迟捆绑技能通过GOT和PLT实现:PLT是一个数组,每个数组项有16字节代码构成。PLT[0]项很特殊,可以跳转到动态链接器,别的条目都负责一个库函数的调用。GOT也是一个数组,但每个数组项对应的是一个8字节的地址。其中GOT[0],GOT[1]包含动态链接器在剖析函数会用到的地址信息,GOT[2]记录了动态链接器在ld_linux.so中的函数入口点。
通过查询节头表发现GOT存储在0x404000处,在调用dl_init前,此位置的内存映像如图5.7.1所示:
(图5.7.1)
在调用dl_init之后,内存映像如图5.7.2所示:
(图5.7.2)
通过上图对比可以发现,在调用dl_init函数后,GOT表中的内容发生了改变。在运行dl_init前,GOT中存放的是PLT条目标第二条指令,而调用函数后,完成了动态链接,GOT中存放的是调用的函数的地址。动态链接正式通过在GOT条目与PLT条目之间不停跳转完成的。
5.8 本章小结
本章对可重定位的目标文件进行链接天生了可实行步伐main,通过检察ELF文件,清楚了可实行文件的格式。通过对比main.o文件和main文件的反汇编代码,分析出链接器在链接时完成了符号剖析和重定位。使用edb实行步伐,直观地展现了helloworld步伐在实行过程中调用了哪些函数,以及如何通过GOT和PLT协作完成动态链接。
第6章 hello进程管理
6.1 进程的概念与作用
6.1.1进程的概念
进程是指步伐实行过程中的一个实例,系统中每个步伐都运行在一个进程的上下文中。
6.1.2进程的作用
进程可以给步伐提供两个关键的抽象,逻辑控制流和私有的地址空间,似乎每个步伐都在独占CPU和内存系统,便于对内存的管理。
6.2 简述壳Shell-bash的作用与处理流程
(1)作用:
shell是命令行表明器,是一个交互型的应用级步伐,代表用户运行其他步伐。主要有以下几个作用:
1)信号处理
2)进程创建
3)前背景控制
4)作业调用
5)信号发送与管理
(2)处理流程:
用户在shell里面输入一个命令,如果此命令是shell内置的命令,则直接实行,如果不是,如./hello,会将其视作一个可实行步伐实行。首先会调用fork()函数,在当前父进程中创建一个子进程作为该步伐的实行实例,调用execve()函数为该步伐建立好与假造内存之间的映射。之后由操纵系统的页面调度机制将磁盘上的步伐调到内存中实行,步伐实行完毕会由父进程回收该子进程。如果命令行的最后一个参数是&,会在背景进行实行,shell会返回循环顶部,等候用户发出下一个命令,不会等候它实行完成;若没有&标志,则会在前台实行该步伐,直至实行完毕才能接收下一个命令。
在实行过程中,shell还会随时接收来自键盘的输入信号,并及时对这些信号进行应答处理。
6.3 Hello的fork进程创建过程
bash接收到一个非内置命令的命令行,如./main 2021111662 gaijiayi 1时,剖析指令时会发现它不在内置命令聚集中,会把它当做一个可实行目标文件来处理。首先调用fork()函数在bash父进程里创建一个子进程,这个子进程会共享与父进程相同但独立的假造地址空间,并继续父进程打开的全部文件,但是子进程的PID(进程组编号)与父进程差别。创建好了子进程,接下来就准备在子进程中实行main步伐。
6.4 Hello的execve过程
bash里面创建的子进程与父进程依然共享相同单独立地假造地址空间,因此需要调用execve()函数进行假造内存与步伐的重新映射。调用execve函数后首先会调用加载器,加载器会删除子进程现有的假造内存段,并组建一组新的代码、数据、堆、栈段。新的堆栈段会初始化为零。通过将假造地址空间中的页映射到可实行文件的页的大小的片,新的代码和数据段就会被初始化为可实行文件中的内容。之后加载器会跳到步伐的入口点实行_start函数,在_start函数中调用系统级函数_libc_start_main初始化实行环境,最后才开始实行main文件中的main函数。在实行过程中,步伐从磁盘调入内存由操纵系统的页面调度机制负责,而在加载过程中只是完成假造内存空间的分配。
此步伐中main函数有两个参数,argc和argv,argc给出了argv中指针不为空的数量,若在bash中输入指令./hello 2021111662 gaijiayi 3,此时argv数组中指针有四个不为空(argv[0]默认存储文件名不为空),会实行循环语句,运行结果如图6.4所示:
(图6.4)
6.5 Hello的进程实行
联合进程上下文信息、进程时间片,论述进程调度的过程,用户态与核心态转换等等。
进程上下文信息是指内核重新启动一个被抢占的进程所需的状态。进程上下文通常包含以下内容:通用目标寄存器、浮点寄存器、步伐计数器、用户栈、状态寄存器、内核栈和各种内核数据布局(页表、进程表、文件表)。
进程时间片是指一个进程实行它的控制流的一部门的每一时间段,当一个进程与其他进程轮流实行时叫做多任务,也叫时间分片。
进程调度是由操纵系统中的内核进行控制的。在调度过程中内核可以决定抢占当进步程并规复之前被抢占的进程,并使用上下文转换机制来控制转移这个重新被开始的进程。上下文转换过程中需要完成三件事:1)生存当进步程的上下文,2)规复此进程的上下文,3)将控制转移给这个新规复进程。
为了限制一个步伐可以实行的指令,处理器提供了用户模式和内核模式转化的机制来保证步伐访问合法的指令和假造内存空间。
控制寄存器中会设置一个模式位,若该模式位没有设置,则代表进程运行在用户态下,反之,运行在内核态下。用户态向内核态转换只能通过异常来实现。好比当一个进程A由于缺页而中断时,会由用户态转移到内核态,实行缺页中断处理子步伐将缺的页从磁盘调到内存。由于访存过程的时间过长,此时不会一直等候而是进行上下文转换切换到另一个进程B,在进程B运行的时间片到了之后会再次进行上下文转换将控制权转回到之前的进程A。由此可见由于异常导致的上下文转换中大概会存在用户模式和内核模式的切换。
6.6 hello的异常与信号处理
1、hello实行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
异常可以被分为两大类:同步异常和异步异常,这里的同步与异步是指异常是否由当前实行的指令发出(异步不是由当前指令导致的异常)。
是指由外部设备发出中断哀求信号导致的异常,当CPU完成当前处理的指令的实行阶段后会同一发出对中断异常的查询信号,当查询过程中若是发现终端引脚的电压升高,就会从数据总线上读取相应的异常服务号,并调用相应中断处理子步伐处理异常,竣事后会返回原指令的下一条指令处。
同步异常主要有三类分别为:陷阱、故障和终止。
1)陷阱:
是指故意的异常,是实行当前指令的结果,处理后会返回到下一条指令。陷阱为用户步伐和内核之间提供了一个像过程一样的接口,实现了系统调用,可由syscall指令实现。
2)故障:
不是故意的,有的可以被规复。常见的故障:缺页异常(可被规复并返回当前指令)、掩护故障(不可规复)、浮点异常。不可修复的故障会导致步伐的终止。
3)终止:
不是故意导致的,由不可规复的致命错误导致。常见的终止异常有:非法指令、奇偶校验错误、呆板检查等,会立即终止当出息序。
大概产生的信号:SIGINT(键盘中断信号)、SIGKILL(杀死步伐)
当内核向进程发出信号后,如果被进程接收并调用了信号处理步伐,会根据信号的名称调用相应的处理步伐进行处理,处理完毕后会下一条指令处继续正常实行。
2、步伐运行过程中可以按键盘,如不停乱按,包罗回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。
- 步伐实行过程中乱按键盘,并不会影响步伐运行,如图6.6_1所示
(图6.6_1)
- 步伐实行过程中按Ctrl+C会挂起当进步程,如图6.6_2所示。
(图6.6_2)
- 步伐实行过程中按Ctrl+Z后运行ps指令,Ctrl+Z会使当进步程被挂 起,ps可以显示当前系统中的全部进程的信息,如图6.6_3所示:
(图6.6_3)
- 步伐实行过程中按Ctrl+Z后运行jobs指令,终止后显示shell环境中已启动的状态,如图6.6_4所示。
(图6.6_4)
- 步伐实行过程中按Ctrl+Z后运行pstree指令,将进程关系以树状图形式展现出来,如图6.6_5所示。
(图6.6_5)
- 步伐实行过程中按Ctrl+Z后运行kill指令,杀死当进步程,如图6.6_6所示。
(图6.6_6)
6.7本章小结
本章介绍了进程的概念与作用,详细地介绍了进程的创建、步伐的加载实行过程,以及实行过程中大概出现的异常即相应的信号处理。完整展现了步伐从加载、实行、到最后终止、子进程被父进程回收的全过程。
第7章 hello的存储管理
7.1 hello的存储器地址空间
联合hello说明逻辑地址、线性地址、假造地址、物理地址的概念。
逻辑地址:包含在呆板语言中,用来指定一条指令或者一个操纵数的地址,由段地址加偏移地址构成。
线性地址:逻辑地址到物理地址之间变换的中心层。步伐产生的段偏移地址加上相应段的基地址就会构成一个线性地址。
物理地址:CPU可以直接访问的地址形式,对应某个存储单元。
假造地址:是操纵系统对存储器和I/O设备的抽象,步伐中天生的地址单元都是假造地址,需要经过MMU找到与物理地址的对应关系。
7.2 Intel逻辑地址到线性地址的变换-段式管理
逻辑地址的高位部门为段号,低位部门为段内偏移量。16位的系统中段寄存器中直接生存段的基地址,而32为系统段寄存器中存有段选择符。32位系统寻址时先到相应的段寄存器中找到相应的段选择符,根据段选择符中的索引和范例到全局形貌符表或者局部形貌符表中得到段形貌符。再将段形貌符送往形貌符cache中,通过映射关系找到相应段的基地址,基地址加上偏移量可以得到对应的32位线性地址。
7.3 Hello的线性地址到物理地址的变换-页式管理
页式管理是将物理空间与假造地址空间均划分为大小相同的页,由页表记录假造地址中的页与物理地址中的页的映射关系,页地址加上页内偏移地址就会得到与假造地址对应的物理地址。
7.4 TLB与四级页表支持下的VA到PA的变换
(1)TLB:
页表存储在主存中,每次访存时间效率不高,因此增加一个特殊的cache 用于存放那些经常被使用的页表项。TLB接纳全相联的映射方法,对于所给的 VA会先到TLB中进行比对,如果未命中才会去主存访问页表。
(2)四级页表:
由于一个步伐所需要的页表项大概会很多,会占用大量的存储空间,现采 取多级页表的方法来办理这个题目。在多级页表中高一级的页表存储着指向低 一级页表的指针,以此类推,直到最后一级页表才会指向物理地址。之前的单 级页表中大概会有大量的空页表项占用存储空间,而接纳多级页表布局只需将 上一级页表项中的指针置为空,无须生存这些空页表项,在肯定水平上淘汰了 存储空间的浪费。
(3)四级页表下的VA与PA的转换:
在VA与PA转化时,首先在TLB中比对。若未命中,会在页表基址寄存器中得到一级页表首地址,然后根据虚地址中高位段的VAPN1找到对应的一级页表项,根据这个一级页表项中生存的指针找到第二级页表首地址,同样的,用VAPN2找到在二级页表中的页表项得到三级页表首地址,以此类推,直至找到对应的物理地址。
7.5 三级Cache支持下的物理内存访问
得到物理地址后,传统的计算机会直接用这个物理地址去访问存储器得到所需的数据。由于访存速度过慢,会严重影响计算机效率,因此如今的计算机大多接纳三级cache来存储那些被经常访问的数据,以淘汰访存次数提升性能。通过之前的多级页表机制得到物理地址后,会先用这个物理地址到一级cache中查找,通常接纳组相连的映射方式。先根据PA中的组号找到对应的组,再比对tag位检察所需数据是否在这个组中。如果未命中,则到二级cache中继续比对,以此类推,如果三级cache中均未命中才会访存。对于cache中保存哪些数据会有相应算法进行约束,尽大概提高cache的命中率,切实淘汰访存次数,提升效率。
7.6 hello进程fork时的内存映射
Shell中实行hello步伐时会先调用fork()函数为步伐创建一个进程,并分配给它一个唯一的PID。为了给这个新进程创建假造内存,它创建了当进步程的mm_struct,地区布局和页表的原样副本。为了保证与父进程共享相同但独立地虚地址空间,接纳一种私有的写时复制策略:当fork在新进程中返回时,新进程如今的假造内存刚好和调用fork时存在的假造内存相同。当这两个进程中的任意一个后来进行写操纵的时间,写时复制机制就会创建新页面,并更改页表项中虚地址与实地址的对应关系,从而hello步伐得到了属于自己的代码段、数据段……
7.7 hello进程execve时的内存映射
Execve函数调用加载器完成以下操纵:
(1)删除已存在的用户地区。删除当进步程假造地址的用户部门中已存在的地区布局。
(2)映射私有地区。为新进程的代码、数据、bss和栈区创建新的地区布局。全部这些新的地区都是私有的、写时复制的。代码和数据区被映射为可实行文件中的.text和.data区。bss地区是哀求二进制0的,映射到匿名文件,其大小包含在可实行文件hello中。栈和堆地区也是哀求二进制0的,初始长度为0.
(3)映射共享地区。如果hello的可实行文件与共享对象链接,那么这些对象都是动态链接到这个步伐的,然后再映射到用户假造地址空间中的共享地区内。
(4)设置步伐计数器PC。在下一次调度这个进程的时间,它将从这个入口点开始实行。Linux将根据需要换入代码和数据页面。
7.8 缺页故障与缺页中断处理
由于在步伐加载时步伐并未从磁盘上加载到内存,而CPU只能对内存中的步伐进行实行。在步伐实行时在页表项中不会查到该虚地址对应的实地址,此时会产生缺页中断信号。在CPU查询到该信号时会进行上下文切换,转到内核态,调用缺页中断子步伐将需要的步伐块从磁盘加载到内存中,并在页表项中增添这个映射关系,然后再转回之前的进程继续实行步伐。
7.9动态存储分配管理
Printf会调用malloc,请简述动态内存管理的根本方法与策略。
动态内存分配器维护者一个进程的假造内存地区,称为堆。分配器将堆视为一组差别大小的块的聚集来维护。每个块都是一个连续的假造内存片,有的是已经分配的,还有一些是未分配的。已分配的块会被相应步伐使用,未分配的等候被分配使用。
动态内存分配器的两种风格:显示空闲链表和隐式空闲链表。
隐式空闲链表通过头部中的大小字段隐含地连接。分配器通过遍历堆中的全部块间接地遍历整个空闲块的聚集。放置已分配的块时要从链表头开始搜刮空闲块,但在分配过程中很容易产生碎块,这时就需要采取合适的归并策略将空闲块进行归并。
由于隐式空闲链表块分配与堆块的总是成线性关系,对于一样平常的分配器不得当,故还有一种显示空闲链表的方式。显示空闲链表有两种维护方式: 一种是后进先出的顺序维护。将新开释的块放在链表的开始处,分配器会最先检查近来使用过的块,使得开释一个块可以在常数时间内完成。另一种是按照地址顺序维护。链表中每个块的地址都小于它的后继地址。
空闲块的分配方式也有很多种方法:初次适配、下一次适配、最佳适配和分离适配,差别适配方案的吞吐率和内存利用率各有差别。
7.10本章小结
本章介绍了hello步伐的存储管理——假造内存。介绍了常见的两种假造内存管理方式:段式管理和页式管理。并详细说明确目前的计算机在页式管理布局下如何利用TLB、多级页表、三级cache完成从虚地址到实地址的转换并且得到相应存储单元的数据。最后,联合hello步伐分析了步伐在加载、实行过程中的存储管理。
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:文件
设备管理:unix io接口
一个Linux文件就是一个m字节的序列,全部的I/O设备(比方网络、磁盘和终端)都被模型化成文件,而全部的输入和输出都被当尴尬刁难应文件的读和写来实行。使用这种将设备优雅地映射为文件的方式,允许Linux内核引用出一个简单、低级的应用接口,称作Unix IO。
8.2 简述Unix IO接口及其函数
Unix IO令全部输入和输出都能以一种同一且一致的方式来实行:
(1)打开文件:一个应用步伐通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备。内核返回一个小的非负整数,叫做形貌符,它在后续对此文件的全部操纵中标识这个文件。内核记录有关这个打开文件的全部信息。应用步伐只需要记着这个形貌符。
(2)Linux shell创建的每个进程开始时都有三个打开的文件:标准输入(文件形貌符为0)、标准输出(文件形貌符为1)和标准错误(文件形貌符为2)。
(3)改变当前的文件位置:对于每个打开的文件,内核保持着一个文件位置k,初始为0.这个文件设置是从文件开头起始的字节偏移量。应用步伐能够通过实行seek操纵,显式地设置文件的当前位置为k。
(4)读写文件:一个读操纵就是从文件复制n>0个字节到内存,从当前文件位置k开始,然后将k增加到k+n。给定一个大小为m字节的文件,当k>=m时实行读操纵会触发一个EOF的条件,应用步伐能够检测到这个条件。在文件末了处并没有明确的“EOF符号”。类似地,写操纵就是从内存复制n>0个字节到一个文件,从当前文件位置k开始,然后更新k.
- 关闭文件:当应用完成了对文件的访问后,它就关照内核关闭这个文件。作为响应,内核开释文件打开时创建的数据布局,并将这个形貌符规复到可用的形貌符池中。无论一个进程由于何种原因终止时,内核都会关闭全部打开的文件并开释它们的内存资源。
包含的函数:
int open(const cahr *pathname,int flags,int perms)
Int close(unistd.h)
ssize_t read (int fd,void *buf,size_t count)
ssize_t write (int fd,void *buf,size_t count)
8.3 printf的实现分析
从vsprintf天生显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall等.
字符显示驱动子步伐:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照革新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
详细过程如下:
printf负责获取格式字符串和可变形参列表,并将其作为参数传递给函数vsprintf。vsprintf负责实现格式化,它接受确定输特别式的格式字符串fmt,并用格式字符串对个数变化的参数进行格式化,即构建出将要打印在屏幕上的字符串。之后vsprintf会调用write函数,在write函数的最后会调用syscall函数。syscall函数调用save函数,完成字符串的打印,直到碰到’\0’竣事符。最后通过字符显示驱动子步伐将字符的ASCII码存到显示vram(存储每一个点的RGB颜色信息)中,显示芯片按照革新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。在屏幕上就显示出了想要打印的字符串。
8.4 getchar的实现分析
Getchar中的read函数将缓冲区都读入buf数组中,返回缓冲区的长度。当buf数组为空,调用read函数,如果不空就返回buf中的第一个元素。
异步异常-键盘中断的处理:键盘中断处理子步伐。接受按键扫描码转成ascii码,生存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
本章介绍了Linux下IO设备的管理方法,了解了IO接口的作用以及接口中可以调用的一些函数,最后分析了printf和getchar函数的实现过程。
结论
用计算机系统的语言,逐条总结hello所履历的过程。
你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。
Hello 的一生经过了以下过程:
- 预处理天生预处理文件。
- 编译天生汇编文件。
- 汇编天生可重定位的二进制文件。
- 链接天生目标文件。
- 在shell中输入”./main 2021111662 gaijayi 3”会为其创建进程去实行。
- 在一个时间片中顺序实行步伐。
- 缺页时会利用页面调度机制将步伐从磁盘调到内存。
- 运行中可以发出各种信号控制。
- 运行竣事会清空内存中为其分配的堆栈。
- shell父进程回收已经终止的main子进程。
通过对以上过程的逐条实践,我对于步伐实行过程有了更深刻的了解,并对于计算机系统产生了浓重的爱好。通过本门课程的学习,我在编程时也会更加注意步伐的效率,尽量编写对编译器对cache有好的步伐,在实践中不停践行和应用学到的知识,提升自己的本领。
附件
列出全部的中心产物的文件名,并予以说明起作用。
文件名
| 作用
| main.c
| 源步伐
| main.i
| 预处理文件
| main.s
| 汇编文件
| main.o
| 可重定位的目标步伐
| main
| 可实行步伐
| main_o.elf
| 用readelf读取main.o文件的ELF格式信息
| main.asm
| main.o反汇编文件
| main.txt
| main文件反汇编
| main2.elf
| 用readelf读取main文件的ELF格式信息
|
参考文献
- Randal E.Bryant,David R.O’Hallaron. 深入明白计算机系统
- [转]printf的深入剖析:https://www.cnblogs.com/pianist/p/3315801.html
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |