程序人生-Hello’s P2P

计算机系统

大作业

摘  要

本文从hello.c程序入手开始逐步分析,展示经预处理、编译、汇编和链接生成可执行文件的全过程,阐述计算机系统对hello程序的进程管理和存储管理,展示hello程序在Linux系统中的完整生命周期。在虚拟机Ubuntu中,将采用gcc、edb等工具进行探索,从而加深读者对于计算机系统的理解。

关键词:hello程序;预处理;编译;汇编;链接;进程管理;存储管理                          

目  录

第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的编译结果解析... - 9 -

3.4 本章小结... - 12 -

第4章 汇编... - 13 -

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

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

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

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

4.5 本章小结... - 17 -

第5章 链接... - 18 -

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

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

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

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

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

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

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

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

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

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

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

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

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

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

6.7本章小结... - 30 -

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

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

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

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

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

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

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

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

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

7.9本章小结... - 34 -

结论... - 35 -

附件... - 36 -

参考文献... - 37 -

第1章 概述

1.1 Hello简介

P2P(From Program to Process):从Program开始,hello.c经过预处理、编译、汇编和链接四个过程生成可执行文件。预处理阶段使用预处理器cpp,根据以#开头的代码修改原始程序,读取头文件,将其中的内容加入源程序得到另外一个c程序hello.i(仍然是一个文本文件)。然后通过编译器编译(词法分析语法分析,语义分析,中间代码生成优化等)生成hello.s。然后通过汇编器(as)进行汇编,根据指令集将汇编程序hello.s翻译为机器指令,并且把这一系列机器指令按照固定的规则进行打包得到可重定位的目标文件hello.o(二进制)。最后链接器进行链接得到可执行文件hello(二进制),在linux环境下使用指令./hello启动程序,shell调用函数fork()产生子进程hello就完成了从Program到Progress的过程。

020(From Zero-0 to Zero-0):开始时内存中并没有hello程序,即从0开始,在运行结束之后,hello进程回收,内核数据也删除,最终将hello在内存中存在的痕迹全部去掉,即以0结束,故为020。

1.2 环境与工具

硬件环境:X64 CPU 3.2GHz,64GB RAM,1TB SSD。

软件环境:Windows 11 64位;VMware Workstation 17;Ubuntu 22.04 LTS 64位。

开发工具:Visual Studio 2022;Codeblocks;vim;gcc;edb。

1.3 中间结果

文件名

作用

hello.c

源文件

hello.i

预处理后文件

hello.s

编译后文件

hello.o

可重定位目标文件

hello

可执行目标文件

hello.elf

hello.o的elf文件

hello2.elf

hello的elf文件

hello.fan

hello.o反汇编得到文件

hello2.fan

hello反汇编得到文件

1.4 本章小结

本章简要介绍了P2P和020的过程,介绍了完成本文过程中使用的环境和开发工具,并给出了hello程序在生命周期中产生的中间文件,初步介绍了hello程序的一生。

第2章 预处理

2.1 预处理的概念与作用

概念:预处理是指预处理器根据以字符#开头的命令(包括宏定义、条件编译和源文件包含等),修改原始的C程序,插入指定文件,扩展指定的宏,得到一个完整的文本文件。

作用:预处理读取预处理命令,根据不同的命令对源文件进行处理,得到新的文本代码,同时还会删除程序中的注释和多余的空白字符。预处理将源文件中以“include”格式包含的文件复制到编译的源文件中;用实际值替换用“#define”定义的字符串,进行宏替换;根据“#if”等后面的条件决定需要编译的代码。

2.2在Ubuntu下预处理的命令

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

图1 预处理过程

2.3 Hello的预处理结果解析

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

图3 hello.i文件(末尾部分)

可以看到,在hello.i文件中仍有hello.c的原内容,是C语言的程序文本。同时,将源文件中的注释删除了。除此之外,hello.i文件中增加了三个头文件(stdio.h,unistd.h,stdlib.h)的源代码,而主函数main并未发生变化。

2.4 本章小结

本章介绍了预处理的概念及作用,并演示了在Ubuntu虚拟机下对hello.c进行预处理的过程,对预处理结果hello.i进行了分析。

第3章 编译

3.1 编译的概念与作用

概念:编译是指编译器将预处理后得到的.i文件(文本文件)处理成为.s文件(文本文件),它包含一个汇编语言程序。

