ICS2022大作业论文

计算机系统

大作业

题     目  程序人生-Hello’s P2P 

专     业       计算机科学与技术(1+x                

学  号       2021112064                

班  级        21W0312               

学     生        曹家炜             

指 导 教 师        史先俊               

计算机科学与技术学院

2022年11月

摘  要

   本文通过一个简单的hello.c 的程序来分析一个计算机对一段代码的详细处理过程,包括预处理、编译、汇编、链接阶段,进入到内存,各级cache,最后被回收的过程描述。完成本次大作业,可以将课堂上所学内容有条理地全部进行一遍实践操作,同时可以进一步理解计算机系统的分工与协作。

关键词:预处理,编译,汇编,链接,存储空间,进程管理,微壳系统;                           

目  录

第1章 概述............................................................................................................. - 4 -

1.1 Hello简介...................................................................................................... - 4 -

1.2 环境与工具..................................................................................................... - 4 -

1.3 中间结果......................................................................................................... - 4 -

1.4 本章小结......................................................................................................... - 4 -

第2章 预处理......................................................................................................... - 5 -

2.1 预处理的概念与作用..................................................................................... - 5 -

2.2在Ubuntu下预处理的命令.......................................................................... - 5 -

2.3 Hello的预处理结果解析.............................................................................. - 5 -

2.4 本章小结......................................................................................................... - 5 -

第3章 编译............................................................................................................. - 6 -

3.1 编译的概念与作用......................................................................................... - 6 -

3.2 在Ubuntu下编译的命令............................................................................. - 6 -

3.3 Hello的编译结果解析.................................................................................. - 6 -

3.4 本章小结......................................................................................................... - 6 -

第4章 汇编............................................................................................................. - 7 -

4.1 汇编的概念与作用......................................................................................... - 7 -

4.2 在Ubuntu下汇编的命令............................................................................. - 7 -

4.3 可重定位目标elf格式................................................................................. - 7 -

4.4 Hello.o的结果解析...................................................................................... - 7 -

4.5 本章小结......................................................................................................... - 7 -

第5章 链接............................................................................................................. - 8 -

5.1 链接的概念与作用......................................................................................... - 8 -

5.2 在Ubuntu下链接的命令............................................................................. - 8 -

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

5.4 hello的虚拟地址空间.................................................................................. - 8 -

5.5 链接的重定位过程分析................................................................................. - 8 -

5.6 hello的执行流程.......................................................................................... - 8 -

5.7 Hello的动态链接分析.................................................................................. - 8 -

5.8 本章小结......................................................................................................... - 9 -

第6章 hello进程管理................................................................................... - 10 -

6.1 进程的概念与作用....................................................................................... - 10 -

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

6.3 Hello的fork进程创建过程..................................................................... - 10 -

6.4 Hello的execve过程................................................................................. - 10 -

6.5 Hello的进程执行........................................................................................ - 10 -

6.6 hello的异常与信号处理............................................................................ - 10 -

6.7本章小结....................................................................................................... - 10 -

第7章 hello的存储管理................................................................................ - 11 -

7.1 hello的存储器地址空间............................................................................ - 11 -

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

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

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

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

7.6 hello进程fork时的内存映射.................................................................. - 11 -

7.7 hello进程execve时的内存映射.............................................................. - 11 -

7.8 缺页故障与缺页中断处理........................................................................... - 11 -

7.9动态存储分配管理....................................................................................... - 11 -

7.10本章小结..................................................................................................... - 12 -

第8章 hello的IO管理................................................................................. - 13 -

8.1 Linux的IO设备管理方法.......................................................................... - 13 -

8.2 简述Unix IO接口及其函数....................................................................... - 13 -

8.3 printf的实现分析........................................................................................ - 13 -

8.4 getchar的实现分析.................................................................................... - 13 -

8.5本章小结....................................................................................................... - 13 -

结论......................................................................................................................... - 14 -

附件......................................................................................................................... - 15 -

参考文献................................................................................................................. - 16 -

第1章 概述

1.1 Hello简介

Hello的P2P过程就是hello.c通过一系列操作,包括预处理,编译,汇编,链接,最后生成一个可执行文件的全部过程。

Hello.c的020就是指进程在内存中由0变为0的全部过程,首先程序依靠fork函数为整个程序在内存中申请一个进程,然后调用execve函数加载并执行hello程序,映射虚拟内存,然后加载物理内存执行main函数代码。在执行过程中,CPU为为进程分配时间片,管理逻辑控制流。当整个程序运行完毕,shell父进程回收子进程,内核删除相关数据。

1.2 环境与工具

硬件环境:Intel X86-64 CPU 3.00GHZ; 16G RAM; 512 G SSD

软件环境:Windows 64位; VMware 15 PRO; Ubuntu 20.04 LTS 64位

开发工具:Visual Studio 2022;CodeBlocks 64位;EDB;GDB;OBJDUMP;GCC等

1.3 中间结果

hello.i  hello.c的ASCII码的中间文件

hello.s  ASCII的汇编语言文件

hello.o  可重定位目标文件

hello    可执行目标文件

hello_o.elf  可重定位目标文件的elf头文件

hello.elf    可执行目标文件的elf头文件

helloo_obj.txt  可执行目标文件的反汇编文件

hello_obj.txt  可重定位目标文件的反汇编文件

1.4 本章小结

本章简述了本次大作业的主要内容,开发环境(包括软硬件),并总结了所有的中间结果。

第2章 预处理

2.1 预处理的概念与作用

概念:预处理又称预编译,(对于c/c++来说)预处理指的是在程序编译之前,根据以字符#开头的命令(即头文件/define/ifdef之类的),修改原始的c程序,正如我们hello.c文件中的三个头文件,#include<stdio.h>命令告诉预处理器读取系统头文件stdio.h中的内容,并把它插入到程序之中。经过预处理我们会得到一个.i结尾的预处理文件。

作用:扩展源代码,插入所有用#include命令指定的文件,并扩展所有用#define声明指定的宏。这样做的目的是方便编译器在对程序进行翻译的时候更加方便。

2.2在Ubuntu下预处理的命令

2.3 Hello的预处理结果解析

 hello.i文件中插入了所有用#include命令指定的文件,包括stdlib.h、stdio.h、unistd.h三个文件。其次,删除了注释等信息。

2.4 本章小结

本站介绍了对.c文件的第一步处理——预处理。预处理将文件中的#开头字符串全部替换,为后面的处理做准备。

第3章 编译

3.1 编译的概念与作用

概念:编译器首先检查代码是否有错误,确定代码接下来要做的工作,检查无误后,编译器将hello.i翻译成hello.s。该程序包含函数main的定义,以.s这种文本格式输出源程序编译后产生的汇编代码。

作用:检查代码中的错误,将代码翻译成更接近于机器代码的汇编语言。

3.2 在Ubuntu下编译的命令

3.3 Hello的编译结果解析

       3.3.1数据

  1. int型数据:i,argc

i为局部变量,存储在栈中

argc存放在栈中-20(%rbp)中

  1. 字符串

字符串有两个,都是全局变量,存放在.rodata段。第一个字符串为“用法: Hello 学号 姓名 秒数!\n”汉字在utf-8中占3个字节

  1. 数组:argv[]

argv[]为一个字符串的指针数组,存放函数执行的命令行参数存放在栈中。

  1. 常数

源代码中的常数均以立即数形式给出。

例如:等等。

       3.3.2操作

  1. 赋值操作

例如:等等,mov类指令操作的语句。

  1. 加法操作:将后面寄存器或者栈中的数加上前面的立即数或者寄存器或者栈中的数,将结果保存在后面的寄存器或者栈中。本程序中用于通过指针找数组中的元素,和对于循环变量的每次加一操作。

 

  1. 减法操作:将后面寄存器或者栈中的数减去前面的立即数或者寄存器或者栈中的数,将结果保存在后面的寄存器或者栈中。本程序中用于栈顶减去一定的大小来为数据开辟空间存放在栈中。

  1. 加载有效地址:将.LC0.LC1的有效地址传送给%rdi

 

  1. 比较操作:用于比较argc与4的大小,判断程序是否退出。

同样用于循环变量与8的大小比较,判断循环是否结束:

  1. 数组/指针操作:源代码中只有一个数组char *argv[]。所以在hello.s程序中,argv[]作为唯一一个数组,被存储在栈中,取用其中信息时利用%rbp加偏移量的方法来进行。

  1. 控制转移类操作:条件转移类指令上一行进行比较等操作,然后修改标志位,控制转移类指令根据标志位判断是否跳转。

  1. 函数操作
  1. Main函数
  1. 传递控制:main函数被系统函数_libc_start_main调用 执行(call main)。、
  2. 传递数据:main函数的两个参数分别是argc和argv, 分别使用%rdi和%rsi来存储。函数的返回值用%eax来存储。
  3. 分配和释放内存:在调用main函数的时候,用%rbp记录栈帧起始位置,函数在栈中分配的空间在%rbp之上。 当程序调用结束时,用leave指令  ,leave指令将栈空间恢复为初始状态,之后ret返回。

  1. Puts函数
  1. 传递数据:一个参数,一个整型返回值
  2. 传递控制:call puts@PLT
  1. Exit函数
  1. 传递数据:一个参数存放在%edi;正确返回0,错误返回非0.
  2. 传递控制:call exit@PLT
  1. Printf函数
  1. 传递数据:printf设置%rdi 为“Hello %s %s\n”的首地址,设置%rsi为 argv[1],%rdx为argv[2]。
  2. 传递控制:call printf@PLT
  1. Sleep函数
  1. 传递数据:一个存放在%edi的参数。
  2. 传递控制:call sleep@PLT
  1. Atoi函数
  1. 传递数据:一个存放在%edi的参数,返回一个整型值。
  2. 控制传递:call atoi@PLT
  1. Getchar函数
  1. 传递数据:无输入参数;返回用户输入的第一个字符的 ASCII码,出错返回-1。
  2. 传递控制:call getchar@PLT

3.4 本章小结

本章列举了汇编代码中几种常见的的数据类型以及操作指令,并对hello.c进行了详细的分析。

第4章 汇编

4.1 汇编的概念与作用

  1. 概念:把汇编语言翻译成机器语言的过程称为汇编,并将这些指令打包成一种叫做可重定位目标程序,并将这个结果保留在(xxx.o)中。这里的xxx.o是二进制文件。汇编过程的作用是将汇编指令转换成一条条机器可以直接读取分析的机器指令。
  2. 作用:通过汇编,将汇编语言转换为完完全全的机器代码,供给计算机读取。

4.2 在Ubuntu下汇编的命令

4.3 可重定位目标elf格式

  1. ELF头:ELF头(ELF header)以一个16字节的序列开始,这个序列描述了生成该文件的系统的大小和字节顺序。ELF头剩下的部分包含帮助链接器语法分析和解释目标文件的信息。其中包括ELF头的大小、目标文件的类型(如可重定位、可执行或者共享的)、机器类型(如x86-64)、节头部表(section header table)的文件偏移,以及节头部表中条目的大小和数量。

  1. 节头部表:节头部表描述了不同节的位置和大小,其中目标文件中每个节都有一个固定大小的条目。

  1. .rel.text节:一个.text节中位置的列表,包含.text节中需要进行重定位的信息,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。一般而言,任何调用外部函数或者引用全局变量的指令都需要修改。另一方面,调用本地函数的指令则不需要修改。这个elf中的重定位节中有8条重定位信息,分别为对第一个字符串内容(.L0)、puts函数、exit函数、第二个字符串内容(.L1)、printf函数、atoi函数、sleep函数、getchar函数的重定位声明。

  1. symtab节:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。每个可重定位目标文件在.symtab中都有一张符号表。

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

4.4 Hello.o的结果解析

Hello.o的反汇编代码和hello.s的内容差距不大,主要差距体现在操作数,分支转移,函数调用和全局变量访问上。

  1. 操作数:hello.s中的操作数为十进制数,而反汇编代码中的操作数为十六进制数。
  2. 分支转移:在hello.s中分支转移使用.L2的标识符来标记,在反汇编代码中跳转的目标地址则是使用主函数加偏移量来表示。
  3. 函数调用:在hello.s中,函数调用用函数名加@PLT,在反汇编代码中,函数调用的目标地址是使用主函数加偏移量来表示。这是因为hello.c中的函数都是共享库中的函数,最终都需要动态链接器进一步来确定函数运行时地址。在汇编时,由于函数地址是不确定的,因此设置为全零,然后在.rel.text节中增加一个可重定位条目。
  4. 全局变量的访问:在hello.s中,通过.LC0等标识来访问全局变量,在反汇编代码中,对于全局变量的访问是通过栈指针。

4.5 本章小结

本章查看了可重定位目标文件的ELF格式,并对各个可重定位条目进行了分析,最后区分了hello.s文件和反汇编代码的相同点和不同点。

第5章 链接

5.1 链接的概念与作用

  1. 概念:链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可以被加载到内存并执行。
  2. 作用:把可重定位目标文件和命令行参数作为输入,产生一个完全链接的,可以加载运行的可执行目标文件。

5.2 在Ubuntu下链接的命令

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

   

  1. ELF头可执行目标文件hello的格式类似于可重定位目标文件的格式,ELF头描述文件的总体格式。它还包括程序的入口点,也就是当程序运行时要执行的第一条指令的地址。.text、.rodata和.data节与可重定位目标文件中的节是类似的,除了这些节已经被重定位到它们最终的运行时的内存地址外。.init节定义了一个小函数_init,程序初始化代码会调用它。因为可执行文件时完全连接的(已被重定位),所以无.rel节。

  1. 节头部表:节头部表描述了不同节的大小和数量,它对hello中所有节的信息进行了说明,包括名称、大小、类型、在程序中的偏移量等等。由于hello为完全链接的程序,因此根据给出的信息即可确定程序实际加载到虚拟地址的地址位置。

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

5.4 hello的虚拟地址空间

与5.3对照分析可知二者具有一一对应关系。 根据对应关系,可以知道每个函数,每条命令对应位置都存放了哪些内容。

5.5 链接的重定位过程分析

Hello与hello.o的不同:

  1. 节数:hello与hello.o比较增加了很多节
  2. 函数个数:hello同样也增加了很多从外部库链接进来的函数

重定位的实现依靠.rodata这个段,这个段保留重定位所需的信息,叫做重定位条目,链接器解析重定条目时发现两个类型为R_X86_64_PC32的对.rodata的重定位(printf中的两个字符串),.rodata与.text节之间的相对距离确定,因此链接器直接修改call之后的值为目标地址与下一条指令的地址之差,指向相应的字符串。这一部分教材上有详细介绍。

  1. 函数调用:hello中已经对函数完成了重定位,函数跳转目的地址已经明确,而hello.o中函数调用的目标地址不明确,对于这些地址不确定的函数调用,将其调用指令后的相对地址设置为全零(目标地址正好是下一条指令),然后在.rela.text部分为其添加一个重定位条目,等待静态链接的进一步确定。
  2. 虚拟内存地址:hello每行代码前的地址为虚拟内存地址,而hello.o中的都是相对偏移地址。

objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。

结合hello.o的重定位项目,分析hello中对其怎么重定位的。

5.6 hello的执行流程

ld-2.27.so! dl_start

0x7ffee5aca680

ld-2.27.so! dl_init

0x7f9f48629630

hello!_start

0x0x400500

ld-2.27.so!_libc_start_main

0x7f9f48249ab0

libc-2.27.so! cxa_atexit

0x7f4523fd6af7

libc-2.27.so! lll_look_wait_private

0x7f4523ff8471

libc-2.27.so!_new_exitfn

0x7f87ff534220

hello!_libc_csu_init

0x7f87ff512b26

libc-2.27.so!_setjmp

0x7f87ff512b4a

libc-2.27.so!_sigsetjmp

0x7f87ff52fc12

libc-2.27.so!__sigjmp_save

0x7f87ff52fbc3

hello_main

0x400532

hello!puts@plt

0x4004b0

hello!exit@plt

0x4004e0

hello!printf@plt

0x400587

hello!sleep@plt

0x400594

hello!getchar@plt

0x4005a3

dl_runtime_resolve

0x7f169ad84750

libc-2.27.so!exit

0x7fce8c889128

5.7 Hello的动态链接分析

    

5.8 本章小结

本章主要介绍了链接器如何将hello.o可重定向文件与动态库函数链接起来,其中用到了rodata中的重定位条目,最终分析了程序如何实现的动态库链接。

第6章 hello进程管理

6.1 进程的概念与作用

  1. 概念:进程的经典定义是一个执行中程序的实例,系统的每个程序都运行在某个进程的上下文。上下文是由程序正确运行所需的状态组成的,这个状态包括存放在内存里的程序的代码和数据,它的栈,通用目的寄存器的内容,程序计数器,环境变量以及打开文件描述符的集合。
  2. 作用:通过进程,我们会得到一种假象,好像我们的程序是当前唯一运行的程序,我们的程序独占处理器和内存,我们程序的代码和数据好像是系统内存中唯一的对象。

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

    1. 概念:shell俗称壳,它是指UNIX系统下的一个命令解析器;主要用于用户和系统的交互。UNIX系统上有很多种Shell。首个shell,即Bourne Shell,于1978年在V7(AT&T的第7版)UNIX上推出。后来,又演变出C shell、bash等不同版本的shell。
      bash,全称为Bourne-Again Shell。它是一个为GNU项目编写的Unix shell。bash脚本功能非常强大,尤其是在处理自动循环或大的任务方面可节省大量的时间。bash是许多Linux平台的内定Shell。
    2. 处理流程:
  1. shell从终端读取用户输入的命令。
  2. 根据空间将输入字符串划分为一个小字符串,以获取所有参数
  3. Shell判断:如果用户输入内置命令,将立即执行
  4. 如果是外部命令,shell将调用相应的程序,为其分配子进程并运行它
  5. 在程序执行期间,外壳应接受键盘输入信号并相应地处理这些信号

6.3 Hello的fork进程创建过程

shell首先会调用 fork 函数创建一个新的运行的子进程,这个子进程就是当前shell的一个副本。新创建的子进程几乎但不完全与父进程相同,子进程得到与父进程用户级虚拟地址空间相同的(但是独立的)一份副本,这就意味着,当父进程调用 fork 时,子进程可以读写父进程中打开的任何文件。父进程与子进程之间最大的区别在于它们拥有不同的 PID。

6.4 Hello的execve过程

execve函数在当前进程的上下文中加载并运行新程序。它覆盖当前进程的地址空间,但不创建新进程。新程序仍然具有相同的PID,并继承调用execve函数时打开的所有文件描述符。execve函数加载并运行带有参数列表argv和环境变量list ENVP的可执行对象文件hello。只有在发生错误(例如,找不到Hello)时,Execve才会返回调用方。

6.5 Hello的进程执行

一开始,hello运行在用户模式,当程序收到一个信号(可能是用户的键盘输入或者硬件中断等)时,进入内核模式,运行信号处理程序,之后再返回用户模式。在hello运行的过程中,cpu不断切换上下文,使hello程序运行过程被切分成时间片,与其他进程交替占用cpu,实现进程的调度。

    1. hello的异常与信号处理
  1. Ctrl+Z:程序挂起,处理过程是向程序发送SIGSTP信号,程序执行默认行为:挂起程序,之后会返回shell中。

  1. Ctrl+c:程序终止,处理过程是向程序发送SIG_INT信号,程序执行默认行为:终止前台进程。

  1. 乱按:会将输入的字符串显示在两组本应输出的Hello字符串之间,并不会有其他变化。

  1. Ps:显示当前进程的状态

  1. Jobs:查看后台运行的进程

  1. Pstree:显示进程树

  1. Kill:结束一个进程
  2. Fg:恢复一个后台进程

6.7本章小

本章分析了进程管理,理解了fork函数以及execve函数在进程执行过程中的作用。同时通过运行进程时输入的各种命令——ps、pstree、crtl+c、crtl+z、kill、fg等等了解了进程管理。

第7章 hello的存储管理

7.1 hello的存储器地址空间

  1. 逻辑地址:又称相对地址,是程序运行由CPU产生的与段相关的偏移地址部分。他是描述一个程序运行段的地址。
  2. 物理地址:程序运行时加载到内存地址寄存器中的地址,内存单元的真正地址。他是在前端总线上传输的而且是唯一的。在hello程序中,他就表示了这个程序运行时的一条确切的指令在内存地址上的具体哪一块进行执行。
  3. 线性地址:这个和虚拟地址是同一个东西,是经过段机制转化之后用于描述程序分页信息的地址。他是对程序运行区块的一个抽象映射。以hello做例子的话,他就是一个描述:“我这个hello程序应该在内存的哪些块上运行。”

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

  Intel使用段式管理处理逻辑地址到线性地址的变换。

  逻辑地址=段选择符+逻辑地址偏移量。Intel设置了四类段寄存器:SS(栈段寄存器)、ES/GS/FS(辅助段寄存器)、DS(数据段寄存器)、CS(代码段寄存器)用于存放段选择符。段选择符由三个字段组成:索引、TIRPLTI决定描述符表的类型,RPL决定进程的状态转换,索引用来确定当前使用的段描述符在描述符表中的位置。Intel处理器中,通过段选择符查表获得段基址、段限、存取权限信息。通过基址寄存器、变址寄存器、比例因子和逻辑地址的偏移量可以计算出一个有效地址EA,再加上段基址得到线性地址。

线性地址到主存地址的转换可以通过分页完成。

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

计算机利用页表,通过MMU来完成从虚拟地址到物理地址的转换。

PTBR是cpu里的一个控制寄存器,指向当前页表,n位的虚拟地址包括p位的虚拟页面偏移VPO和n-p位的虚拟页号VPN。MMU通过VPN来选择适当的PTE,将页表条目中的PPN(物理页号)和虚拟地址的VPO串联起来,就得到相应的物理地址。

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

每次cpu产生一个虚拟地址,MMU需要查询一个PTE,如果运气不好,需要从内存中取得,这需要花费很多时间,通过TLB(翻译后备缓冲器)能够消除这些开销。TLB是一个小的,虚拟寻址的缓存,在MMU里,其每一行都保存着一个单个PTE组成的块,TLB通常具有高度相联度。用于组选择和行匹配的索引和标记字段是从虚拟地址中的虚拟页号中提取出来的。

如果是32位系统,我们有一个32位地址空间,4KB的页面和一个4字节的PTE,我们总需要一个4MB的页表驻留在内存中,而对于64位系统,我们甚至需要8PB的空间来存放页表,这显然是不现实的。用来压缩页表的常见方式就是使用层次结构的页表。现在的64位计算机采用4级页表,36位的VPN被封为4个9位的片,每个片被用作一个页面的偏移,CR3寄存器包含L1页表的物理地址。VPN1提供到一个L1PET的偏移量,这个PTE包含L2页表的基地址,VPN2提供一个到L2PTE的偏移量,以此类推。

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

当我们通过MMU得到了物理地址后,我们就需要去内存里去相应的数据,当从内存直接取数据速度太慢,计算机利用cache(高度缓存)来加快访存速度。它位于CPU与内存之间,访问速度比内存块很多,需要从内存里取数据时,先考虑是否在cache里有缓存。下图是一个典型的cache结构。

把物理地址分割成这些部分,其中S = 2^s,B = 2^b,剩下的t位都是标记位,得到一个物理地址后,通过组索引部分可以确定在cache里的哪一组,通过标记位确定看是否与组里的某一行标记相同,如果有,通过块偏移位确定具体是哪个数据块,从而得到我们的数据。如果没有找到,则需要从内存里去数据,并找到cache里的一行替换,对于L1,L2这样的组相联cache,替换策略通常有LFU(最不常使用),LRU(最近最少使用)。

7.6 hello进程fork时的内存映射

当fork函数被shell调用时,内核会为hello进程创建各种数据结构并分配给hello唯一的PID。为了给hello创建虚拟内存,内核创建了当前进程的mm_struct、区域结构和样表的原样副本,并将两个进程中的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为写时复制。从上述过程可以看出,在调用fork后和调用execve之前,hello所在进程的内存空间情况和父进程完全一致。

7.7 hello进程execve时的内存映射

  1. 删除已存在的用户区域。删除当前进程虚拟地址的用户部分中的已存在的区域结构。
  2. 映射私有区域。为新程序的代码、数据、bss和栈区创建新的区域结构
  3. 映射共享区域。
  4. 设置程序计数器,使之指向代码区域的入口点。

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

物理内存缓存不命中称为缺页。假设CPU引用了磁盘上的一个字,而这个字所属的虚拟页并没有缓存在DRAM中。地址翻译硬件会从内存中读取虚拟页对应的页表,说明这个虚拟页没有被缓存,触发一个缺页故障。

如果程序执行过程中发生了缺页故障,则内核调用缺页处理程序。处理程序执行如下步骤:

  1. 检查虚拟地址是否合法,如果不合法则触发一个段错误,程序终止。
  2. 检查进程是否有读、写或执行该区域页面的权限,如果不具有则触发保护异常,程序终止。
  3. 两步检查都无误后,内核选择一个牺牲页面,如果该页面被修改过则将其交换出去,换入新的页面并更新页表。然后将控制转移给hello进程,再次执行触发缺页故障的指令。

7.9动态存储分配管理

 基本方法:维护一个虚拟内存区域“堆”,将堆视为一组不同大小的 块(blocks)的集合来维护,每个块要么是已分配的,要么是空闲的,需要时选择一个合适的内存块进行分配。

  1. 记录空闲块,可以选择隐式空闲链表,显示空闲链表,分离的空闲链表和按块大小排序建立平衡树
  2. 放置策略,可以选择首次适配,下一次适配,最佳适配
  3. 合并策略,可以选择立即合并,延迟合并
  4. 需要考虑分割空闲块的时机,对内部碎片的忍耐阈值。

7.10本章小结

本章介绍了hello的存储器地址空间、Intel逻辑地址到线性地址的变换-段式管理、Hello的线性地址到物理地址的变换-页式管理、TLB与四级页表支持下的VA到PA的变换、三级Cache支持下的物理内存访问、hello进程fork时的内存映射、hello进程execve时的内存映射,以及缺页故障与缺页中断处理和动态存储分配管理。

结论

Hello的一生经历了这些过程:

      1. 编写:在编译器中用C语言完成hello.c的编写;
      2. 预处理:预处理器将hello.c预处理成为hello.i;
      3. 编译:编译器将hello.i文件翻译为汇编文件hello.s。
      4. 汇编:汇编器将hello.s文件翻译为二进制机器语言,生成可重定位目标文件hello.o。
      5. 链接:链接器将可重定位目标文件hello.o和其他目标文件链接成为可执行文件hello。
      6. 创建子进程:用户在shell中输入执行hello的命令后,shell通过fork创建进程。
      7. 加载: shell调用execve,在上下文中加载可执行程序hello,开始执行hello。
      8. 执行:hello程序运行,在此过程中,可能产生异常与信号,例如用户键盘输入ctrl+z,ctrl+c等,需要调用信号处理程序进行处理。在执行程序的过程中,hello还将利用各种复杂的机制进行内存访问。
      9. 访问内存:通过MMU将需要访问的虚拟地址转化为物理地址,并通过缓存系统访问内存。
      10. 动态申请内存:hello运行过程中可能会通过malloc函数动态申请堆中的内存。
      11. 异常:hello运行过程中可能会产生各种异常和信号,系统会针对出现的异常和收到的信号做出反应。
      12. 终止:hello运行结束或收到信号后终止,父进程结束并回收子进程,内核删除相关数据。

附件

Hello  可执行目标文件

Hello.c   源代码

Hello.i    中间文件、

Hello.s    汇编语言文件

Hello.o    可重定位目标文件

hello1.elf   可执行目标文件elf文件

hello.elf    可重定位目标文件的elf文件

helloo_obj.txt  可执行目标文件的反汇编文件

hello_obj.txt  可重定位目标文件的反汇编文件

参考文

[1] 百度百科https://baike.baidu.com/

[2]计算机系统课程以及实验文档ppt

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值