ICS大作业报告

计算机系统

大作业

题     目  程序人生-Hello’s P2P        

专       业     物联网工程                   

学     号    2021111888                    

班     级    2137301                    

学       生    张金玉              

指 导 教 师    吴锐                 

计算机科学与技术学院

2023年5月

摘  要

本文通过跟踪hello程序的生命周期来加深对计算机系统的理解,从它被程序员创建开始,到在系统上运行,输出简单的信息,然后终止。这是打开新世界的大门,这是一次有趣的计算机系统漫游,能够让我们对计算机系统有更深的理解。

关键词:预处理;编译;汇编;链接;进程;信号;虚拟内存;                            

(摘要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简介

P2P: From Program to Process

程序员用高级语言编写Hello.c,此时这是一个Program,该源程序在Linux中经过cpp的预处理,ccl的编译,as的汇编,ld链接最终形成可执行目标程序Hello。在shell界面输入可执行目标程序名Hello,shell使用fork命令创建子进程,此时成为一个进程,子进程中调用execve,开始进行程序中编写的操作。

020:Zero to Zero

起初电脑中并没有关于Hello程序运行的信息,Shell创建子进程调用execve后映射到虚拟内存,删除当前的数据结构,为Hello创建新的区域结构。进入程序的入口后开始载入内存,进入main函数执行目标代码。CPU分配时间片进行逻辑控制流。当进程结束后,Shell回收Hello进程(Shell是Hello的父进程),内核彻底删除相关的数据结构,不再保留Hello程序运行信息(参数,环境变量数据结构等)。即从0到0.

1.2 环境与工具

硬件环境:X64 CPU;2GHz;2G RAM;256GHD Disk以上

软件环境:Windows10 64位;Ubantu 16.04 LTS 64位

开发与测试工具:gcc,visual studio,edb,readelf,HexEdit

1.3 中间结果

Hello.c

hello.i(预处理生成的文本文件)

hello.s(编译后的汇编语言文件)

hello.o(可重定位目标文件)

hello(链接后的可执行目标文件)

1.4 本章小结

本章介绍了Hello.c程序运行的全过程,让学习者对流程有大致认识。

(第1章0.5分)


第2章 预处理

2.1 预处理的概念与作用

预处理是做些代码文本的替换工作。预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。比如hello.c中第1行的#include 命令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入程序文本中。例如拷贝 #include 包含的文件代码,#define 宏定义的替换,条件编译等,就是为编译做的预备工作的阶段,结果就得到了另一个C程序Hello.i。

预处理作用

(1)宏定义指令,如#define Name TokenString,#undef等。对于前一个伪指令,预编译所要做的是将程序中的所有Name用TokenString替换,但作为字符串常量的Name则不被替换。对于后者,则将取消对某个宏的定义,使以后该串的出现不再被替换。

(2)条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif,等等。这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。

(3)头文件包含指令,如#include "FileName"或者#include 等。在头文件中一般用伪指令#define定义了大量的宏(最常见的是字符常量),同时包含有各种外部符号的声明。采用头文件的目的主要是为了使某些定义可以供多个不同的C源程序使用。因为在需要用到这些定义的C源程序中,只需加上一条#include语句即可,而不必再在此文件中将这些定义重复一遍。预编译程序将把头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。

包含到c源程序中的头文件可以是系统提供的,这些头文件一般被放在/usr/include目录下。在程序中#include它们要使用尖括号(<>)。另外开发人员也可以定义自己的头文件,这些文件一般与c源程序放在同一目录下,此时在#include中要用双引号("")。

(4)特殊符号,预编译程序可以识别一些特殊的符号。例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的.C源程序的名称。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。

2.2在Ubuntu下预处理的命令

预处理指令gcc hello.c -E -o hello.i

如图生成了hello.i文件

2.3 Hello的预处理结果解析

经过预处理后得到hello.i,hello.c从十几行代码扩展到了3060行。在内容上的增加包括引入了头文件中的各种内容、使用了具体的常量来替换宏定义等宏展开等等。

2.4 本章小结

本章对预处理的概念和作用进行了说明,并对hello.c进行了预处理操作以验证。

(第2章0.5分)


第3章 编译

3.1 编译的概念与作用

编译器ccl将文本文件hello.i翻译成文本文件hello.s,它包含一个汇编语言程序。其中每条语句都以一种文本格式描述了一条低级机器语言指令。

汇编语言是非常有用的,因为它为不同高级语言的不同编译器提供了通用的输出语言,例如C编译器和Fortran编译器产生的输出文件用的都是一样的汇编语言。   

编译的作用有:

1.提高执行效率:编译器将源代码转换为机器码后,可以直接在计算机上执行,无需解释器的参与,因此执行速度更快。

2.平台无关性:编译后的目标代码可以在不同的平台上执行,而无需重新编写代码。这是因为编译器将源代码翻译为特定平台的机器码,使得程序具有跨平台的能力。

3.代码优化:编译器可以进行各种优化技术,以改善程序的性能和资源利用率。例如,优化循环、减少内存访问次数等。

4.错误检查:编译器可以检测源代码中的语法错误、类型错误等问题,并提供错误提示和警告信息。这有助于开发人员在编译时发现和修复问题,提高代码的质量和可靠性。

3.2 在Ubuntu下编译的命令

编译指令gcc -S hello.i -o hello.s

3.3 Hello的编译结果解析

3.3.1数据:常量、变量(全局/局部/静态)、表达式、类型、宏   

常量以立即数的形式出现,例如

变量:在hello.c中并无静态变量,所以在hello.s开头并没有.data和.bss,有只读数据节.rodata,里面存放输出的格式串,.LC0中存放的就是输出的字符串的编码形式:

main是全局函数,对全局函数main进行了声明,与C语言中main函数的功能相对应

其他变量为局部变量。

表达式

3.3.2 赋值 =   ,逗号操作符,赋初值/不赋初值

赋值操作通过mov指令实现,mov指令有多个形式,用不同方式传送不同大小的数据

3.3.3类型转换(隐式或显式) unsigned/char/int/long/float/double

此处将字符串转化为整型数atoi(argv[3])

3.3.4 sizeof本程序中未体现

3.3.5算术操作:+ - * / %  ++  --     取正/负+-   复合“+=”等

加法和减法

3.3.6逻辑/位操作:逻辑&& ||  !    位 & | ~ ^    移位>>   <<    复合操作如 “|=” 或“<<=”等

3.3.7关系操作:==    !=      >    <      >=     <=

argc!=4

i< 8

汇编语言中cmpl表示比较,相减设置条件码,通常后跟一条条件跳转语句,根据条件码跳转至不同位置

3.3.8控制转移:if/else switch for while  do/while  ?: continue  break

jmp语句实现控制转移,同3.3.7

3.3.9数组/指针/结构操作:A[i]    &v   *p    s.id    p->id

数组argv[]的首地址为-32(%rbp),传递给寄存器%rax后通过增加%rax的值(按数据字长加)来实现对数组元素的访问。

3.3.10函数操作:参数传递(地址/值)、函数调用()、局部变量、函数返回

(1)main函数

参数传递:第一个参数是argc(int型),第二个参数是argv[](char *型),分别存放在寄存器%rdi和%rsi中;

函数操作中的栈维护:main函数使用栈指针,同时使用栈帧%rbp来记录使用情况。如图main函数在进入时先将rsp减去32形成一个栈空间结构,然后开始进行各种操作。

(2)循环函数:

可以看到在cmpl处将4与-20(%rbp)比对,因此可以得出argc存放在-20(%rbp)处。若argc不等于4,进入下面的leaq语句

(3)exit函数:

传递数据:将%edi 设置为 1。

控制传递:call exit@PLT。

(4)sleep函数:

(5)getchar函数:

函数返回 ret

3.4 本章小结

本章通过对hello.i文件进行汇编得到其汇编文本文件hello.s。经过编译之后, hello自C语言解构为更加低级的汇编语言。深入汇编代码细致地分析了汇编代码如何实现变量、常量、传递参数以及分支和循环。

(第3章2分)


第4章 汇编

4.1 汇编的概念与作用

概念:汇编器(as)将.s汇编程序翻译成机器语言,把这些机器语言指令打包成可重定位目标程序的格式,并将结果保存在.o目标文件中,这个过程就叫做汇编.

作用:翻译成机器语言并打包成可重定位目标程序格式

4.2 在Ubuntu下汇编的命令

gcc -c hello.s -o hello.o

4.3 可重定位目标elf格式

ELF头以一个16字节大小的Magic头开始,用来判断文件类型的魔数,随后给出了程序的文件类型、机器类型、字节头部表的文件偏移,以及节头部表中条目的大小和数量等信息。

该节头表包含14个节,节信息无须复制到虚拟地址空间中为可执行文件创建的最终的进程映像。尽管如此,该信息在二进制文件中总是存在的。

(1) .text节:编译程序后生成的机器代码部分。

(2) .rodata节:只读数据部分,包括printf语句中的格式字符串和开关语句的跳转表。

(3) .data节:已初始化的全局和静态C变量存储部分。局部C变量在运行时保存在栈中,不包含在.data节或.bss节中。

(4) .bss节:未初始化的全局和静态C变量存储部分。

(5) .symtab节:符号表,记录程序中定义和引用的函数和全局变量的信息。

不包含与本地非静态程序变量相对应的任何符号,这些变量在运行时在栈中进行管理。

  1. .debug节:调试符号表,包含程序中定义的全局变量和类型定义、引用的全局变量以及原始的C源文件。
  2. .line节:原始C源代码的行号与.text节中机器指令的对应关系。
  3. .strtab节:字符串表,包含.symtab和.debug节中的符号表以及节头部中的节名称。

(9) 节头部表:描述目标文件中各个节的信息。

重定位节项目分析

.rel.text节:一个.text 节中位置的列表,包含.text 节中需要进行重定位的信息,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。

.rel.data节:引用或定义的模块中所有全局变量的重定位信息。

Offset:需要被修改的引用节的偏移Info:包括symbol和type两个部分,symbol在前面四个字节,type在后面四个字节,symbol:标识被修改引用应该指向的符号,

type:重定位的类型Type:告知链接器应该如何修改新的应用Attend:一个有符号常数,一些重定位要使用它对被修改引用的值做偏移调整Name:重定向到的目标的名称。

4.4 Hello.o的结果解析

  1. 操作数:1.hello.s中的操作数是十进制,hello.o反汇编代码中的操作数是十六进制。

  1. 分支转移函数调用:跳转语句之后,hello.s中是.L2和.LC1等段名称,而反汇编代码中跳转指令之后是相对偏移的地址,也即间接地址。

PC表示相对寻址

(3)操作指令:机器指令中有许多mov,add,去掉了汇编指令中的该类的表示大小的后缀。

4.5 本章小结

本节主要讲述汇编的概念与作用,可重定位目标elf格式和他的各节详细信息,还介绍了反汇编代码与hello.s文件的差别

(第4章1分)


5链接

5.1 链接的概念与作用

hello程序使用了printf函数,它是每个C编译器都提供的标准C库中的一个函数。printf函数存在于一个名为printf.o的单独的预编译好了的目标文件中,而这个文件必须以某种方式合并到我们的 hello.o程序中。链接器(ld)就负责处理这种合并。结果就得到hello文件,它是一个可执行目标文件(或者简称为可执行文件),可以被加载到内存中,由系统执行。

链接的主要作用有:

(1)符号解析:链接器通过符号解析将模块之间的符号引用与符号定义关联起来。它会解析函数调用和全局变量的引用,将其与定义进行匹配.

(2)符号重定位:链接器根据符号解析的结果,将目标文件中的相对地址转换为最终的绝对地址。这样可以确保程序在运行时能够正确地访问和调用函数以及使用全局变量。

(3)节合并:链接器将多个目标文件中相同类型的节(如代码节、数据节)合并成一个单独的节。这样可以消除重复的节并减小最终可执行文件的大小。

(4)库链接:链接器能够将程序所依赖的库文件与目标文件进行链接,将库中的函数和代码合并到最终的可执行文件中。这样可以在程序中使用库中提供的功能。

(5)生成可执行文件或库文件:链接器最终生成一个可执行文件,该文件包含了所有链接后的目标文件和库文件,可以直接执行。另外,链接器也可以生成库文件,供其他程序进行链接使用。

通过链接的过程,将多个目标文件整合成一个可执行文件或库文件,使得程序能够顺利运行并调用所需的函数和资源。

5.2 在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

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

分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。

有入口点地址

节信息:

5.4 hello的虚拟地址空间

Hello虚拟地址从0040000到00400ff0

如5.3中节头表所示,

.rodata从00402000 0002000开始,长度为3b

5.5 链接的重定位过程分析

hello和hello.o的不同:

1.out比hello多.init和.plt节等其他节

  1. Hello反汇编已经完成了重定位,代码中是确定的虚拟地址,hello.o反汇编中代码虚拟地址均为0

重定位的过程:

(1)重定位节和符号定义。在这一步中,链接器将所有相同类型的节合并为同--类型的新的聚合节。例如,来自所有输入模块的.data节被全部合并成一个节,这个节成为输出的可执行目标文件的.data节。然后,链接器将运行时内存地址赋给新的聚合节,赋给输人模块定义的每个节,以及赋给输人模块定义的每个符号。当这一步完成时,程序中的每条指令和全局变量都有唯一的运行时内存地址了。

(2)重定位节中的符号引用。在这一步中,链接器修改代码节和数据节中对每个符号的引用,使得它们指向正确的运行时地址。要执行这一步,链接器依赖于可重定位目标模块中称为重定位条目( relocation entry)的数据结构

两种基本的重定位类型:

R_X86_64_PC32。重定位一个使用32位PC相对地址的引用。一个PC相对地址就是距程序计数器(PC)的当前运行时值的偏移量。当CPU执行一条使用PC相对寻址的指令时,它就将在指令中编码的32位值加上PC的当前运行时值,得到有效地址(如call指令的目标),PC值通常是下一条指令在内存中的地址。

R_x86_64_32。重定位一个使用32位绝对地址的引用。通过绝对寻址,CPU直接使用在指令中编码的32位值作为有效地址,不需要进一步修改。

5.6 hello的执行流程

ld-2.27.so!_dl_start             Ox7fce 8cc38ea0

ld-2.27.so!_dl_init              Ox7fce 8cc476330

hello!_start                    Ox400500

libc-2.27.so!_libc_start_main     Ox7fce 8c867ab0

-libc-2.27.so!_cxa_atexit         Ox7fce 8c889430

-libc-2.27.so!_libc_csu_init       Ox4005c0

libc-2.27.so!_setjump            Ox7fce 8c884c10

libc-2.27.so!exit                Ox7fce 8c889128

5.7 Hello的动态链接分析

分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的内容变化。要截图标识说明。

在调用共享库函数时,编译器没有办法预测这个函数的运行时地址,因为定义它的共享模块在运行时可以加载到任意位置。正常的方法是为该引用生成一条重定位记录,然后动态链接器在程序加载的时候再解析它。GNU编译系统使用延迟绑定(lazybinding),将过程地址的绑定推迟到第一次调用该过程时。

延迟绑定是通过GOT和PLT实现的。GOT是数据段的一部分,而PLT是代码段的一部分。动态链接器使用过程链接表PLT+全局偏移量表GOT实现函数的动态链接,GOT中存放函数目标地址,PLT使用GOT中地址跳转到目标函数。

根据节头表可知got起始位置为0040400

运行前

运行后got表内容发生改变如下

5.8 本章小结

本章通过链接成功生成了可执行的目标文件,并介绍了动态链接库等内容

(第5章1分)


6hello进程管理

6.1 进程的概念与作用

异常是允许操作系统内核提供进程(process)概念的基本构造块,进程是计算机科学中最深刻、最成功的概念之一。

在现代系统上运行一个程序时,我们会得到一个假象,就好像我们的程序是系统中当前运行的唯一的程序一样。我们的程序好像是独占地使用处理器和内存。处理器就好像是无间断地一条接一条地执行我们程序中的指令。最后,我们程序中的代码和数据好像是系统内存中唯—的对象。这些假象都是通过进程的概念提供给我们的。

进程的经典定义就是一个执行中程序的实例。系统中的每个程序都运行在某个进程的上下文(context)中。上下文是由程序正确运行所需的状态组成的。这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合。

每次用户通过向shell输入一个可执行目标文件的名字,运行程序时,shell就会创建一个新的进程,然后在这个新进程的上下文中运行这个可执行目标文件。应用程序也能够创建新进程,并且在这个新进程的上下文中运行它们自己的代码或其他应用程序。

我们将关注进程提供给应用程序的关键抽象:

—个独立的逻辑控制流,它提供一个假象,好像我们的程序独占地使用处理器。

一个私有的地址空间,它提供一个假象,好像我们的程序独占地使用内存系统。让我们更深入地看看这些抽象。

6.2 简述壳Shell-bash的作用与处理流程

Shell在计算机科学中,Shell俗称壳(用来区别于核),是指"为使用者提供操作界面"的软件(command interpreter,命令解析器)。它类似于DOS下的COMMAND.COM和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。

处理流程:在shell命令行中输入命令:$./hello

shell命令行解释器构造argv和envp;

调用fork()函数创建子进程,其地址空间与shell父进程完全相同,包括只读代码段、读写数据段、堆及用户栈等

调用execve()函数在当前进程(新创建的子进程)的上下文中加载并运行hello程序。将hello中的.text节、.data节、.bss节等内容加载到当前进程的虚拟地址空间

调用hello程序的main()函数,hello程序开始在一个进程的上下文中运行。

如果用户没要求后台运行(命令末尾没有&号)否则shell使用waitpid等待作业终止后返回。

如果用户要求后台运行(如果命令末尾有&号),则shell返回;

6.3 Hello的fork进程创建过程

Shell调用fork()函数创建子进程,其地址空间与shell父进程完全相同,包括只读代码段、读写数据段、堆及用户栈等

6.4 Hello的execve过程

调用parseline函数,这个函数解析了以空格分隔的命令行参数,并构造最终会传递给execve 的argv向量。第一个参数被假设为要么是一个内置的shell命令名,马上就会解释这个命令,要么是一个可执行目标文件,会在一个新的子进程的上下文中加载并运行这个文件。

如果最后一个参数是一个“&”字符,那么parseline返回1,表示应该在后台执行该程序(shell不会等待它完成)。否则,它返回0,表示应该在前台执行这个程序(shell 会等待它完成)。

在解析了命令行之后,eval函数调用builtin_command函数,该函数检查第一个命令行参数是否是一个内置的shell 命令。如果是,它就立即解释这个命令,并返回值1。否则返回0。简单的shell 只有一个内置命令——quit命令,该命令会终止shell。实际使用的 shell有大量的命令,比如pwd、jobs和fg。

如果builtin_command返回0,那么shell创建一个子进程,并在子进程中执行所请求的程序。如果用户要求在后台运行该程序,那么shell 返回到循环的顶部,等待下一个命令行。否则,shell使用waitpid函数等待作业终止。当作业终止时,shell就开始下一轮迭代。

调用execve()函数在当前进程(新创建的子进程)的上下文中加载并运行hello程序。将hello中的.text节、.data节、.bss节等内容加载到当前进程的虚拟地址空间

调用hello程序的main()函数,hello程序开始在一个进程的上下文中运行。

6.5 Hello的进程执行

多个流并发地执行的一般现象被称为并发(concurrency)。一个进程和其他进程轮流运行的概念称为多任务( multitasking)。一个进程执行它的控制流的--部分的每一时间段叫做时间片(time slice)。因此,多任务也叫做时间分片(time slicing)。例如,图8-12中,进程A的流由两个时间片组成。

为了使操作系统内核提供一个无懈可击的进程抽象,处理器必须提供一种机制,限制一个应用可以执行的指令以及它可以访问的地址空间范围。

处理器通常是用某个控制寄存器中的一个模式位(mode bit)来提供这种功能的,该寄存器描述了进程当前享有的特权。当设置了模式位时,进程就运行在内核模式中(有时叫做超级用户模式)。一个运行在内核模式的进程可以执行指令集中的任何指令,并且可以访问系统中的任何内存位置。

内核为每个进程维持一个上下文(context)。上下文就是内核重新启动一个被抢占的进程所需的状态。它由一些对象的值组成,这些对象包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构,比如描述地址空间的页表、包含有关当前进程信息的进程表,以及包含进程已打开文件的信息的文件表。

在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占了的进程。这种决策就叫做调度(scheduling),是由内核中称为调度器(scheduler)的代码处理的。当内核选择一个新的进程运行时,我们说内核调度了这个进程。在内核调度了一个新的进程运行后,它就抢占当前进程,并使用一种称为上下文切换的机制来将控制转移到新的进程,上下文切换1)保存当前进程的上下文,2)恢复某个先前被抢占的进程被保存的上下文,3>将控制传递给这个新恢复的进程。