作用:编译将高级语言转为较低级的汇编语言。分为五个阶段:词法分析、语法分析、语义检查和中间代码生成、代码优化、目标代码生成。

1词法分析:词法分析的任务是对由字符组成的单词进行处理,从左至右逐个字符地对源程序进行扫描,产生一个个的单词符号,把作为字符串的源程序改造成为单词符号串的中间程序。执行词法分析的程序称为词法分析程序或扫描器。

2语法分析:以单词符号作为输入,分析单词符号串是否形成符合语法规则的语法单位,如表达式、赋值、循环等,最后看是否构成一个符合要求的程序,按该语言使用的语法规则分析检查每条语句是否有正确的逻辑结构,程序是最终的一个语法单位。编译程序的语法规则可用上下文无关文法来刻画。

3中间代码:源程序的一种内部表示,或称中间语言。中间代码的作用是可使  编译程序的结构在逻辑上更为简单明确,特别是可使目标代码的优化比较    容易实现中间代码。

4代码优化:代码优化是指对程序进行多种等价变换,使得从变换后的程序出发,能生成更有效的目标代码。所谓等价,是指不改变程序的运行结果。所谓有效,主要指目标代码运行时间较短,以及占用的存储空间较小。这种变换称为优化。

5目标代码生成:目标代码生成器把语法分析后或优化后的中间代码经汇编程序汇编生成汇编语言代码,成为可执行的机器语言代码。

3.2 在Ubuntu下编译的命令

编译命令:gcc -S hello.i hello.s

图4编译命令

3.3 Hello的编译结果解析

图5 hello.s文件(部分)

      1. 数据及赋值操作

赋值操作利用mov,主要应用后缀为l与q,l表示四字节,q表示八字节。全局变量main,类型function,保存在.text。

图6 全局变量main

字符串以string标注。

 

图7 字符串

局部变量int i:movl $0,-4(%rbp)指将0赋给局部变量i,将i保存在地址%rbp-4处。

图8 局部变量i

数组*argv[],整型数据argc:movq %rsi ,-32(%rbp),将argv首地址保存在%rbp-32处,movl %edi ,-20(%rbp),将argc保存在%rbp-20处。

                                                                             

图9 char *argv[]和int argc

      1. 算数操作

(1)加法:add a,b等价于b=a+b。hello中主要应用如下:

 

图10 加法操作

(2)减法:sub a,b等价于b=b-a。hello中主要应用如下

图11 减法操作

(3)地址之间的赋值leaq a,b等价于b=&a,具体应用如下:

 

图12 地址赋值

      1. 关系操作及控制转移

关系操作指cmp a,b,即比较a与b的大小,通常与控制转移语句一起使用。hello中出现的控制转移语句有je,a和b相等则跳转;jle,b小于等于a则跳转;以及jmp,无条件跳转,具体如下:

(1)je:比较argc和4,等于跳转到L2

图13 je跳转

(2)jle:比较i和8,i<=8则跳转到L4

图14 jle跳转

(3)jmp:直接跳转到L3

图15 jmp跳转

      1. 函数操作

调用函数时用指令call,hello中一共调用了以下函数:

  1. puts

图16 puts函数

  1. exit

图17 exit函数

  1. printf

图18 printf函数

  1. atoi

图19 atoi函数

  1. sleep

图20 sleep函数

  1. getchar

图21 getchar函数

3.4 本章小结

本章介绍了编译的概念及作用,演示了在Ubuntu虚拟机下对hello.i进行编译得到hello.s的过程,并分析了编译结果。在结果分析部分,对hello.c中存在的数据及赋值操作、算术操作、关系操作、控制转移和函数操作进行了具体的分析,详细解读了hello的编译结果。

第4章 汇编

4.1 汇编的概念与作用

概念:汇编是指将.s文件进行翻译的过程。汇编器(as)将.s文件翻译成.o文件,即从编译后的文件到生成机器语言二进制程序的过程。

作用:将汇编代码翻译成机器语言,使得计算机能够直接识别和执行。

4.2 在Ubuntu下汇编的命令

汇编命令:gcc -c hello.s -o hello.o

图22 汇编命令

4.3 可重定位目标elf格式

命令:readelf -a hello.o >hello.elf

图23 生成elf文件

对生成的elf文件分析如下:

  1. elf头

图24 elf头

