程序人生-Hello‘s P2P

打印 上一主题 下一主题

主题 550|帖子 550|积分 1650



计算机系统


大作业



题     目  程序人生-Hello’s P2P 

专       业     未来技能学院人工智能模块                  

学     号       2022112635                

班     级         22WL027              

学       生         潘晗        

指 导 教 师           郑贵滨         







计算机科学与技能学院

2024年5月

摘  要

为了更好地总结并应用《计算机系统》这门课程的知识,本文以hello.c这一简单的C语言文件为研究对象,在Ubuntu操作系统下深入研究了它的编译、链接、加载、运行、终止和采取过程,即系统而深入地相识了hello.c的一生。同时,通过一个简单的hello.c,本文也将计算机系统的整个体系串联起来,融会贯通并学以致用。
关键词: 计算机系统;hello程序;程序生命周期;编译;链接;进程管理;存储管理;IO管理









目  录


第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 本章小结... - 7 -
第3章 编译... - 8 -
3.1 编译的概念与作用... - 8 -
3.2 在Ubuntu下编译的命令... - 8 -
3.3 Hello的编译效果解析... - 8 -
3.4 本章小结... - 15 -
第4章 汇编... - 16 -
4.1 汇编的概念与作用... - 16 -
4.2 在Ubuntu下汇编的命令... - 16 -
4.3 可重定位目标elf格式... - 16 -
4.4 Hello.o的效果解析... - 18 -
4.5 本章小结... - 19 -
第5章 链接... - 20 -
5.1 链接的概念与作用... - 20 -
5.2 在Ubuntu下链接的命令... - 20 -
5.3 可实行目标文件hello的格式... - 20 -
5.4 hello的虚拟所在空间... - 22 -
5.5 链接的重定位过程分析... - 23 -
5.6 hello的实行流程... - 24 -
5.7 Hello的动态链接分析... - 25 -
5.8 本章小结... - 25 -
第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的线性所在到物理所在的变换-页式管理... - 33 -
7.4 TLB与四级页表支持下的VA到PA的变换... - 34 -
7.5 三级Cache支持下的物理内存访问... - 34 -
7.6 hello进程fork时的内存映射... - 35 -
7.7 hello进程execve时的内存映射... - 36 -
7.8 缺页故障与缺页中断处理... - 36 -
7.9动态存储分配管理... - 37 -
7.10本章小结... - 37 -
第8章 hello的IO管理... - 39 -
8.1 Linux的IO设备管理方法... - 39 -
8.2 简述Unix IO接口及其函数... - 39 -
8.3 printf的实现分析... - 39 -
8.4 getchar的实现分析... - 41 -
8.5本章小结... - 42 -
结论... - 43 -
附件... - 44 -
参考文献... - 45 -



第1章 概述

1.1 Hello简介

1.1.1 Hello的P2P
P2P,Program to Process,即hello.c文件从可实行程序到正在运行的进程的整个过程。在Ubuntu系统下,从C语言文件到可实行目标文件,hello.c文件依次经历cpp(C预处理器,C Pre-Processor)预处理,ccl(C编译器,C Compiler)编译,as(汇编器,Assembler)汇编以及ld(链接器,Linker)链接四个过程。随后,向shell程序输入命令./hello后,shell程序调用fork()函数产生子进程。如许,hello就从Program变成了Process。

1.1.2 Hello的020
020,From 0 to 0,即从一开始系统中没有任何与hello.c相干的内容,到最后hello被采取的过程。在shell程序中输入./hello命令后,shell程序将调用fork()函数并通过调用execve()函数在进程的上下文中加载并运行hello。然后系统将进程映射到虚拟内存,并加载须要的物理内存。实行hello时,相干指令进入CPU流水线实行。实行结束后,父进程将采取这一进程,内核将清除这一进程的相干信息,这一进程就结束了。hello.c从0到0,挥一挥手,不带走一片云彩。
1.2 环境与工具

1.2.1 硬件环境
X64 CPU;3.2GHz;16G RAM;512GHD Disk

1.2.2 软件环境
Windows11 64位;Vmware 17;Ubuntu 22.04 LTS 64位

1.2.3 开发工具
Visual Studio 2022 64位;
gcc ;vim;gdb
1.3 中间效果

中间效果如表1所示。


表1 中间效果

文件名称

作用

hello.c

源程序文本文件

hello.i

预处理后的文本文件

hello.s

汇编后的二进制文件

hello.o

可重定位目标文件

hello

可实行目标文件

hello1.elf

由hello.o天生的.elf文件

hello2.elf

由hello天生的.elf文件

hello1.s

hello.o的反汇编文件

hello2.s

hello的反汇编文件


1.4 本章小结

本章从P2P和020两方面介绍了Hello,并列出了研究hello的程序人生的软硬件环境和开发工具,以及本文天生的中间效果文件和它们的作用。



第2章 预处理

2.1 预处理的概念与作用