当内核代表用户执行系统调用时,可能会发生上下文切换。如果系统调用因为等待某个事件发生而阻塞,那么内核可以让当前进程休眠,切换到另一个进程。比如,如果一个read系统调用需要访问磁盘,内核可以选择执行上下文切换,运行另外一个进程,而不是等待数据从磁盘到达。另一个示例是sleep系统调用,它显式地请求让调用进程休眠。一般而言,即使系统调用没有阻塞,内核也可以决定执行上下文切换,而不是将控制返回给调用进程。

Hello以用户模式运行,输出hello,argv[1]和argv[2],然后调用sleep函数,进程进入内核模式,此时进程挂起,定时器开始计时,并将hello从运行队列中移除等待,进行上下文切换,控制权交给其他进程,定时器计时完毕后,再进行上下文切换,控制权给回hello,hello进行自己的逻辑控制流。

6.6 hello的异常与信号处理

以下格式自行编排,编辑时删除

 hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。

 程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps  jobs  pstree  fg  kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。

随意输入会显示出来,若有回车,再程序结束后作为命令输入

Ctrl+Z,内核发送一个SIGSTP信号到前台进程组的每个进程,挂起前台作业

Ctrl+C,内核发送SIGINT信号到前台进程组的每个进程,终止前台作业