首先以一个16字节序列开始,描述了生成该文件的系统的字大小和字节顺序。剩下的部分包含帮助链接器进行语法分析和解释目标文件的信息,其中包括ELF头的大小、目标文件的类型(可重定位、可执行、共享)、机器类型(X86-64等,本例中为虚拟机unix)、节头部表的文件偏移以及节头部表中条目的大小和数量。

  1. 节头

图25 节头

包含各节的名称、大小、类型、地址和偏移量。

  1. 重定位节.rela.text

图26 .rela.text

节中包含了该程序所调用的函数puts、exit、printf、atoi、sleep、getchar,另外还包含着两处字符串(L0,L1)(用.rodata表示),这一节中写明了它们的名称、信息、寻址类型(绝对寻址、PC相对寻址)、偏移量以及符号值。

  1. 重定位节.rela.eh_frame

图27 .rala.eh_frame

.rela.text节使用了绝对寻址,而.rela.eh_frame节使用了PC相对寻址。

  1. 符号表

图28 符号表

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

4.4 Hello.o的结果解析

反汇编命令:objdump -d -r hello.o > hello.fan

图29 反汇编命令

图30 反汇编文件

比较hello.fan与hello.s文件,有如下几处不同:

  1. 调用函数时

在hello.s文件中,直接利用语句call后面加函数名进行调用。

图31 hello.s文件中调用函数

而在hello.fan中,则利用重定位符号引用的方式调用函数,需要与动态链接器作用才能确定函数运行时地址。

图32 hello.fan文件中调用函数

  1. 跳转时

hello.s中jxx后直接跟所要跳转的段,如L1、L2等。

 

图33 hello.s文件中的跳转语句

hello.fan中jxx后跟所要跳转地址相对于main的偏移量。

 

图34 hello.fan中的跳转语句

  1. 访问全局变量时

hello.s中通过段地址加%rip完成。

图35 hello.s中全局变量访问

hello.fan中则通过0+%rip完成。

图36 hello.fan中全局变量访问

4.5 本章小结

本章介绍了汇编的概念及作用,分析了elf文件的各组成部分,对hello.o文件进行反汇编并与hello.s文件进行了比较。

第5章 链接

5.1 链接的概念与作用

概念:链接是指将一个或多个由编译器或汇编器生成的目标文件和库链接为一个可执行文件的过程,也就是从.o文件到可执行文件的过程。目标文件是包括机器码和链接器可用信息的程序模块。链接可以执行于编译时,也就是在源代码被翻译成机器代码时;也可以执行于加载时,也就是在程序被加载器加载到内存并执行时;也可以执行于运行时,也就是由应用程序来执行。

作用:链接使得分离编译成为可能,这样,不需要将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其它文件。

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

图37 链接命令

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

输入命令readelf -a hello >hello2.elf生成文件hello2.elf。

                                                                 图38 生成elf文件

对生成的elf文件分析如下:

  1. elf头

图39 elf头

与上一章hello.elf大同小异,分析详见上一章。可以看到,文件类型变为可执行文件。

  1. 节头

图40 节头

(3)程序头

图41 程序头

该节为可执行文件相比与可重定位文件新增的,包含了偏移量、虚拟地址及物理地址,根据查阅资料得知:

PHDR:指定程序头表在文件及程序内存映像中的位置和大小

INTERP:指定要作为解释程序调用的以空字符结尾的路径名的位置和大小。

LOAD:指定由p_fileszp_memsz描述的可装入段。

DYNAMIC:指定动态链接信息。

NOTE:指定辅助信息的位置和大小。

GNU_STACK:标志栈是否可执行

GNU_RELRO:重定位后需被设定为只读内存区域

(4)重定位节

  

图42 重定位节

     (5)符号表

图43 符号表

5.4 hello的虚拟地址空间

    图44 edb查看hello的虚拟地址空间

可以看到,在程序头表中描述的段的各类信息(类型、起始地址和大小等)均和Data Dump查看结果一致。实际上,所有段的虚拟地址信息都被正确而完整地记录在了程序头表中。

5.5 链接的重定位过程分析

输入命令objdump -d -r hello >hello2.fan将hello反汇编并保存在hello2.fan中。

图45 反汇编命令

不同之处:
    (1)增加了许多函数:在hello中多了许多函数如printf等,原因是链接时将库中所用代码加入hello。

 