2.1.1 预处理的概念
预处理是指在源代码编译之前先由预处理器(Preprocessor)举行的处理步骤。预处理器通常会对源代码举行一些替换、添加或者删除操作,以便为后续的编译阶段做准备。
常见的预处理指令包罗宏定义、条件编译、文件包罗等。通过宏定义,程序员可以在代码中定义一些符号或者简单的代码片断,方便后续代码的利用和维护。条件编译是通过指定条件来控制不同代码块的编译,从而实当代码的灵活性。文件包罗可以通过在代码中包罗其他文件的内容,实当代码的复用和模块化。

2.1.2 预处理的作用
① 处理头文件:形如#include<stdio.h>的命令告诉预处理器读取系统有文件stdio.h的内容,并把它直接插入程序文本中。
② 处理宏定义:对于#define指令,举行宏替换;对于代码中全部利用宏定义的地方利用符号表现的实际值替换定义的符号。
③ 处理条件编译:根据可能存在的#ifdef来确定程序须要实行的代码段。
④ 处理特殊符号:预编译程序可以识别一些特殊的符号,并在后续过程中举行符合的替换。
⑤ 删除解释:删除c语言源程序中的解释部分。

2.2在Ubuntu下预处理的命令

Ubuntu下预处理的命令为gcc -E hello.c -o hello.i,如图1所示。

图1 Ubuntu下预处理命令

2.3 Hello的预处理效果解析


如图2(a)所示,hello.c源程序文件共24行,但hello.i文件有超过3000行。main()函数前是大段的头文件stdio.h、unistd.h、stdlib.h以及这三个头文件通过#include插入的其他头文件的展开。如图2(b)所示,hello.i文件的末尾是hello.c源程序,但可以发现源程序文件中的解释已经消失。

图2(a) hello.i文件的开头部分


图2(b) hello.i文件的结尾部分

2.4 本章小结

本章介绍了预处理的概念和作用,并以hello.c的预处理为例分析了预处理器对源程序文件的处理,比方头文件展开、去除解释等。

第3章  编译

3.1 编译的概念与作用

3.1.1 编译的概念
       编译是ccl(C编译器,C Compiler)通过语法和词法分析,将合法的指令转换成等价的汇编指令的过程。通过编译,ccl将文本文件hello.i转换成二进制汇编语言文件hello.s。       

3.1.2 编译的作用
① 词义分析:扫描器将源代码中的字符序列分割为一系列c语言中的符合语法要求的字符单元。
② 语法分析:基于词法分析得到的字符单元天生语法分析树。
③ 语义分析:在语法分析完成之后由举行语义分析,以判断指令是否是合法的c语言指令。
④ 天生中间代码:中间代码可使编译程序的逻辑更加明确,以便提升下一步代码优化的效果。
⑤ 代码优化:根据用户指定的不同优化等级对代码举行安全的、等价的优化,以便提升代码在实行时的性能。
⑥ 天生代码:经过上面的全部过程后,ccl将会天生一个汇编语言代码文件,也就是我们最后得到的hello.s文件,这一文件中的源代码将以汇编语言的格式出现。
3.2 在Ubuntu下编译的命令

Ubuntu下编译的命令为gcc hello.i -S -o hello.s,如图3所示。

图3 Ubuntu下编译命令

3.3 Hello的编译效果解析

3.3.1 文件结构分析 

Hello的编译效果如图4所示。对hello.s文件的团体分析如表2所示。


图4 Hello的编译效果


表2 hello.s的团体结构

内容

寄义

.file

源文件

.text

代码段

.section .rodata

存放只读变量

.global

存放全局变量

.data

存放已初始化的全局变量和static变量

.align

对齐方式

.type

函数类型/对象类型

.size

巨细

.long .string

long类型/string类型


3.3.2 数据
① 常量
(1)数字常量
如图5所示,源代码中的数字常量储存在.text段,比方比较时利用的数字变量,以及循环时利用的循环比较变量等。

图5 hello.s中的数字常量

(2)字符串常量
       如图6所示,在printf等函数中利用的字符串常量储存在.rotate段中。

图6 hello.s中的字符串常量


② 变量
(1)局部变量
       局部变量储存在栈中的某一个位置或直接储存在寄存器中。源代码中的局部变量共有:循环变量i,argc和argv。如图7所示,i储存在栈中所在为-4(%rbp)的位置。如图8所示,argc表现程序运行时输入变量的个数,储存在栈中所在为-20(%rbp)的位置。如图9所示,argv是一个生存输入变量的数组,储存在栈中。

图7 hello.s中的局部变量i


图8 hello.s中的局部变量argc


图9 hello.s中的局部变量argv


3.3.3 赋值
       源代码中的循环对局部变量i举行了赋值操作。如图10所示,对于局部变量i,每次循环结束的时候都对它举行+1操作。

图10 hello.s中对局部变量i的赋值操作