PS命令:

可以看到前者(C)展示进程中没有hello,后者(Z)展示进程中有hello

 Jobs

 

 pstree 运行pstree命令可以显示整个进程树,其中可以找到当前的bash进程, 并从其分支中找到"hello"进程。

 fg将程序调到前台来,hello程序被挂起,进程号是1,fg 1 调至前台

 

 Kill发送命令信号,-9表示杀死进程,如图,hello进程未运行完毕被杀死。

6.7本章小结

本章介绍了进程的概念、什么是shell、以及上下切换的过程与异常与信号的处理。

(第6章1分)


7hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:在用户编程过程中使用的地址,由段地址和偏移地址两部分组成.线性地址:在CPU的保护模式下,逻辑地址通过"段基址 + 段内偏移地址"的方式计算得到线性地址。虚拟地址:在虚拟内存中的地址,它是由一个存储在磁盘上的连续字节单元数组组成的,每个字节对应一个虚拟地址。物理地址:在内存单元的真实地址,用于访问实际的物理内存空间。

7.2 Intel逻辑地址到线性地址的变换-段式管理

1段式存储管理的基本思想:把程序按内容或过程(函数)关系分为段,每段有自己的名字。一个用户作业或进程所包含的段对应于一个二维线性的虚拟空间,也就是一个二维虚拟存储器。段式管理程序以段为单位分配内存,通过地址映射机构把段式虚拟地址转换为实际的内存物理地址。