图46 新增函数

(2)地址:hello.o中无确切地址,未进行重定位,而hello中已经有了确定的虚拟地址,说明已完成重定位。

图47 hello文件

图48 hello.o文件

5.6 hello的执行流程

子程序名

程序地址

<_init>

4004c0

<.plt>

4004e0

<puts@plt>

4004f0

<printf@plt>

400500

<getchar@plt>

400510

<atoi@plt>

400520

<exit@plt>

400530

<sleep@plt>

400540

<_start>

400550

<_dl_relocate_static_pie>

400580

<main>

400582

<__libc_csu_init>

400610

<__libc_csu_fini>

400680

<_fini>

400684

5.7 Hello的动态链接分析

dl_init前PIC函数调用的目标地址都实际指向PLT中的代码逻辑,GOT部分采取了延迟绑定,GOT保存下一命令的地址。dl_init执行后,重定位确定函数地址。

查看hello2.elf文件可以得到GOT的起始位置为0x403ff0。再用edb查看.got的内容。

图49 hello2.elf文件

图50 edb查看.got

5.8 本章小结

    本章介绍了链接的概念及作用,演示了在Ubuntu虚拟机下对hello.o进行链接得到hello的过程,并分析了链接结果。分析结果时用readelf查看了hello的ELF文件,从ELF头、节头部表和程序头部表的角度分别进行了分析,验证了段在虚拟地址空间的位置。同时也使用objdump查看了hello的反汇编代码,分析了hello的执行流程,并对hello进行了动态链接分析。

第6章 hello进程管理

6.1 进程的概念与作用

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

作用:使用户得到一种假象,就好像我们的程序是系统当前唯一运行的程序一样,我们的程序好像独占的使用处理器和内存。处理器就好像是无间断地一条一条执行我们程序中的指令,最后我们的代码和数据好像是系统内存中唯一的对象。

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

作用:shell是一个应用程序,是操作系统中用户与系统内核进行交互的界面。

处理流程:

1终端进程读取用户由键盘输入的命令行;

2分析命令行字符串,获取命令行参数,并构造传递给execve的argv向量;

3检查第一个命令行参数的是否是一个内置的shell命令。如果不是内部命令,调用fork创建新进程或子进程;

4在子进程中用步骤2获取的参数,调用execve()执行指定程序;

5如果用户没要求后台进行,则shell使用waipid等待作业终止后返回;如果用户要求后台运行,则shell返回。

6.3 Hello的fork进程创建过程

在终端中输入命令行,shell判断是否为内置命令。若不是,shell通过fork函数创建一个新的运行的子进程。新的子进程几乎与shell有完全相同的地址空间,这就意味着当父进程调用fork时,子进程可以读写父进程中打开的任何文件;但又不完全相同,最大的区别就是它们拥有不同的PID。

6.4 Hello的execve过程

子进程调用exceve函数在当前子进程的上下文加载并运行一个新的程序,此处即hello程序。exceve函数加载并运行可执行目标文件hello,且带参数列表argv和环境变量argc。加载器会删除现有的用户区域;映射私有空间,创建新的代码、数据、堆和栈区域,将代码段和数据段初始化为hello的代码和数据,堆和栈被置空;映射共享区域,实现动态链接;设置PC,将其指向hello程序的起始位置,即从下条指令开始执行hello程序。

6.5 Hello的进程执行

进程上下文:上下文就是内核重新启动一个被抢占的进程所需的状态,上下文切换机制是建立在较低层异常机制之上的。

进程时间片:时间片是指CPU分配给每个程序的运行时间,每个线程被分配一个时间段,作为它的时间片。给不同进程分配不同时间片就可以形成好像多个进程在同时进行的效果。

用户态与核心态:为了能让处理器安全运行,需要限制应用程序可执行指令所能访问的地址范围。因此划分了用户态与核心态。核心态可以说是拥有最高的访问权限,处理器以一个寄存器当做模式位来描述当前进程的特权。进程只有故障、中断或陷入系统调用时才会得到内核访问权限,其他情况下始终处于用户权限之中,保证了系统的安全性。

系统在执行进程时,会反复横跳,一段时间执行一个程序,每个程序只执行一段时间,系统以时间片轮转调度算法来实现进程之间的调度,这种系统以该方法实现“同时”执行多个程序的过程叫做并发。