3.3.4 类型转换
       在源程序中,对argv数组举行了从字符串到整数的类型转换。如图11所示,在hello.s中,程序跳转到PLT(Procedure Linkage Table)中生存atoi函数入口所在的位置,并开始实行atoi函数。

图11 hello.s中的类型转换


3.3.5 算术操作
       因为局部变量i是循环变量,所以在每一次循环中都要修改这个值。hello.s中的汇编语句如图12所示。

图12 hello.s中的算术操作


3.3.6 关系操作
       源程序中的关系操作有:对于argc是否等于5的判断和对于i是否小于10的判断。如图13所示,当argc等于5时举行条件跳转。如图14所示,当i大于等于9时举行条件跳转。

图13 hello.s中对于argc的关系操作



图14 hello.s中对于i的关系操作


3.3.6 数组/指针/结构体操作
① 数组操作
       源程序中只有对于argv数组的操作。如图15所示,argv储存的三个值都存放在栈中:argv[1]的储存所在是-8(%rbp),argv[2]的储存所在是-16(%rbp),argv[3]的储存所在是-24(%rbp)。

图15 hello.s中的数组操作


3.3.7 函数调用
       源程序中的调用的函数有:main、printf、exit、sleep和getchar。在64位系统中,第1~6个参数依次储存在%rdi、%rsi、%rdx、%rcx、%r8、%r9这六个寄存器中,其余的参数生存在栈中。
① main函数
       如图16所示,调用main函数时传入参数argc和argv,其中argv储存在栈中,argc储存在%rdi中。源程序通过return 0从main函数返回,而在hello.s中则是调用了exit(1)。

图16 hello.s中的main函数

② printf函数
       如图17(a)和图17(b)所示,第一次调用printf函数时只传入了字符串参数首所在,并在满足if条件时调用。而第二次调用时for传入了 argv[1]、argc[2]和argc[3]的所在,并在for循环条件满足时调用。

图17(a) hello.s中第一次调用printf函数



图17(b) hello.s中第二次调用printf函数


③ exit函数
       如图18所示,当满足if条件时传入参数1并调用exit函数。

图18 hello.s中的exit函数

④ sleep函数
       如图19所示,sleep函数在for循环条件满足时被调用。

图19 hello.s中的sleep函数

⑤ getchar函数
       如图20所示,当循环结束时调用getchar函数。

图20 hello.s中的getchar函数


3.4 本章小结

本章介绍了编译的概念和作用,并结合hello.s中出现的C语言的数据与操作分析了ccl对C语言的各个数据类型及各类操作的处理过程。

第4章 汇编

4.1 汇编的概念与作用

4.1.1 汇编的概念
       汇编是指as(汇编器,Assembler)将以.s结尾的文本文件转换成呆板指令并天生以.o结尾的可重定位目标文件的过程。

4.1.2 汇编的作用
       汇编将汇编语言根据特定的转换规则转换为呆板指令,便于后续链接器对可重定位目标文件举行重定位操作。
4.2 在Ubuntu下汇编的命令

       Ubuntu下汇编的命令为gcc -c hello.c -o hello.o,如图21所示。

图21 Ubuntu下的汇编命令

4.3 可重定位目标elf格式

       因为汇编器天生的hello.o是二进制文件,无法利用vim检察,所以在Ubuntu的终端输入readelf -a hello.o > hello1.elf命令,在hello1.elf中检察elf头的相干信息。
4.3.1 ELF头
       如图22所示,ELF头以一个16字节的序列开始。该序列描述了天生该文件系统下的字的巨细以及一些其他信息。剩余部分则包罗了便于链接器举行语法分析和表明目标文件的相干信息,如ELF头的巨细、目标文件的类型、呆板类型、节头部表的文件偏移,节头部表中条目标巨细和数量等。

图22 可重定位目标文件的ELF头


4.3.2 节头
       如图23所示,节头描述了hello.o文件中每个节的名称、所在、巨细、偏移量等信息。

图23 可重定位目标文件的节头


4.3.3 重定位节
       如图24所示,重定位节包罗了源代码中利用的外部变量等信息。这些信息便于链接器在链接时对这些变量或符号的位置举行修改。在Hello中,须要重定位的信息有exit、printf、sleep、getchar等函数的位置等。源程序的重定位类型仅有R_X86_64_PC32(PC相对寻址)和R_X86_64_PLT32(利用PLT表寻址)两种,而未出现R_X86_64_32(绝对寻址)。

图24 可重定位目标文件的重定位节


4.3.4 符号表
       如图25所示,符号表包罗了在源程序中定义和引用的函数和全局变量的相干信息。

图25 可重定位目标文件的符号表

4.4 Hello.o的效果解析

       在Ubuntu终端中输入objdump -d -r hello.o > hello1.s命令,在文本文件hello1.s中检察hello.o的反汇编效果。
       如图26所示,与hello.s对照,在hello.o的反汇编效果中,hello.o的每条呆板指令都对应着一条汇编指令。而不同之处如下:
(1)反汇编效果中带有重定位信息,如R_X86_64_PLT32等。
(2)在调用函数时,反汇编效果利用相对偏移量取代hello.s中的函数名。
(3)对于数据的表现,反汇编效果利用十六进制,而hello.s利用十进制。
(4)对于分支转移,反汇编效果利用跳转的目标所在取代hello.s中的.L2等代码块的名字。


图26 hello.o的反汇编效果

4.5 本章小结

本章介绍了汇编的概念和作用,通过可重定位目标文件的elf格式检察了汇编效果并通过反汇编的效果与hello.s文件的对比从侧面相识了汇编的作用。

第5章 链接

5.1 链接的概念与作用

5.1.1 链接的概念
       链接是指ld(链接器,Linker)将各种不同文件的源代码和数据块等文件收集并组合成一个单一的可实行目标文件的过程。实行链接操作的时机有:编译时、加载时和运行时。

5.1.2 链接的作用
       ld将已完成编译的若干可重定位目标文件合并成为一个可实行目标文件,使得各个文件的独立编译成为可能。当改变各个文件模块中的一个时,只需简单重新编译它并重新链接即可,不必重新编译其他文件。
5.2 在Ubuntu下链接的命令

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,如图27所示。

图27 Ubuntu下链接的命令

5.3 可实行目标文件hello的格式

在Ubuntu终端中输入readelf -a hello > hello2.elf命令,在hello2.elf文件中检察由链接器天生的可实行目标文件hello。
5.3.1 ELF头
       如图28所示,ELF头列出可实行目标文件各段的根本信息,如各段的起始所在、巨细等。

图28 可实行目标文件的ELF头


5.3.2 节头
       如图29所示,节头包罗了可实行目标文件中各节的巨细、偏移量等信息。链接器链接时将各个文件的雷同段合成一段,并且根据该合成段的巨细以及偏移量重定位各个符号的所在。

图29 可实行目标文件的节头(部分)


5.3.3 重定位节
       如图30所示,可实行目标文件的重定位节与可重定位目标文件的重定位节不同。前者的每个重定位条目标符号名称变为具体的函数名且加数均为0,阐明链接过程中确实实现了重定位。

图30 可实行目标文件的重定位节


5.3.4 符号表
       如图31所示,对比可重定位目标文件中的符号表,可实行目标文件中的符号表增加了来自.dynsym的符号,同时原本.symtab中的符号数量也有所增加。

图31 可实行目标文件的符号表(部分)

5.4 hello的虚拟所在空间

       如图32所示,利用edb加载hello,并在Data Dump窗口中检察该进程的虚拟空间的各段信息。源程序是从所在0x401000开始,在所在0x402000处结束。通过起始处的ELF标识,可获取从可实行文件时加载的信息。

图32 利用edb检察虚拟空间的信息

5.5 链接的重定位过程分析

在Ubuntu的终端中输入objdump -d -r hello > hello2.s命令,在hello2.s文件中检察hello的反汇编效果。hello的反汇编效果与hello.o的反汇编效果的不同之处有:
① 如图33所示,链接器参加了puts、exit等库函数以及它们的虚拟所在。

图33 hello的反汇编效果1


② 如图34所示,链接器参加了一些在hello.o中须要重定位的符号或变量的虚拟所在。

图34 hello的反汇编效果2

③ 如图35所示,hello的反汇编效果中增加了.init节和.plt节,且无重定位节。

图35 hello的反汇编效果3


通过5.5节中的分析可知,链接分为符号解析和重定位两个过程。
① 符号解析:在可重定位目标文件中定义和引用符号,而连接过程中的符号解析则将每个符号引用和一个符号定义关联起来。
② 重定位:链接器通过把每个符号定义与一个内存位置关联起来,从而重定位可重定位目标文件中的数据节和代码节等,然后修改这些符号的引用,使得它们指向这个内存位置。
5.6 hello的实行流程

利用gdb/edb实行hello,阐明从加载hello到_start,到call main,以及程序终止的全部过程(主要函数)。请列出其调用与跳转的各个子程序名或程序所在。
如图36所示,由edb实行hello的效果可知,从加载hello到_start,到call main,以及程序终止的全部过程(主要函数)调用与跳转的各个子程序名或程序所在如下表所示。

表3 hello的实行流程

程序名称

程序所在

<_init>

0x401000

<.plt>

0x401020

<puts@plt>

0x401090

<printf@plt>

0x4010a0

<getchar@plt>

0x4010b0

<atoi@plt>

0x4010c0

<exit@plt>

0x4010d0

<sleep@plt>

0x4010e0

<_start>

0x4010f0

<_dl_relocate_static_pie>

0x401120

<main>

0x401125

<_fini>

0x4011c8


5.7 Hello的动态链接分析