2.段式管理把一个进程的虚地址空间设计成而为结构,即段号S和段内相对地址W。段式管理中段号和段号之间无循序关系,段的划分长度也是不固定的,一个进程中的程序和数据可被划分为主程序段,子程序段,数据段和工作区段。

每个段是一个首地址为零,连续的一维的线性空间。根据需要段可以动态增长。对段式虚地址空间访问包括两个部分:段名和段内地址。

3.段式管理中以段为单位分配内存,每段分配一个连续的内存区。同一进程所包含的各段之间不要求连续。

4.在段的调入时,所需的淘汰段数和段的大小有关,可能在调入某个大段时,需要淘汰几个小段的情况。但是,任何一个段长都不允许超过内存可用区长度,否则会造成内存分配出错。除了初始分配之外,段的动态分配是在CPU所要访问的指令和数据不再内存中时所产生的缺段中断的情况下发生的。因此,段的淘汰和置换实际上是缺段中断处理过程中的一部分。

7.3 Hello的线性地址到物理地址的变换-页式管理

是一种内存空间存储管理的技术,页式管理分为静态页式管理和动态页式管理。将各进程的虚拟空间划分成若干个长度相等的页(page),页式管理把内存空间按页的大小划分成片或者页面(page frame),然后把页式虚拟地址与内存地址建立一一对应页表,并用相应的硬件地址变换机构,来解决离散地址变换问题,页式管理采用请求调页或预调页技术实现了内外存存储器的统一管理。

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