6.6 hello的异常与信号处理

可能出现的异常一共有四种,即中断、陷阱、故障、终止。它们的产生原因及处理如下所示。

图51 异常类别、产生原因及处理

    发送信号:

(1)不停乱按

图52 不停乱按

    可以看到,乱按并未使程序停止运行。实际上,乱按的输入被当成命令存放在缓冲区中。

(2)按回车

图53 按回车

可以看到,输入回车也并未使程序运行收到影响,输入的回车也被加入到了缓冲区,在结束之后被命令行录入。

(3) 按Ctrl+C

图54 按Ctrl+C

可以看到,按下Ctrl+C后程序运行直接停止。接下来利用ps和jobs命令查看。

图55 利用ps和jobs命令查看

可以看到,ps无相应pid,jobs和fg无结果可知,说明hello进程已被结束。

(4) 按Ctrl+Z

图56按Ctrl+Z

图57 利用ps和jobs命令查看

可以看到,在输入Ctrl+Z之后,程序运行停止了,但经过对ps及jobs的查看可知,该进程时被挂起而未结束,输入fg后继续进行。

(5) kill

图58 kill

可以看到,利用kill可使进程终止。

利用pstree可查看所有指令。

图59 利用pstree查看

6.7本章小结

本章介绍了进程的概念与作用,介绍了 shell 的作用和处理流程,分析了调用 fork 创建新进程、调用 execve加载并执行hello的过程。还以乱按键盘、按回车、按Ctrl+C、按下Ctrl+Z和kill为例,分析了hello的异常与信号处理。

第7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:程序产生的和段相关的偏移地址,由一个标识符加上一个指定段内的相对地址的偏移量构成。

线性地址(虚拟地址):逻辑地址到物理地址变换之间的中间层。段中的偏移地址加上相应段的基地址就生成了一个线性地址。

物理地址:指出目前CPU外部地址总线上的寻址物理内存的地址信号,用于内存级芯片的单元寻址,是地址变换的最终结果地址。

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

逻辑地址由[段选择符:偏移量]组成,线性地址由段首地址与逻辑地址中的偏移量组成。其中,段首地址存放在段描述符中。而段描述符存放在描述符表中,也就是GDT(全局描述符表)或LDT(局部描述符表)中。

段选择符(16位)由三部分组成。高13位是索引号,可以在描述符表中找到对应的描述符;紧接着TI指示段描述符是在GDT还是LDT中;最低两位表示请求者特权级别。由此,便可以通过段选择符的指示在段描述符表中找到对应的段描述符,然后便可从段描述符中获得段首地址,将其与逻辑地址中的偏移量相加,就得到了线性地址。

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

系统将虚拟页作为进行数据传输的单元。虚拟内存分割被成为虚拟页,物理内存也被分割为物理页,大小和虚拟页相同。任意时刻虚拟页都被分为三个不相交的子集:未分配的(VM系统还未分配的页)、缓存的(当前已经缓存在物理内存的已分配页)、未缓存的(当前未缓存在物理内存的已分配页)。

每次将虚拟地址转换为物理地址,都会查询页表来判断一个虚拟页是否缓存在DRAM的某个地方,如果不在DRAM的某个地方,通过查询页表条目可以知道虚拟页在磁盘的位置。页表将虚拟页映射到物理页。

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

将VPN分为多级,在每一级都找到对应的位置。当到达最后一层时会得到 PPN,与 VPO 连接就得到了最终的物理地址。

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

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

将已得到的物理地址拆分为CT(标记)、CI(组索引)和CO(块偏移),在L1 Cache中依据组索引,找到对应的组,再依据标记查找是否存在并判断是否有效,最后根据块偏移找到块。如果上述条件均满足则命中,否则按顺序对L2 Cache、L3 Cache、内存进行相同操作,直到命中,然后向上级返回。如果有空闲块则将目标块放置到空闲块中,否则将缓存中的某个块驱逐,将目标块放到被驱逐块的位置。

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

7.6 hello进程fork时的内存映射

当fork 函数被shell调用时,内核为hello进程创建各种数据结构,并分配给它一个唯一的PID 。为了给hello进程创建虚拟内存,它创建了hello进程的mm_struct 、区域结构和页表的原样副本。它将两个进程中的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。当fork 在hello进程中返回时,hello进程现在的虚拟内存刚好和调用fork 时存在的虚拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面,因此,也就为每个进程保持了私有地址空间的抽象概念。