在elf文件中找到.got的所在,并利用edb在dl_init处设置断点,分析在dl_init前后该所在的变革环境,如图36(a)和(b)所示。
当程序调用一个由共享库定义的函数时,由于编译器无法获得函数的所在,所以编译系统将过程所在的绑定推迟到第一次调用该过程时举行,即通过GOT和过程链接表PLT的协作来解析函数的所在。在加载时,动态链接器会重定位GOT中的每个条目,使它包罗精确的绝对所在,而PLT中的每个函数负责调用不同函数。

图36(a) dl_init前



图36(b) dl_init后


5.8 本章小结

本章介绍了链接的概念和作用,通过gdb调试、反汇编等方式相识了链接器的作用以及不同的链接方法。

第6章 hello进程管理

6.1 进程的概念与作用

6.1.1 进程的概念
       进程是一个实行中程序的实例。系统中的每个程序都运行在某个进程的由程序精确运行所需的状态构成的上下文中。

6.1.2 进程的作用
进程给应用程序提供关键抽象:
① 一个独立的逻辑控制流:提供一个程序独占处理器的假象
② 一个私有的所在空间:提供一个程序独占地利用内存系统的假象
6.2 简述壳Shell-bash的作用与处理流程

6.2.1 Shell-bash的作用
       Shell-bash实行一系列的读取或求值操作后终止。读取操作是读取来自用户的一个命令行;求值操作是解析命令行,并根据解析效果运行程序。

6.2.2 Shell-bash的处理流程
① 对用户输入的命令举行解析,判断命令是否为内置命令。
② 如果为内置命令,调用内置命令处理函数;如果不是内置命令,就创建一个子进程,将程序在该子进程的上下文中运行。
③ 判断为前台程序还是背景程序,如果是前台程序则直接实行并等待实行结束,如果是背景程序则将其放入背景并返回。
④ 同时对键盘输入的信号和其他信号有特定的处理。
6.3 Hello的fork进程创建过程

① 父进程通过调用fork函数创建一个新的运行的子进程:新创建的子进程险些但不完全与父进程雷同。子进程得到与父进程用户级虚拟所在空间雷同的一份副本,包罗代码和数据段、堆、共享库以及用户栈。这就意味着当父进程fork时,子进程可以读取父进程中打开的任何文件。
② 父进程和子进程之间的最大区别是他们有不同的PID:在父进程中,fork返回子进程的PID。在子进程中,fork返回0,返回值提供一个明确的方法来分辨程序是父进程还是在子进程中实行。
6.4 Hello的execve过程

execve函数:int execve(const char *filename, const char *argv[], const char envp[]),其作用是在当进步程的上下文中加载并运行一个程序。
execve函数加载并运行可实行文件filename,带参数列表argv和环境变量列表envp且execve调用一次并从不返回。
6.5 Hello的进程实行

结合进程上下文信息、进程时间片,阐述进程调理的过程,用户态与核心态转换等等。
上下文信息:进程实行过程中生存的一组状态信息,用于描述进程当前的实行状态和环境,包罗寄存器状态、进程标识符等。
进程时间片:一个固定的时间单元,用于限定每个进程在CPU上实行的时间长度。在操作系统中,为了实现公平的资源分配和高效的任务调理,通常采用时间片轮转调理算法。每个进程被分配一个固定的时间片,当进程实行完当前时间片后,操作系统会将CPU资源切换给下一个进程,以便实现多任务切换。
用户态:操作系统中进程实行的一种模式,与内核态相对。在用户态下,进程只能访问用户空间的内存和资源,不能直接访问内核空间的资源和实行内核级别的操作。
核心态:操作系统中进程实行的一种模式,与用户态相对。在核心态下,进程可以直接访问内核空间的资源和实行内核级别的操作。
进程调理的过程:如图37所示,纵然系统中通常有许多其他程序在运行,进程也可以向每个程序提供一种假象,好像它在独占地利用处理器。如果利用调试器单步运行程序,会看到一系列的程序计数器(PC)的值,这些值唯一对应于包罗在运行时动态链接到程序的共享对象中的指令。这个PC的序列叫做逻辑控制流。进程轮流占用处理器,每个进程实行它的流的一部分,然后轮到其他进程。
在进程实行的某些时刻,内核可以决定抢占当进步程,并重新开始一个先前被抢占了的进程。这种决策是调理,它是由内核中调理器的代码处理的。当内核选择一个新的进程运行时,内核调理了这个进程。在内核调理了一个新的进程运行了之后,它就抢占了当进步程,并利用上下文切换机制来将控制转移到新的进程。

图37 进程调理示意图

6.6 hello的异常与信号处理

6.6.1 hello实行过程中的异常类型
       hello实行过程中的异常类型如表4所示。

表4 hello实行过程中的异常类型

异常类型

缘故原由

返回操作

中断

来自I/O设备的信号

返回到出现异常的下一条呆板指令

陷阱

有意的异常