页面命中

1)处理器生成一个虚拟地址,并将其传送给MMU

2-3)MMU生成PTE地址发出请求,高速缓存/主存向MMU返回PTE4)MMU将物理地址传送给高速缓存/主存

5)高速缓存/主存返回所请求的数据字给处理器

缺页异常

1)处理器将虚拟地址发送给MMU

2-3)MMU使用内存中的页表生成PTE地址4)有效位为零,因此MMU触发缺页异常

5)缺页处理程序确定物理内存中牺牲页(若页面被修改,则换出到磁盘)6)缺页处理程序调入新的页面,并更新内存中的PTE

7)缺页处理程序返回到原来进程,再次执行导致缺页的指令

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

 当MMU得到物理地址之后,它将发送地址给缓存,缓存从物理地址中抽取出缓存偏移CO,缓存组索引CI和缓存标记CT。利用组索引与缓存标记,缓存能检测是否命中一个目标块。若命中则直接根据偏移量选取相应读取数据,最终该数据将返回CPU;否则依次去下一级缓存中查找,命中后将所需要的数据向上传给CPU,同时更新各级缓存。在更新各级缓存时,如果有空闲块则将目标块放置到空闲块中,否则将对缓存中的某个块进行替换。

7.6 hello进程fork时的内存映射

