csapp大作业程序人生

打印 上一主题 下一主题

主题 837|帖子 837|积分 2511

 




盘算机体系


大作业








盘算机科学与技术学院

20245

摘  要

本文围绕hello.c的完备实现,深入分析了hello.c从源代码文件颠末预处理、编译、汇编、链接再到实行阶段,以及程序终止和接纳的过程,全方面的了解了hello从编写完代码到历程竣事的生命周期全过程。了解了hello.c的“一生”。
关键词:盘算机体系;预处理;编译;汇编;链接;                          

(择要0分,缺失-1分,根据内容精彩称都酌情加分0-1分








目  录


第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的整个过程。

Hello的P2P(From Program to Process):当我们编写好一个原始的c语言代码,并将其储存成hello.c文件,hello.c先颠末预处理得到hello.i文件,编译器对hello.i举行汇编,得到汇编文件 hello.s,hello.s被汇编器翻译成二进制机械指令并打包成可重定位目标程序生存在hello.o中,由于hello.c中调用了外部库中的printf函数,所以需要使用链接器ld将printf.o和hello.o举行链接生成可实行程序文件hello,它可被加载到内存中由体系实行。在Bash(shell)中,通过输入下令./hello,操作体系(OS)的历程管理通过调用fork函数,为其创建历程 (process),从而实现From Program to Process。
Hello的O2O(From Zero-0 to Zero-0):程序从无到有,从最初的零开始,颠末编写、编译、链接等步调,最终生成一个可以实行的程序。Shell 子历程调用 execve 函数,将程序举行虚拟内存映射、物理内存加载,加载完成后,控制权转移到 main 函数,程序开始实行,在实行过程中,程序调用各种体系函数,如 printf,通过体系调用与硬件交互,实现屏幕输出等功能。程序运行竣事后,shell 父历程接纳子历程,释放子历程所占用的虚拟内存空间和相干数据结构,历程的资源全部被释放,体系状态回到历程未实行前的状态,仿佛程序从未运行过,实现归零。

1.2 环境与工具

1.2.1 硬件环境
X64 CPU;2.3GHz;16G RAM;512GHD Disk
1.2.2 软件环境
Windows 11 64位;VMware 17 ;Ubuntu 16.04 LTS 64位
1.2.3 开辟工具
Visual Studio 2022 64位;CodeBlocks 64位;vi/vim/gedit+gcc

1.3 中间结果

列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
文件名

作用

hello.c

源C语言程序

hello.i

预处理生成的文本文件

hello.s

编译生成的汇编文件

hello.o

汇编生成的可重定位目标文件(二进制)

hello

经链接生成的可实行目标文件(二进制)

hello1.elf

hello.o经readelf得到的.elf文件

hello1.asm

hello.o反汇编得到的文本文件

hello2.elf

hello经readelf得到的.elf文件

hello2.asm

hello反汇编得到的文本文件



1.4 本章小结

本章主要介绍了hello程序的P2P(From Program to Process) 及O2O(From Zero-0 to Zero-0)的过程。同时介绍了此次大作业完成的硬件环境、软件环境以及开辟工具的相干信息。并列出为完成本次大作业,生成的中间结果文件的名字以及文件的作用。


(第1章0.5分)



第2章 预处理

2.1 预处理的概念与作用

2.1.1预处理的概念
处理器(cpp)根据以字符#开头的下令,修改原始的C程序。通常包括体系自动查抄包含预处理指令的语句和宏界说并举行相应的替换、删除注释及多余空白字符等,最终将调整后的源代码提供给编译器。比如hello.c中第一行中的 #include <stdio.h> 下令告诉预处理器读取体系头文件stdio.h的内容,并把它直接插入程序文本中。结果是得到了另一个C程序,通常以.i作为文件扩展名。
2.1.2预处理的作用
预处理可以处理C语言中以#开头的语句,大抵包括以下几类:
#define:举行宏替换,用实际的常量或字符串常量来替换它的符号;
#include:处理文件包含,将包含的文件插入到程序文本中从而把包含的文件和当前源文件毗连成一个新的源文件;
#if、#elif、#else等:条件编译,选择符合条件的代码送至编译器编译,实现有选择地实行相干操作
注释:删除C语言源程序中所有的注释;
#error等:特别控制指令。

2.2在Ubuntu下预处理的下令


gcc -E hello.c -o hello.i 或cpp -o hello.i hello.c


图1 预处理下令运行截图

2.3 Hello的预处理结果剖析

原本的24行hello.c源程序文件颠末预处理生成了3061行的hello.i预处理文本文件。代码量显著增长。hello.i中的main函数(除注释与头文件)部门位于代码的末了,与源程序中的main函数基本雷同,没有改变。

图2 hello.c文件与hello.i文件对比
对比发现,首先是注释部门被删除了,而且插入了大量的代码。其中在源程序中被引用的stdio.h,unistd.h,以及stdlib.h的代码,都被直接插入了程序文本中。

图3插入的stdio.h


图4 插入的unistd.h



图5 插入的stdlib.h


图6 文件所包含信息



2.4 本章小结

本章介绍了预处理的概念和详细的作用,并实行了hello.c的预处理过程,生成了hello.i文件,通过对比生成的hello.i文件与hello.c文件,加深了对预处理过程的明确。更好的明确了预处理的概念和作用。
(第2章0.5分)


第3章 编译


3.1 编译的概念与作用

注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序
3.1.1 编译的概念
编译是指对颠末预处理之后得到的.i文本文件通过编译器cc1举行词法分析和语法分析,确保所有语句符合语法规则,并将其转换为等价的中间代码或汇编代码的过程。
3.1.2 编码的作用
在编译阶段中,gcc首先要查抄代码的规范性、是否有语法错误等,在查抄无误后,gcc把代码翻译成汇编语言。在编译阶段,编译器还可以起到优化的作用。编译器颠末词法分析、语法分析、中间代码生成、代码优化、目标代码生成几个过程将高级语言编写的源代码转换为机器可以实行的目标代码,从而确保程序的正确性和高效性。
3.2 在Ubuntu下编译的下令

gcc -S hello.i -o hello.s 或cc -S -o hello.s hello.i


图8编译过程截图

3.3 Hello的编译结果剖析

3.3.1数据

  • 常量数据

  • 数字常量



图9-1 源程序中出现的数字常量



图9-2 hello.s 中编译器将数字常量处理为立即数


  • 字符串常量

图10 源程序中出现的字符串常量



图10 hello.s中将字符串常量存入只读数据段.rodata 中


图11 打印字符串常量


  • 变量数据

  • 局部变量

图12 源程序中局部变量







图13 编译后的局部变量


3.3.2 赋值

图14 源程序中对局部变量i赋值


图15 汇编文件中对局部变量i赋值

3.3.3类型转换

图16 源程序中的类型转换


 颠末编译器编译后,argv[3]将字符型转化为整型数,存放在%eax中。

图17 编译后文件中的类型转换

3.3.4 算术操作

图18 源程序中的算术操作


图19 编译后的算术操作

3.3.5 关系操作

图20 源程序中的比力操作


图21编译后的比力操作

3.3.6  控制转移
第一个比力argc和5是否相称
第二个i初始化为0后,无条件跳转进入循环。
第三个比力i和9,小于便是9继续循环,大于9跳出循环。

图22编译后文件中的三次控制转移

3.3.7数组/指针/结构操作


图23 源程序中的数组


图24 编译后文件中的数组

3.3.8 函数调用
main()函数
参数通报:源程序中:int argc,char *argv[]
编译后:movl %edi, -20(%rbp)
               movq %rsi, -32(%rbp)
局部变量:int i
函数返回:
编译前:exit(1);
return 0;
编译后:movl $1, %edi
     call    exit@PLT
        movl $0, %eax
     Leave
函数调用
1.printf()函数:
调用puts()函数举行字符串打印
还有正常调用printf()函数举行打印
2.exit()函数:参数为1
3.atoi()函数:参数为argc[4]
4.sleep()函数:参数为atoi(argv[3])的返回值
5.getchar()函数,没有参数

图25 源程序中的函数调用


图26 编译后文件中的函数调用


3.4 本章小结

本章主要介绍了编译的的概念以及编译的作用与功能,并在使用下令在Ubuntu下将hello.i文件编译生成了hello.s文件。而且通过比力源程序和编译后的文件,渐渐分析了编译器是怎样处理C语言的各个数据类型以及各类操作的过细入微的分析过程加深了我对编译部门的明确。

(第3章2分)


4章 汇编


4.1 汇编的概念与作用

4.1.1 汇编的概念
汇编是指汇编器将.s文件翻译成机器语言指令,把这些指令打包成可重定位目标文件的格式,而且将结果生存在.o文件中的过程。汇编后的文件是二进制文件。

4.1.2汇编的作用
将高级语言转换为机器代码,提供对硬件的低级控制,而且可以优化性能关键的代码段。
4.2 在Ubuntu下汇编的下令

gcc -c hello.s -o hello.o或as -o hello.o hello.s

图27 汇编下令的截图

4.3 可重定位目标elf格式

分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。
readelf -a hello.o > hello1.elf

图28 将hello.o汇编成.elf格式

4.3.1 文件头

图29 文件头

分析:Magic即魔数,用于标识ELF文件,其描述了生成该文件的体系的字的大小和字节顺序,ELF头的其余部门包含资助链接器剖析和表明目标文件的信息:包括ELF头的大小、目标文件的类型、处理器架构、节头部表的文件偏移,以及节头部表中条目标大小和数量等。
4.3.2 程序头
本程序中程序头为空

图30 文件头

4.3.3节表
节表描述了ELF文件中各个节的信息,包括节的名称、类型、偏移、大小等以及可以对各部门举行的操作权限。


  • 节头

图31 节头


  • 重定位节
用readelf -a hello.o探查ELF文件中能探查的其他节,其中.rela.text 包含 8 个条目。重定位节记录了各段中引用符号的相干信息。在链接过程中,链接器需要对这些位置的地址举行重定位。通过重定位条目标类型,链接器将判断怎样盘算地址值,并使用偏移量等信息盘算出正确的地址。



图32 重定位节

偏移量

需重定向的代码在.text或.data节中的偏移位置

信息

包括symbol和type两部门,symbol占前半部门,type占后半部门,symbol表示重定位到的目标在.symtab中的偏移量,type表示重定位的类型

类型

重定位目标的类型

加数

盘算重定位位置的辅助信息

    表2  hello1.elf重定位节中的相干信息



  • 符号表
符号表中列出了所有定位、重定位过程中需要引用的符号和信息,包括函数、变量和节。所有需要的符号均在其中声明。

图33 符号表

4.4 Hello.o的结果剖析

objdump -d -r hello.o  分析hello.o的反汇编,并请与第3章的 hello.s举行对照分析。
说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不同等,特别是分支转移函数调用等。

图34  hello.o对应的反汇编代码

4.4.1 数字进制不同
hello.s中的数字常量用十进制表示,hello.o的反汇编代码中的操作数用十六进制表示。

图35  hello.o反汇编代码和hello.s中不同的数字进制表示

4.4.2 函数调用不同
在hello.s中调用函数时,call指令后直接引用函数名称,hello.o的反汇编代码中,在call指令后加上下一条指令的地址来表示函数调用。在汇编之后、链接之前,hello.o文件包含机器语言代码,但还需要链接其他调用函数的.o文件,才能生成最终的可实行文件。因此,在call指令反面会留下链接用的空间,以便链接器在下一步举行重定位和链接。

图36  hello.o反汇编代码和hello.s中不同的函数调用

4.4.3 分支转移不同
在hello.s中,每个段都有其段名,分支跳转时,跳转指令后用相应段名表示跳转位置。而在hello.o的反汇编代码中,跳转指令后使用相应的地址表示跳转位置。

图37 hello.s和hello.o反汇编代码中不同的分支转移

4.5 本章小结

本章通过将hello .s 文件汇编成 hello.o 文件,深入明确了从汇编程序到可重定位目标程序(二进制文件)的过程。同时,查看ELF表,分析了其中的各项内容。接着将 hello.o 文件举行反汇编,通过与hello.s的对比,直观明确了它们的区别,并深入了解了机器代码的逻辑。

(第4章1分)



5链接

5.1 链接的概念与作用

注意:这儿的链接是指从 hello.o 到hello生成过程。
5.1.1 链接的概念
链接是将各种代码和数据部门收集并组合成为一个单一文件的过程,这个文件可被加载到内存并实行。链接可以在编译时实行,也可以在加载时实行,或者在运行时实行。
5.1.2 链接的作用
链接在软件开辟中至关重要,由于它使分离编译成为大概。通过链接,我们可以将一个程序分割成多少独立的模块,为每个模块编写不同的源代码,并分别将其编译为目标文件或库,末了将这些模块链接起来形成一个完备的程序。
5.2 在Ubuntu下链接的下令

ld  -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 /usr/lib/gcc/x86_64-linux-gnu/9/crtbegin.o hello.o -lc /usr/lib/gcc/x86_64-linux-gnu/9/crtend.o /usr/lib/x86_64-linux-gnu/crtn.o -z relro -o hello  

图38 生成可实行文件截图

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

分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。
下令readelf -a hello > hello2.elf
5.3.1 ELF头
与 hello1.elf 相比,hello2.elf 的基本信息(如 Magic 字段和种别等)未发生改变,但文件类型有所不同,程序头的大小和节头的数量有所增长,而且获得了入口地址。



图39 ELF头

5.3.2 节头
与hello1.elf相比,hello2.elf在链接之后的数量更多,从14个节变为30个

图40 节头

5.3.3程序头

  和hello1.elf相比hello2.elf中增长了程序头部门,它包含了怎样将文件的各个段加载到内存的指示信息,是实行程序时参考的关键部门。


图41 程序头

5.5.4 分段映射节

图42分段映射节

5.5.5动态节


图43 动态节

5.5.6重定位节


图44 重定位节

5.5.7符号表


图45 符号表

5.4 hello的虚拟地址空间

Data Dump从地址0x400000开始与程序头处读取的相干信息雷同。与5.3对照可以发现各段均一一对应,可直观发现各段的虚拟地址与节头存在对应关系。

  



图46 历程的虚拟地址空间各段信息

5.5 链接的重定位过程分析

将hello.o与hello文件的反汇编代码举行对比分析,发当代码量显著增长。扩充的代码包括程序加载后在实行main之前举行的一些准备工作,以及 hello 需要使用的一些库函数的界说等。
在hello的反汇编文件中,每行指令都有唯一的虚拟地址,而hello.o的反汇编文件中,指令只有相对于代码段的偏移地址。这是由于目标文件只是一个中间产物,还没有被链接到最终的内存地址空间。hello是颠末链接后,已经完成了重定位的可实行文件。每条指令都被分配了唯一的虚拟地址,指令的地址关系已经确定。






图47 hello.o与hello的反汇编代码对比

链接过程会分析所有相干的可重定位目标文件,并举行符号剖析,将每个符号引用与符号界说关联起来。接下来,链接器根据汇编器产生的重定位条目指令,举行重定位,将每个符号界说与一个详细的内存位置关联。最终,链接器将程序运行所需的各个部门组装在一起,生成一个可实行目标文件。

5.6 hello的实行流程

程序名

程序地址


main

0x4011d6

_init

0x401000

_start

0x4010f0

printf@plt

0x4010a0

puts@plt

0x401090

sleep@plt

0x4010e0

getchar@plt

0x4010b0

exit@plt

0x4010d0

_fini

0x4012f8

    

使用gdb/edb实行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程(主要函数)。请列出其调用与跳转的各个子程序名或程序地址。
5.7 Hello的动态链接分析

   当程序调用共享库中的函数时,编译器无法预测该函数的确切地址,由于界说该函数的模块可以在运行时加载到任何内存位置。为了解决这个题目,编译器采用了延迟绑定的计谋,将函数地址的加载推迟到第一次调用该函数时。动态链接器使用全局偏移量表(GOT)和过程链接表(PLT)来实现函数的动态链接。GOT是数据段的一部门,而PLT是代码段的一部门。

PLT:PLT是一个数组,其中每个条目是16字节代码。PLT[0]是一个特别条目,它跳转到动态链接器中。每个被可实行程序调用的库函数都有它自己的PLT条目。每个条目都负责调用一个详细的函数。
GOT:GOT是一个数组,其中每个条目是8字节地址。和PLT连合使用时,GOT[O]和GOT[1]包含动态链接器在剖析函数地址时会使用的信息。GOT[2]是动态链接器在1d-linux.so模块中的入口点。其余的每个条目对应于一个被调用的函数,其地址需要在运行时被剖析。每个条目都有一个相匹配的PLT条目。
通过hello的ELF文件,可以看到GOT和PLT节的起始地址分别为0x403ff0和0x404000

在main处打断点观察变量前后变革:

分析hello程序的动态链接项目,通过edb/gdb调试,分析在动态链接前后,这些项目标内容变革。要截图标识说明。
5.8 本章小结

本章主要介绍了链接的概念及其作用。在Ubuntu环境下完成了链接过程,并分析了生成的hello可实行文件的ELF格式信息。使用EDB查看了“hello”程序的虚拟地址空间,使用objdump对可实行目标文件hello举行反汇编,并与之前的“hello.o”的反汇编程序举行比力。别的,还了解了链接的过程并分析了符号剖析和重定位。末了,扼要介绍了分析了“hello”程序动态链接的过程。
(第5章1分)



6hello历程管理


6.1 历程的概念与作用

6.1.1历程的概念
历程是盘算机操作体系中一个基本的实行单元,是正在运行的程序的实例。它不但包含程序代码,还包含程序计数器、寄存器内容、变量和程序所需的资源。
6.1.2历程的作用
历程提供了独立的逻辑控制流,使得我们的程序好像独占地使用处理器。同时,它还提供了一个私有的地址空间,使得我们的程序好像独占地使用内存体系。别的,历程使CPU能够被科学有用地分别成多个部门,以便并行地运行多个历程。
6.2 简述壳Shell-bash的作用与处理流程

Shell-bash是体系的用户界面,提供了用户与内核举行交互操作的一种接口。可以接收用户输入的下令并将其送入内核去实行。
作用:shell是一个下令表明器,它表明由用户输入的下令而且把它们送到内核。这些下令可以是体系内置的下令,也可以是用户自界说的脚本或程序。
处理流程:当Shell准备好接受用户输入时,会显示一个提示符,通常是一个特别字符或者是包含主机名、当前目次等信息的字符串。Shell接收到用户输入后,会对其举行表明,并根据表明的结果实行相应的操作。如果是体系内置下令,Shell会直接实行;如果是外部程序或脚本,Shell会调用体系的实行程序来运行它们。如果有涉及,Shell会负责调用相应的体系功能来完成创建新的历程、分配资源、实行体系调用等操作。实行完下令后,Shell会将下令的输出结果显示给用户,并等候用户输入下一个下令。

6.3 Hello的fork历程创建过程

父历程通过fork函数创建一个新的运行的子历程,子历程中,fork返回0,父历程中,返回子历程的PID;子历程会险些但不完全地复制父历程的状态。子历程会获得与父历程雷同的虚拟地址空间的副本,但是独立的一份副本,子历程与父历程的最大区别在于子历程有不同于父历程的PID。当我们在shell中运行一个程序,比如./hello时,操作体系就会创建一个子历程来运行这个程序。
6.4 Hello的execve过程

在shell调用fork()函数创建子历程后,会调用execve函数,在历程的上下文中加载并运行“hello”程序。在调用execve函数时,操作体系会将当前历程的内存空间覆盖为新程序的代码和数据,并设置新的栈。然后,启动代码会被实行,它会初始化栈,并将可实行目标文件中的代码和数据从磁盘复制到内存中。末了,程序会通过跳转到入口点或第一条指令来运行,从而将控制转移给新程序的主函数。在正常环境下,execve函数不会返回到调用程序,只有当出现错误时才会返回。
6.5 Hello的历程实行

历程的正常运行依赖于其上下文状态,这些状态包括存储在内存中的程序代码、数据、栈、寄存器以及所占用的资源等。在程序正常运行期间,这些上下文状态必须保持完好,不能被异常破坏。
然而,由于CPU时间片的限制和多任务环境下的需求,历程需要不断举行切换。当一个历程正在运行时,大概会突然发生由主板上的时钟芯片引发的时钟中断,导致处理器立即从用户态转换到内核态。在内核态,操作体系会暂时生存当前历程的上下文,并通过历程调度程序选择下一个要实行的历程。内核会加载所选历程的生存的上下文,并将控制流交给该历程,然后处理器重新转回到用户态。
同时,操作体系会给每个历程分配一个时间片,该时间片决定了当前历程能够连续实行其控制流的一段时间。当历程的时间片用尽或者发生需要切换的事件时,如调用sleep函数导致体系调用引发异常,历程将被置于休眠态,控制流切换到其他历程。直到休眠时间竣事或者其他条件满足时,操作体系会规复被休眠历程的上下文,并将控制流重新交给该历程,处理器切换回用户态。
6.6 hello的异常与信号处理

6.6.1不停乱按,包括回车
在hello历程正常运行时,如果不断乱按键盘上的字符包括回车,这些字符大概会被打印在屏幕上,但没有实际作用。这是由于乱按按键会触发键盘中断(异步异常),导致处理器立即从用户态切换到内核态。在内核态,内核会识别到按下了某个字符,并大概会将其输出到屏幕上。然而,由于hello历程正在实行,所以它并不会对键盘输入做出任何响应,因此这些输入对历程本身没有影响。

6.6.2 Ctrl-C
程序实行时时按Ctrl-C,会导致断异常,使内核产生信号SIGINT,发送给父历程,父历程接收后,向子历程发生SIGKILL来强制终止子历程并接纳。

6.6.3 Ctrl-Z
在程序实行过程中按Ctrl-Z,会产生中断异常,发送信号SIGSTP,这时hello的父历程shell会接收到信号SIGSTP并运行信号处理程序。最终hello被挂起,并打印相干信息。

输入ps将打印各历程的pid。

输入jobs将打印出被挂起的hello的jid及标识。

输入pstree 以树状图显示所有历程。

输入fg 将hello历程再次调到前台实行。

输入kill将杀死指定历程组的历程。

6.7本章小结

本章主要介绍了hello可实行文件的实行过程,包括历程的创建、加载和终止,以及处理键盘输入等环节。从历程的创建到其终止和接纳,整个过程依赖于各种异常和中断的处理。程序的高效运行离不开异常、信号和历程管理等机制,正是这些机制确保了hello程序能够顺遂地在盘算机上实行。
(第6章1分)


7hello的存储管理


7.1 hello的存储器地址空间

7.1.1 逻辑地址
逻辑地址是由程序生成的段内偏移地址,也称为相对地址。它需要颠末盘算或转换才能得到内存中的实际有用地址,即物理地址。在hello程序的反汇编代码中看到的地址,通过加上对应段的基地址后才能得到实际的内存地址,这些就是hello中的逻辑地址。
7.1.2 线性地址
线性地址是逻辑地址到物理地址转换之间的中间层。hello程序的代码会生成逻辑地址,通过加上对应段的基地址,这些逻辑地址便转换成了hello程序中内容对应的线性地址。
7.1.3 虚拟地址
逻辑地址有时也被称为虚拟地址。由于它们与虚拟内存空间的概念雷同,逻辑地址独立于实际物理内存容量,这些地址在hello程序中就是虚拟地址。
7.1.4 物理地址
物理地址是CPU外部地址总线上出现的寻址物理内存的地址信号,是地址转换的最闭幕果。在hello程序运行时,访问内存时需要通过CPU生成虚拟地址,然后通过地址翻译得到物理地址,并通过物理地址访问内存中的位置。
7.2 Intel逻辑地址到线性地址的变换-段式管理

段式管理的全称是段页式内存管理,这种方式将逻辑地址转换为线性地址,然后将线性地址转换为物理地址。逻辑地址与逻辑空间对应,逻辑空间被分别为不同长度的各个段。首先,通过段选择符找到对应的段描述符,段描述符中存储着该段的详细信息,然后根据段描述符获取到基地址。将基地址与偏移量相加即可得到线性地址。需要注意的是,线性地址和逻辑地址在本质上是雷同的。
7.3 Hello的线性地址到物理地址的变换-页式管理

线性地址到物理地址的转换是通过页式管理实现的。分页机制将虚拟内存空间分别成多少页,然后通过页表将这些虚拟页地址与物理内存地址建立一一对应关系。硬件地址转换机构(MMU)负责解决离散地址的转换题目。页式管理采用哀求调页或预调页技术,实现表里存储器的统一管理。页表是由页表项(PTE)组成的数组,存储在内存中,用于将虚拟页地址映射到物理页地址。
如图所示,一个虚拟地址(VA)包含两个部门:虚拟页号(VPN)和虚拟页偏移量(VPO),其中VPO和物理页偏移量(PPO)是雷同的。MMU使用VPN查找得当的PTE。如果PTE的有用位为1,即PTE命中,则直接将PTE中存储的物理页号(PPN)和虚拟地址中的虚拟页偏移量(VPO)联合,得到相应的物理地址。如果PTE不命中,则会触发缺页异常,调用缺页处理子程序举行处理。

图49 虚拟地址到物理地址的变换

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

为节省页表空间,CPU通常采用多级页表机制,即上一级页表中的条目指向下一级页表。在常见的x86-64模式下,CPU使用四级页表。线性地址按位分别为五部门,前四部门分别作为每一级页表的索引,最低的12位作为页的偏移地址。CPU逐级查找对应物理页的PTE,最终得到物理地址。
为了优化页表查找效率,CPU还提供了用于页表的专用缓存TLB(翻译后备缓冲区)。TLB缓存页表的PTE,从而减少对内存的访问次数。
7.5 三级Cache支持下的物理内存访问

得到物理地址后,将其分解为标志位(CT)、组索引(CI)和块偏移(CO)。根据CI在L1缓存中查找对应的组,然后依次与组中每一行的数据比力,如果有用位为有用且标志位同等,则命中。如果命中,直接返回所需数据。如果不命中,则依次查找L2、L3缓存,直到找到数据为止,命中时将数据传给CPU,并更新各级缓存。不命中时,若有空闲块,则放置在空闲块中,否则根据替换计谋选择断送块。
7.6 hello历程fork时的内存映射

在 Shell 输入下令后,内核通过实行 fork 来生成子历程,为 hello 程序的实行准备上下文,并赋予一个与父历程不同的PID。通过 fork 生成的子历程继承了父历程的内存地域结构、页表等副本,同时子历程也能访问父历程已打开的文件。当 fork 在新历程中完成后,新历程的虚拟内存与触发 fork 时父历程的虚拟内存保持同等。之后,当任一历程实行写操作时,写时复制机制将触发新页面的创建,以此维护每个历程的独立私有地址空间。
7.7 hello历程execve时的内存映射

实行 execve 函数以在当前历程中加载并实行可实行文件 hello,需要如下步调
1.删除已存在的用户地域:这里指在fork后创建于此历程用户地域中的shell父历程用户地域副本。
2.设置私有地域:为 hello 程序的代码、数据、BSS段和栈地域创建新的地域结构。这些地域都设定为私有并支持写时复制。代码和数据地域映射自 hello 文件的 .text 和 .data 段。BSS段初始化为零,对应匿名映射,其大小根据 hello 文件界说。栈和堆地域也是哀求二进制零的,初始长度为零。
3.配置共享地域:hello 程序需要与共享库 libc.so 举行动态链接。将 libc.so 映射到历程的用户虚拟地址空间内的共享地域。
4.初始化程序计数器:将程序计数器(PC)设置到hello代码地域的起始点。当历程下次被调度运行时,将从这个位置开始实行。
7.8 缺页故障与缺页中断处理

虚拟内存在DRAM缓存不命中即为缺页故障。
处理缺页中断的步调:缺页中断处理程序首先确定要换出的断送页。如果该页已被修改,它将被写回到磁盘。然后,处理程序调入缺失的页面,并在内存的页表项中举行更新。末了,缺页中断处理程序将控制权返回给之前的历程,重新实行触发缺页异常的指令。
7.9动态存储分配管理

7.9.1动态内存管理的基本方法
动态内存分配器管理历程的一个虚拟内存地域,称为堆。这个分配器把堆视为由多种大小的内存块组成的集合。这些内存块,每一块都代表了一段连续的虚拟内存空间,可以是已被分配的,也可以是未被分配的。
已分配的内存块被明确地保留给应用程序使用。而未分配的块则保留在空闲状态,等候被应用程序分配使用。空闲块直到显式地被应用所分配前保持保持空闲。已分配的块将在释放前一直保持已分配状态,这种释放可以由应用程序显式实行或内存分配器自身隐式实行。
7.9.2 两种分配器
显示分配器:要求应用显示地释放任何已分配的块。如C语言中调用malloc函数来分配一个块,然后调用free函数来释放一个块。
隐式分配器:要求分配器到检测一个已分配块不再被程序所使用时,释放这个块。隐式分配器也叫做垃圾收集器,而自动释放未使用的已分配的块的过程叫做垃圾收集。
7.9.3 两种构造内存块的方法
1.显示链表
每个空闲块中,都包含一个前驱(pred)指针和一个后继(succ)指针,来减少搜索与适配的时间。

图50 分配块与空闲块

2.隐式链表
在堆中,空闲块通过它们头部的字段被隐式链接起来。分配器通过遍历堆的所有块来间接地遍历整个空闲块集合。

图51 简单的堆块的格式

7.10本章小结

本章深入讨论了hello程序的存储器地址空间,涵盖了逻辑地址、线性地址、虚拟地址和物理地址的界说,它们之间的关系及转换方法。分析了段式管理怎样将逻辑地址转换为线性地址(即虚拟地址),探讨了页式管理怎样实现从线性地址到物理地址的转换。详细介绍了在TLB和四级页表支持下,虚拟地址(VA)到物理地址(PA)的转换过程。强调了高速地址变址缓存(TLB)加速页表访问的功能。别的,讨论了在三级Cache支持下的物理内存访问流程,包括缓存命中和未命中的环境。分析了hello历程在实行fork与execve操作时的内存映射变革,还介绍了处理缺页故障和缺页中断的方法,详细说明了缺页中断的处理流程。末了,探讨了动态存储分配的管理。
(第7章 2分)


8hello的IO管理


8.1 Linux的IO装备管理方法

8.1.1装备的模型化:文件
在Linux中,一个文件被视为一个字节序列,通常有着特定的大小,用字节表示。所有的I/O装备,如网络、磁盘和终端,都被抽象为文件。因此,对于这些装备的输入输出操作都可以通过文件的读取和写入来完成。
8.1.2装备管理:unix io接口
通过将装备映射为文件,Linux内核提供了一种简单而底层的应用接口—UNIX I/O,使得对装备的访问与对普通文件的操作方式雷同。

8.2 简述Unix IO接口及其函数

8.2.1打开文件
应用程序哀求内核打开特定文件以访问I/O装备。内核会返回一个非负整数,称为文件描述符,用于标识该文件,并在后续操作中使用。内核会记录有关打开文件的所有信息,而应用程序只需记住该描述符即可。
8.2.2改变文件位置
对于每个打开的文件,内核都会维护一个文件位置,初始值为0。这个文件位置表示从文件开头开始的字节偏移量,应用程序可以通过实行seek操作来显式设置文件的当前位置。
8.2.3读写文件
读操作从文件的当前位置开始,将n个字节复制到内存中,从文件位置k到k+n的范围。写操作则将内存中的n个字节从当前位置k开始写入文件,然后更新文件的位置为k。
8.2.4 关闭文件
当不再需要打开的文件时,内核会释放相干的数据结构,并将文件描述符返回到可用的描述符池中。无论历程因何种原因终止,内核都会关闭所有已打开的文件,并释放它们的内存资源。
8.2.5 Unix I/O函数
1)open()函数:
int open(char *filename, int flags, mode_t mode);
open 函数将指定的文件 filename 转换为一个文件描述符,并返回该描述符的数字。flags参数指明历程怎样访问文件,mode 参数指定了新文件的访问权限位。

  • close()函数:
int close(int fd);
用于关闭一个打开的文件。

  • read()函数:
ssize_t read(int fd, void *buf, size_t n);
从描述符为 fd 的当前文件位置复制最多n个字节到内存位置buf。如果发生错误,函数返回值为-1,返回值为 0表示EOF。否则,返回值表示实际传输的字节数量。
4)write()函数:
ssize_t write(int fd, const void *buf, size_t n);
从内存位置buf复制最多n个字节到描述符fd的当前文件位置。
5)lseek()函数:
使应用程序能够显式地修改当前文件的位置。
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;
}

首先,printf函数会在内存中开辟一块输出缓冲区,然后使用vsprintf函数在输出缓冲区中生成要输出的字符串。接下来,通过write体系调用将这个字符串输出到尺度输出装备,比如屏幕。write体系调用会触发一个体系调用陷阱,将控制权转移到内核态。在内核中,显示驱动程序会将这些字符串转换为相应的像素数据,并将它们传输到屏幕对应地域的视频内存(VRAM)中。显示芯片会按照设定的革新频率逐行读取VRAM,并通过信号线向液晶显示器传输每一个像素的RGB分量,最终在屏幕上显示出对应的内容。
8.4 getchar的实现分析

int getchar(void)
{
static char buf[BUFSIZ];
static char* bb=buf;
static int n=0;
if(n==0)
{
n=read(0,buf,BUFSIZ);
bb=buf;
}
return(--n>=0)?(unsigned char)*bb++:EOF;
}
当程序调用getchar()时,程序会等候用户按键。用户输入的字符被存放在键盘缓冲区中,直到用户按下回车为止。当用户按下回车后,getchar()开始从尺度输入(stdin)流中每次读取一个字符。getchar()函数的返回值是用户输入的第一个字符的ASCII码,如果出错则返回-1,并将用户输入的字符回显到屏幕上。如果用户在按下回车之前输入了多个字符,那么这些字符会保留在键盘缓冲区中,等候后续的getchar()调用来读取。因此,后续的getchar()调用不会等候用户按键,而是直接读取键盘缓冲区中的字符,直到缓冲区中的字符被读取完毕后,才会再次等候用户的输入。可以将其看作是一种异步事件处理,即键盘中断处理。键盘中断处理程序接收按键的扫描码并将其转换成ASCII码,然后将ASCII码生存到体系的键盘缓冲区。当getchar()函数调用read()体系函数时,通过体系调用读取键盘缓冲区中的按键ASCII码,直到接收到回车键才返回。
8.5本章小结