返回到出现异常的下一条呆板指令

故障

埋伏可恢复的错误

可能返回到引起异常的当前呆板指令

终止

不可恢复的错误

不会返回


6.6.2 hello的正常实行效果
       如图38所示,hello正常运行时会按照输入打印10次个人信息且在输入恣意一个字符后结束并被采取。

图38 hello正常运行的效果


6.6.3 不停乱按
       如图39所示,屏幕输入将被缓存到缓冲区。乱按输入内容会被以为是命令,不影响当进步程后续的实行。

图39 不停乱按的效果


6.6.4 按下Ctrl-z
       如图40所示,当hello在运行时按下Ctrl-z,系统会产生中断异常信号。hello的父进程会接收到信号SIGSTP,系统实行相应的信号处理程序。效果是程序被挂起,并打印相干挂起信息。

图40 按下Ctrl-z的效果


① 按下Ctrl-z后运行ps命令:如图41所示,在按下Ctrl-Z后运行ps命令,终端打印出了各进程的pid,其中包罗之前挂起的进程hello。

图41 在按下Ctrl-z后实行ps命令的效果


② 按下Ctrl-z后运行jobs命令:如图42所示,在按下Ctrl-z后实行jobs命令,终端打印出被挂起进程组的jid,其中包罗之前被挂起的hello。它以被挂起的标志Stopped被标识。

图42 在按下Ctrl-z后实行jobs命令的效果


③ 按下Ctrl-z后运行pstree命令:如图43所示,按下Ctrl-z后运行pstree命令,终端打印系统中全部进程的树状结构,表现进程的父子关系和层级。

图43 按下Ctrl-z后运行pstree命令的效果(部分)


④ 按下Ctrl-z后运行fg命令:如图44所示,按下Ctrl-z后运行fg命令,系统将之前挂起的hello重新调到前台来实行,打印出剩余部分,然后输入恣意字符,程序运行结束,进程被采取。

图44 按下Ctrl-z后运行fg命令的效果


⑤ 按下Ctrl-z后运行kill命令:如图45所示,重新运行hello,可知它的pid为4668。按下Ctrl-z后输入kill -9 4668命令,则hello进程被杀死。

图45 按下Ctrl-z后运行kill命令的效果


6.6.5 按下Ctrl-c
       如图46所示,按下Ctrl-c后,进程接收到SIGINT信号,被彻底杀死。

图46 按下Ctrl-c后的效果


6.7本章小结

本章介绍了进程的概念和作用,简述了Shell-bash的作用和处理过程、Hello fork和execve的过程,并结合hello的实际运行过程分析了不同的异常与信号处理过程。

第7章 hello的存储管理

7.1 hello的存储器所在空间

7.1.1 逻辑所在
       逻辑所在是计算机内存中的虚拟所在,用于程序的编写和实行。
7.1.2 线性所在
       线性所在是在操作系统管理下的虚拟所在,也称为虚拟所在。它在逻辑所在和物理所在之间的一种转换形式。
7.1.3 虚拟所在
       虚拟所在是指在计算机系统中由操作系统分配给进程或线程的所在空间。它是程序员在编程时利用的所在,与实际的物理所在是分开的,须要通过内存管理单元(MMU)或转换表等机制举行所在转换,以映射到物理内存中的实际存储位置。如图35所示,在反汇编文件中表现的均为虚拟所在。
7.1.4 物理所在
       物理所在是计算机存储系统中真实的内存所在,它表现RAM(随机存取存储器)中的实际存储位置。
7.2 Intel逻辑所在到线性所在的变换-段式管理

在Intel处理器架构把一个程序分成若干个段举行存储,每个段都是一个逻辑实体,即段式管理。段式管理是通过段表举行的,包罗段号、段起点等。程序通太过段划分为代码段、数据段等。
逻辑所在是程序源码编译后所形成的跟实际内存没有直接联系的所在,即在不同的呆板上利用雷同的编译器来编译同一个源程序则其逻辑所在是雷同的,但在不同呆板上天生的线性所在是不雷同的。
一个逻辑所在是两部分构成的,包罗段标识符和段内偏移量。段标识符是由一个16位长的字段构成的,称为段选择符。前13位是一个索引号,后3位为一些硬件细节。索引号便是“段描述符”的索引,段描述符具体所在描述了一个段,很多个段描述符就构成了段描述符表。通过段标识符的前13位直接在段描述符表中找到一个具体的段描述符。
如图47所示,整个系统只有一个全局描述符表(GDT),它包罗操作系统利用的代码段、数据段、堆栈段的描述符以及各任务、各程序的LDT(局部描述符表)段。而每个任务或程序有一个独立的LDT,它包罗该任务或程序私有的代码段、数据段、堆栈段的描述符以及该任务或程序利用的门描述符。

图47 段式管理示意图

7.3 Hello的线性所在到物理所在的变换-页式管理