7.7 hello进程execve时的内存映射

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

1删除已存在的用户区域:删除当前进程虚拟地址的用户部分中的已存在的区域结构。

2映射私有区域:为新程序hello的代码、数据、bss 和栈区域创建新的区域结构。所有这些新的区域都是私有的、写时复制的。代码和数据区域被映射为hello文件中的.text和.data 区。bss 区域是请求二进制零的,映射到匿名文件,其大小包含在hello中。栈和堆区域也是请求二进制零的,初始长度为零。

3映射共享区域:如果hello程序与共享对象(或目标)链接,那么这些对象都是动态链接到这个程序的,然后再映射到用户虚拟地址空间中的共享区域内。

4设置当前进程上下文中的程序计数器,使之指向代码区域的入口点。下一次调度这个进程时,它将从这个入口点开始执行。

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

当虚拟地址在MMU中查找页表时发现对应的物理内存不在主存中,需要操作系统将其调入主存,这称为缺页故障。

1请求调页: 当进程调用malloc()之类的函数调用时,并未实际上分配物理内存,而是仅仅分配了一段线性地址空间,在实际访问该页框时才分配物理页框,这样可以节省物理内存的开销。在内存回收时,该物理页面的内容被写到了磁盘上,被系统回收了,这时候需要再分配页框,并且读取其保存的内容。

2写时复制:当fork一个进程时,子进程并未完整复制父进程的地址空间,而是共享相关的资源,父进程的页表被设为只读的,当子进程进行写操作时,会触发缺页异常,从而为子进程分配页框。

3地址范围外的错误:内核访问无效地址、用户态进程访问无效地址等。

4内核访问非连续性地址:用于内核的高端内存映射,高端内存映射仅仅修改了主内核页表的内容,当进程访问内核态时需要将该部分的页表内容复制到自己的进程页表里面。

5缺页中断处理:缺页处理程序是系统内核中的代码,选择一个牺牲页面,如果这个牺牲页面被修改过,那么就将它交换出去,换入新的页面并更新页表。当缺 页处理程序返回时,CPU重新启动引起缺页的指令,这条指令再次发送VA到 MMU,这次MMU就能正常翻译VA了。

7.9本章小结

本章介绍了存储器地址空间的概念,以及用段式管理和页式管理实现线性地址到物理地址的变换。同时,分析了TLB与四级页表支持下的VA到PA的变换、三级cache支持下的物理内存访问、fork和execve函数的内存映射、缺页故障和缺页中断处理。

结论

Hello的一生有如下过程:

1预处理:将hello.c外部库拓展到hello.i中;

2编译:根据hello.i文件进行编译形成汇编文件hello.s;

3汇编:经过汇编将汇编语言转化为机器语言存在二进制文件hello.o中;

4链接:将库中函数以某种方式合在hello.o中形成可执行目标文件;

5fork:调用fork创建进程;

6execve:调用execev进行加载;

7执行CPU为其分配资源;

8结束:子进程被父进程回收,资源空间被回收。

附件

文件名

作用

hello.c

源文件

hello.i

预处理后文件

hello.s

编译后文件

hello.o

可重定位目标文件

hello

可执行目标文件

hello.elf

hello.o的elf文件

hello2.elf

hello的elf文件

hello.fan

hello.o反汇编得到文件

hello2.fan

hello反汇编得到文件

参考文献

[1]  辛希孟. 信息技术与信息服务国际研讨会论文集:A集[C]. 北京:中国科学出版社,1999.

[2]  KANAMORI H. Shaking Without Quaking[J]. Science,1998,279(5359):2063-2064.

[3]  CHRISTINE M. Plant Physiology: Plant Biology in the Genome Era[J/OL]. Science,1998,281:331-332[1998-09-23]. http://www.sciencemag.org/cgi/ collection/anatmorp.

[4]  兰德尔E.布莱恩特等. 深入理解计算机系统. 北京:机械工业出版社,2016:1-640.

[5]  程序头 - 链接程序和库指南 (oracle.com)

[6]  用Ubuntu编写第一个C程序并预处理、编译、汇编、链接_北岛寒沫的博客-CSDN博客_ubuntu创建c语言文件

[7]  https://baike.baidu.com/item/%E7%BC%96%E8%AF%91/1258343

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值