当fork函数被当前进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的PID。为了给这个新进程创建虚拟内存,它创建了当前进程的mm_struct、区域结构和页表的原样副本。它将两个进程中的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。

当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面,因此,也就为每个进程保持了私有地址空间的抽象概念。

7.7 hello进程execve时的内存映射

execve 函数在当前进程中加载并运行包含在可执行目标文件a.out中的程序,用a.out程序有效地替代了当前程序。

Execve需要删除已存在的用户区域。删除当前进程虚拟地址的用户部分中的已存在的区域结构。映射私有区域。为新程序的代码、数据、bss 和栈区域创建新的区域结构。所有这些新的区域都是私有的、写时复制的。代码和数据区域被映射为a.out文件中的.text和.data区。bss区域是请求二进制零的,映射到匿名文件,其大小包含在a.out中。栈和堆区域也是请求二进制零的,初始长度为零。图9-31概括了私有区域的不同映射。

·映射共享区域。如果a.out程序与共享对象(或目标)链接,比如标准C库libc.so,那么这些对象都是动态链接到这个程序的,然后再映射到用户虚拟地址空间中的共享区域内。

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

1)处理器将虚拟地址发送给MMU

2-3)MMU使用内存中的页表生成PTE地址4)有效位为零,因此MMU触发缺页异常