页式管理是一种内存空间存储管理的技能,它将各进程的虚拟空间划分成若干个长度相等的页,把内存空间按页的巨细划分成片或者页面,然后把页式虚拟所在与内存所在创建一一对应页表,并用相应的硬件所在变换机构,来解决离散所在变换题目。页式管理采用请求调页或预调页技能实现了内外存存储器的统一管理。
如图48所示,虚拟所在由虚拟页号VPN和虚拟页偏移VPO构成。起首MMU从虚拟所在中抽取出VPN,并且查抄TLB,判断它是否因为前面某个内存引用缓存了PTE的一个副本。TLB从VPN中抽取出TLB索引和TLB标志,查找对应组中是否有匹配的条目。若命中,则将缓存的PPN返回给MMU。否则,MMU需从页表中的PTE中取出PPN,若得到的PTE无效或标志不匹配,就产生缺页,内核需调入所需页面,重新运行加载指令,如有用,则取出PPN。最后将线性所在中的VPO与PPN连接起来就得到了对应的物理所在。

图48 页式管理示意图

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

如图49所示,在四级页表中,36位的VPN被划分成四个9位的片断,每个片断被用作到一个页表的偏移量。CR3寄存器中存放了一级页表的物理所在。VPN1提供了相对于一级页表起始所在的偏移量,这些页表条目包罗二级页表的基所在。VPN2提供了相对于二级页表起始所在的偏移量,三级页表和四级页表如此类推。

图49 TLB与四级页表支持下的VA到PA的转换示意图

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

通用的高速缓存的结构如图50所示,其中每个所在位被划分成了t个标志位,s个组索引位和b个块偏移位。
根据所在位信息,起首举行组定位。然后举行行定位,即查抄该组中是否有行匹配且该行的有用位为1。如果创建,则缓存命中,返回定位到的数据。否则,缓存不命中,须要从存储器层次结构的下一层中取出被请求的块,然后将新的块存储在组索引位指示组中的一个高速缓存行中。其中具体替换哪一行取决于替换策略。

图50 高速缓存的结构示意图

7.6 hello进程fork时的内存映射

hello进程fork时起首为新进程创建虚拟内存,包罗创建当进步程的mm_struct、vm_area_struct和页表的原样副本,且两个进程中的每个页面都标志为只读,两个进程中的每个区域结构都被标志为私有的写时复制。
如图51所示,在新进程中返回时,新进程拥有与父进程雷同的虚拟内存。其写操作通过写时复制机制创建新页面。

图51 私有的写时复制机制

7.7 hello进程execve时的内存映射

如图52所示,hello进程execve时起首删除已存在的用户区域,然后创建新的区域结构。后者包罗将代码和初始化数据映射到.text和.data区,将.bss和栈映射到匿名文件。随后设置PC,使其指向代码区域的入口。Ubuntu系统根据须要换入代码和数据页面。

图52 execve时的内存映射

7.8 缺页故障与缺页中断处理

如图53所示,当MMU在试图翻译某个虚拟所在VA时触发了一个缺页故障,那么这个异常会导致控制转移到内核的缺页处理程序:
① 判断这个虚拟所在VA是否在某个区域结构定义的区域内:如果这个VA不合法,那么缺页处理程序就会触发一个段错误,进而终止这个进程。
② 判断该进程是否有读、写或者实行这个区域内页面的权限:如果试图举行的访问不合法,那么缺页处理程序会触发一个保护异常并终止进程。
③ 如果缺页是由于对合法的虚拟所在举行合法的操作造成的,那么内核会选择一个牺牲页面。如果这个牺牲页面被修改过,那么就将它交换出去,换入新的页面并更新页表。当缺页处理程序返回时,CPU重新启动引起缺页的指令,这条指令将再次发送VA到MMU。这次就不会出现缺页中断异常了。

图53 缺页故障与缺页中断处理


7.9动态存储分配管理

动态存储分配管理时一种内存管理方法,即对于内存空间的分配、采取等操作在进程实行过程中举行,以便更好地适应系统的动态需求,进步内存利用率。
分配器分为两种:
① 表现分配器:要求应用表现地开释任何已分配的块。
② 隐式分配器:要求分配器检测一个已分配的块何时不再被程序所利用,那么就开释这个块。
动态存储分配管理的根本方法与策略如下:
① 带边界标签的隐式空闲链表分配器管理:带边界标志的隐式空闲链表的每个块由一个字的头部、有用载荷、可能的额外填充以及一个字的尾部构成。当一个应用请求一个k字节的块时,分配器搜刮空闲链表,查找一个符合巨细的空闲块来放置这个请求块。
② 表现空间链表管理:显式空闲链表是将空闲块构造为某种形式的显式数据结构。因为根据定义,程序不须要一个空闲块的主体,所以实现这个数据结构的指针可以存放在这些空闲块的主体里面。
7.10本章小结