本章主要介绍了Linux中的IO装备管理方法、Unix I/O接口以及相干函数,以及printf和getchar函数的实现原理。通过本章的学习,深入了解了Unix I/O接口在Linux体系中的作用,以及键盘中断作为异步异常的处理过程。
(第8章1分)







结论

用盘算机体系的语言,逐条总结hello所经历的过程。
你对盘算机体系的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。
1.hello.c源文件颠末预处理生成hello.i。
2.编译器(cpp)编译hello.i生成hello.s汇编文本文件。
3.将hello.s汇编程序翻译成机器语言指令,并把这些指令打包成可重定位目标程序格式,最闭幕果生存在hello.o目标文件中。
4.通过链接器,将hello的程序编码与动态链接库等收集整理成为一个单一文件,生成完全链接的可实行的目标文件hello。
5. 在Shell中输入下令./hello 王均怡 2022112282 13212801667 2,生成子历程。
6.子历程使用execve函数加载并运行名为hello的程序,并通过参数通报给该程序。
7. 操作体系为子历程分配虚拟内存空间,并将hello程序加载到该空间中。
8. 内核使用异常控制流调度hello历程,使其开始实行。在实行过程中,hello历程调用printf函数和getchar等函数举行输入输出操作。
9. 当hello历程实行完毕后,Shell父历程等候并接纳子历程资源,内核删除为hello历程创建的所有数据结构。
深切感悟:通过盘算机体系的学习,我了解了在学习C语言的时间一个最为简单的hello程序实现的背后,原来是云云复杂的过程。在没有学习盘算机体系时,我以为hello是最为简单的程序,但是颠末本次大作业,我深入的了解了hello需要经历的每一个步调,以及每个步调是怎样实现的有什么作用。对于盘算机的学习,我还是一个初学者,但本次大作业也让我看到了盘算机学习的精妙。在以后的学习生活中,我也将继续深入的学习盘算机体系相干知识,并使用这些知识举行更为深入的学习与创新。

(结论0分,缺失 -1分,根据内容酌情加分)

附件

列出所有的中间产物的文件名,并予以说明起作用。
文件名

作用

hello.c

源C语言程序

hello.i

预处理生成的文本文件

hello.s

编译生成的汇编文件

hello.o

汇编生成的可重定位目标文件(二进制)

hello

经链接生成的可实行目标文件(二进制)

hello1.elf

hello.o经readelf得到的.elf文件

hello1.asm

hello.o反汇编得到的文本文件

hello2.elf

hello经readelf得到的.elf文件

hello2.asm

hello反汇编得到的文本文件


(附件0分,缺失 -1分)


参考文献


为完成本次大作业你翻阅的册本与网站等


  • 伍之昂. Linux Shell编程从初学到精通 [M]. 北京:电子工业出书社 
[2]  Randal E.Bryant, David O'Hallaron. 深入明确盘算机体系(原书第三版)[M]. 机械工业出书社.2016.
[3]  http://www.elecfans.com/emb/20190402898901.html 
[4]  https://www.cnblogs.com/knife-king/p/11090029.html
(参考文献0分,缺失 -1分)



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

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

熊熊出没

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

标签云

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