5)缺页处理程序确定物理内存中牺牲页(若页面被修改,则换出到磁盘)6)缺页处理程序调入新的页面,并更新内存中的PTE

7)缺页处理程序返回到原来进程,再次执行导致缺页的指令

7.9动态存储分配管理

动态内存分配器用于维护进程的虚拟内存区域,即堆。堆被视为一组不同大小的块的集合,每个块是连续的虚拟内存片段,可以是已分配或空闲状态。已分配的块用于应用程序使用,而空闲块用于分配新的内存。空闲块保持空闲状态,直到被应用程序分配。已分配的块保持分配状态,直到被释放。在C语言中,使用malloc函数分配块,使用free函数释放块。

动态内存分配有如下几种方式:

隐式空闲链表:使用隐式空闲链表来维护空闲块的集合。每个空闲块包含一个头部,用于存储块是否已分配的信息,并可能包含一个脚部。有效载荷和额外填充组成中间部分。分配器可以通过遍历堆中的所有块来间接遍历空闲块集合。放置策略可以是首次适配、下次适配或最佳适配。

显式空闲链表:将空闲块组织成链表形式的数据结构。每个空闲块包含前驱和后继指针。链表可以按先进后出的顺序维护,使得最近释放的块放在链表的开始处。分配器可以检查最近使用的块。另一种方式是按地址顺序维护链表,确保每个块的地址小于其后继块的地址。释放时需要线性搜索来定位适当的前驱块。