本章介绍了所在空间的分类、段式管理与页式管理、VA到PA的转换、物理内存访问机制、fork与execve时的内存映射、缺页故障及其处理以及动态存储分配管理等内容。

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

8.1.1 设备的模型化
文件,即全部的I/O设备都被视为文件,而全部的输入输出都被视为对相应文件的读、写操作。

8.1.2 设备管理
unix io接口,这种将设备映射为文件的方式,答应Linux内核引出一个简单、低级的应用接口。
8.2 简述Unix IO接口及其函数

8.2.1 Unix I/O接口
① 打开文件:一个应用程序通过要求内核打开相应的文件,表现它想要访问一个I/O设备。内核返回一个小的非负整数(描述符),并在后续对此文件的全部操作中标识这个文件。
② 读写文件:读操作即从文件复制n个字节到内存当前文件位置k处。若给定一个巨细为m字节的文件,那么当k>=m时的读操作会触发一个EOF。
③ 关闭文件:内核开释文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符所在中。

8.2.2 Unix I/O函数
① open函数:进程通过调用open函数来打开一个已存在的文件或者创建一个新文件。
② close函数:进程通过调用close函数来关闭一个打开的文件。
8.3 printf的实现分析

printf函数的函数体如图54所示。其中,va_list是一个字符指针,(char*)(&fmt + 4)表现fmt后的第一个参数的所在。如图55所示,vsprintf函数返回值是要打印出来的字符串的长度,其作用是格式化,即产生格式化的输出并生存在buf中。最后的write函数即为写操作,把buf中的i个元素的值输出到终端。

图54 printf函数的函数体



图55 vsprintf函数的函数体


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


图56 追踪write函数



图57 sys_call函数的实现

8.4 getchar的实现分析

getchar函数的函数体如图58所示。在getchar函数中声明了几个静态变量:缓冲区buf,缓冲区的最大长度BUFSIZ,指向缓冲区的首所在的bb指针。getchar调用read函数,通过系统调用读取按键ascii码,直到担当到回车键才返回。read将缓冲区读入到buf中,并将长度送给n,再重新令bb指针指向buf。最后返回buf中的第一个字符。如果长度n<0,则出现EOF报错。其中,对于异步异常-键盘中断的处理方式是键盘中断处理子程序,它担当按键扫描码转成ascii码,并生存到系统的键盘缓冲区。

图58 getchar函数的函数体

8.5本章小结

本章介绍了Linux的I/O设备管理方法、Unix I/O接口及函数,并具体分析了printf函数和getchar函数的实现方式。
结论

hello的一生:
(1)预处理:
       cpp将hello.c中的全部头文件内容直接插入到程序中,并去掉解释等内容,天生hello.i文件。
(2)编译:
       ccl通过语法分析和词法分析将合法的C语言指令转换成等效的汇编指令,天生hello.s文件。
(3)汇编:
       as将汇编指令转换成等价的呆板指令,并将这些指令转换成可重定位目标文件的格式,便于后续的重定位,天生hello.o文件。
(4)链接:
       ld将hello的代码和动态链接库等文件整理成一个文件,举行符号解析和重定位后天生可实行目标文件hello。
(5)加载:
       在Ubuntu终端中将./hello命令输入Shell-bash程序中,Shell-bash程序调用fork函数为hello创建新进程,并调用execve函数将代码段和数据段等映射到虚拟内存。程序实行时在通过缺页中断处理和私有写时复制等机制换入代码页或数据页。
(6)实行指令:
       处理器为hello进程分配调理时间片。在该调理时间片内,hello进程享有处理器的全部资源,并顺序地实行其逻辑控制流。
(7)访问内存:
       MMU将虚拟所在翻译成物理所在,并通过三级高速缓存访问内存或磁盘上的数据。
(8)信号处理:
       当hello进程被切换回来时,它会利用默认方式或自定义的信号处理函数对接收到的信号举行处理。
(9)终止和采取:
       Shell-bash等待并采取hello进程,采取后内核会删除hello进程相干的全部信息。
      
感悟:
通过《计算机系统》这门课程和hello的一生,我从编译的四个阶段、程序的加载和运行、内存管理、信号处理等方面更加深入地相识了计算机系统的计划与实现头脑,这将十分有利于我日后在深入明白计算机系统的根本上写出更精确、更快的程序。

附件

文件名称

作用

hello.c

源程序文本文件

hello.i

预处理后的文本文件

hello.s

汇编后的二进制文件

hello.o

可重定位目标文件

hello

可实行目标文件

hello1.elf

由hello.o天生的.elf文件

hello2.elf

由hello天生的.elf文件

hello1.s

hello.o的反汇编文件

hello2.s

hello的反汇编文件



参考文献

[1] Randal E.Bryant David R.O’Hallaron. 深入明白计算机系统(第三版). 机械工业出版社,2016.
[2] https://www.cnblogs.com/pianist/p/3315801.html


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

滴水恩情

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表