分离链表:分离链表将空闲块按照大小分类到不同的等价类中。每个等价类中的块大小相同,因此可以通过地址判断块大小。分配可以在常数时间内完成。不需要合并操作,只需要查找适当的等价类。这种方式包括简单分离链表、分离适配和伙伴系统等几种实现方式。

7.10本章小结

本章讲述hello的存储管理。先从hello的存储器地址空间出发,回顾了线性地址、逻辑地址、虚拟地址和物理地址的概念,接着分别回顾了逻辑地址到线性地址的变换—段式管理和线性地址到物理地址地变换—页式管理。接着更进一步,了解了TLB块表和多级页表的相关概念,同时回顾了三级Cache下的物理内存访问。又对hello进程fork和execve时的内存映射进行了回顾。最后,了解了缺页故障与缺页中断处理,以及动态存储分配管理的基础知识。

(第7章 2分)

结论

用计算机系统的语言,逐条总结hello所经历的过程。

你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。

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


附件

Hello.c

hello.i(预处理生成的文本文件)

hello.s(编译后的汇编语言文件)

hello.o(可重定位目标文件)

hello(链接后的可执行目标文件)

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


参考文献

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

  1. 深入理解计算机系统原书第3版
  2. https://blog.csdn.net/yuzaipiaofei/article/details/51219847
  3. c语言编译预处理的作用,c语言编译预处理_陈允信的博客-CSDN博客
  4. https://blog.csdn.net/lqm1094583745/article/details/124675157?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168475840916800215063780%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=168475840916800215063780&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-124675157-null-null.142^v87^control_2,239^v2^insert_chatgpt&utm_term=%E5%93%88%E5%B7%A5%E5%A4%A7csapp%E5%A4%A7%E4%BD%9C%E4%B8%9A&spm=1018.2226.3001.4187

(参考文献0分,缺失 -1分)


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部