程序人生-Hello’s P2P--哈工大计算机系统PA

摘 要

本文详细介绍了hello程序的一生,通过在Linux环境下使用gcc,cpp,cll,as,ld,vim,objdump,edb等工具分析了 hello程序如何从一个文本文件源程序hello.c经过预处理、编译、汇编、链接成为可执行目标文件,还分析了进程管理、异常与信号处理的过程,以及hello程序在计算机中的存储管理等信息。

关键词:程序编译;预处理;汇编;链接;进程;异常与信号;

概述

Hello简介

Hello程序的生命周期是从一个高级的C语言程序开始的,我们可以通过I/O设备编写C语言程序hello.c。但要在系统上运行Hello程序,每条C语句都必须被转化成机器语言指令。这些指令按照一种称为可执行目标文件程序的格式打包,并以二进制磁盘文件的形式存放起来。目标程序也称为可执行目标文件。
P2P过程:(From Program to Process)通过编译器的处理,hello.c文件经历预处理、编译、汇编、链接,四个步骤,从源程序文件变为可执行目标文件,然后由shell为其创建一个新的进程并运行它,此时hello就从program变成了process。
020过程:(From 0 to 0)刚开始程序还不在内存空间中,所以是0,shell通过execve和fork加载并执行hello,映射虚拟内存,进入程序入口后程序开始载入物理内存,然后执行第一条指令,CPU为运行的hello分配时间片执行逻辑控制流。当程序运行结束后,shell父进程负责回收hello进程,内核删除相关数据结构,此时又变成了0。

环境与工具

硬件环境:AMD Ryzen 7 5800H; 16G RAM;512G SSD
软件环境:Windows 11 64位;CodeBlocks 20.13;Ubuntu 22.04 LTS 64;
开发工具: gcc, objdump, vim

中间结果

名称作用
hello.chello程序c语言源文件
hello.ihello.c预处理生成的文本文件
hello.s由hello.i编译得到的汇编文件
hello.ohello.s汇编得到的可重定位目标文件
elf.txtobjdump生成的hello.o反汇编文件
hello.o.t由hello.i编译得到的汇编文件
hellohello.o和其他文件链接得到的可执行目标文件
elf1.elfreadelf生成的hello的elf文件
hello.tobjdump生成的hello的反汇编文件

小结

本章介绍了hello的P2P和020过程,分析hello一生需要的软硬件环境和开发工具,还罗列出了中间结果文件的名字及其作用。

预处理

预处理的概念与作用

预处理是指程序在编译一个c文件之前,预处理器cpp根据以字符#开头的命令(头文件、宏等),修改原始的c程序。比如hello.c中的#include <stdio.h>命令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入程序文本中,结果就得到了另一个C程序,通常是以.i作为文件扩展名。

在Ubuntu下预处理的命令

gcc -m64 -no-pie -fno-PIC -E hello.c -o hello.i

Hello的预处理结果解析

如图展示了部分结果,预处理器cpp处理以#开头的语句,因为<stdio.h >,<unistd.h>,<stdlib.h>都是标准文件库,所以它会在linux系统中的环境变量下寻找这三个库,它们在/usr/include下,然后cpp将这三个库直接插入代码中。因为这三个库中还有#define #ifdef等,所以cpp还需要将这些展开,所以hello.i最后没有#define。
在这里插入图片描述

小结

本章主要讲述了预处理的概念和作用,并且分析了hello.c经过预处理后生成hello.i的过程,对hello.c来说是cpp读取了这三个系统头文件的内容,并把它插入此程序文本中,从而得到了另一个C程序hello.i。

编译

编译的概念与作用

编译过程是指编译器ccl做一些语法语义分析,如果没有错误,它就会把文本文件hello.i翻译成ASCII汇编语言文件hello.s,它包含一个汇编语言程序,翻译后的汇编语言程序中,每一条语句都以一种文本格式描述一条低级机器语言指令,为不同高级语言的不同编译器提供了通用的输出语言。

在Ubuntu下编译的命令

gcc -m64 -no-pie -fno-PIC -S hello.i -o hello.s
在这里插入图片描述

Hello的编译结果解析

数据

  1. 立即数
    汇编语言中立即数是以$开头的。hello.c中的整型常量是以数字出现的,对应了hello.s中的立即数,如图红框中的数字就是hello.c中的整型常量。分别是4与argc比较,exit(1)参数为1,给i赋初值1,i与8比较,i加1,输出argv[1],argv[2]时的下标1,2,以及argv[3]中的下标3。
    在这里插入图片描述
    它们与hello.s中的对应关系如图所示:
    在这里插入图片描述

  2. 字符串
    字符串有红框中的两个,即”用法: Hello 学号 姓名 秒数!\n”和格式串”Hello %s %s\n”它们都作为为源程序中printf函数中直接输出的部分,存放在了.rodata中。
    在这里插入图片描述

  3. int
    在这里插入图片描述
    4.数组
    char *argv[]是指向char型变量的指针, 没有单独声明,在函数执行时在命令行进行输入。argc指向已经分配好连续的连续空间,起始地址为argv,它保存在%rsi中,如图,将rsi的值传给了-32(%rbp)。
    在这里插入图片描述

赋值

对局部变量i赋值即i=0;因为i是双字,所以用的是movl
在这里插入图片描述

类型转换

atoi(argv[3])用到了强制转换,它把字符串转换成整型数。
在这里插入图片描述

算术操作

  1. 加法:ADD S, D (D <- D + S)
    在这里插入图片描述

    1. 减法:SUB S, D (D <- D - S)
      在这里插入图片描述

    关系操作

    CMP S1, S2

    1. 判断argc是否等于4,使用cmp S1,S2根据S2-S1的值设置条件码,等于0则ZF=1,否则ZF=0,为接下来的跳转做准备。
      在这里插入图片描述

    2. 判断i是否小于等于7,将条件码设置为i - 7,为下一句跳转指令做准备
      在这里插入图片描述

数组指针操作

printf中用了argv[1]、argv[2],atoi中用了argv[3]对应在汇编中的操作是寻址后把argv[1]传给%rsi,argv[2]传给%rdx完成printf的参数传递,寻址后把argv[3]传给%rdi,将参数传给atoi。

在这里插入图片描述

控制转移

1.第一处是判断argc是否等于4,若不等于,则继续执行,若等于,则跳转至L2处继续执行。
在这里插入图片描述

2.第二处是无条件跳转,以跳到L4,即循环部分代码。
在这里插入图片描述

3.第三处是判断是否达到循环终止条件(i<9),hello.s中是比较i和8,若小于等于则跳回L4重复循环,否则顺序执行。
在这里插入图片描述

函数调用

1.第一处是调用printf(),输出一个字符串常量,参数存在%edi中。
在这里插入图片描述

2.第二处是调用printf()输出字符串常量和两个char指针argv[1],argv[2]指向的字符串,字符串常量作为参数1在%edi中,两个指针作为参数2、3分别存在%rsi和%rdx中。
在这里插入图片描述
3.第三处是调用atoi(),将%rdi设置为argv[3],call调用atoi函数进行字符串到整型数的转换。
在这里插入图片描述

4.第四处是调用sleep(),首先是将atoi转换后的值保存到%edi中,然后调用sleep。
在这里插入图片描述

5.第五处是调用exit,参数是1,将立即数1保存到%edi,然后调用exit
在这里插入图片描述

6.第六处是调用getchar(),没有参数。
在这里插入图片描述

小结

本章分析了从hello.i到hello.s的过程和hello.s文件的程序代码,包括C语言中的各种数据类型以及各种操作指令。汇编语言比高级语言更接近于底层和机器,每条指令都有自己唯一的步骤,因此一条高级语言通常会拆分成好几条汇编指令,同时其也会有一些不同,是按汇编器习惯做的选择与优化。

汇编

汇编的概念与作用

概念:汇编器as将hello.s翻译成机器语言指令,把这些指令打包成一种叫可重定位目标程序的格式,并将结果保存在目标文件hello.o中。其中hello.o是一个二进制文件。
作用:产生机器语言指令,使得机器能够识别。

在Ubuntu下汇编的命令

gcc -m64 -no-pie -fno-PIC -c hello.s -o hello.o
在这里插入图片描述

可重定位目标elf格式

ELF头

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

节头部表

节头部表中描述其他节的位置和大小,还包括包括节的名称、类型、地址、偏移量、对齐等。
在这里插入图片描述

rel.text和.rel.data

.rel.text(重定位节) 一个.text节中位置的列表,包含了.text节中需要进行重定位的信息。.rel.data是被模块引用或定义的所有全局变量的重定位信息。链接时,需要重定位函数位置(exit, printf, atoi, sleep, getchar)和.rodata中LC0和LC1两个字符串中的信息。
在这里插入图片描述

符号表

.symtab一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。Name是字符串中的字节偏移,指向符号的以null结尾的字符串名字,value是据定义目标的节的起始位置偏移,size是目标的大小(以字节为单位)。Type是符号的种类,有函数、数据、文件等,Binding简写为bing,表示符号是本地的还是全局的,符号表还可以包含各个节的条目,以及对应原始源文件的路径名的条目。
在这里插入图片描述

Hello.o的结果解析

1.反汇编结果:
在这里插入图片描述
2. hello.s
在这里插入图片描述
在这里插入图片描述

3.区别分析
反汇编和hello.s在代码段很相像,但是反汇编左侧增加了汇编语言对应的机器语言指令。机器语言是由0/1所构成的序列,在终端显示为16进制表示的。
(1)分支转移:
hello.s分支转移目标位都是使用.L*表示,hello.o反汇编之后,目标位置变成具体的地址。段名称在hello.s只是助记符,在hello.o机器语言中不存在。
(2)函数调用:
hello.s中函数调用是call+函数名,在反汇编文件中目标地址变成了当前的PC值,因为都调用外部函数,所以需要在链接阶段重定位。
(3)操作数:
hello.s中立即数是十进制的,反汇编文件中都是二进制的,在终端显示的时候转换成了十六进制。

小结

本章分析了从hello.s到hello.c的过程,分析了hello.s的ELF文件信息,对比hello.s和hello.o反汇编代码,分析了汇编指令和机器指令区别和相同点。之后hello.o就会通过链接,成为可执行文件。

链接

链接的概念与作用

概念:链接是将各种代码和数据片段收集并组成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行于编译、加载、运行时。
作用:使得分离编译成为可能,可以将大型应用程序分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需要简单地重新编译它,并重新链接应用,而不必重新编译其他文件。

在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
在这里插入图片描述

可执行目标文件hello的格式

ELF头

hello的 ELF头和hello.o的包含的信息种类相同,但是它还包括程序的入口点,也就是当程序执行时要执行的第一条地址,程序头和节数量也有了增加。
在这里插入图片描述

节头表

与之前的相比,因为邻接,所有的节都有了实际地址。它描述各个节的大小、偏移量和其他属性。链接器链接时,将文件的相同段合并成一个段,根据这个段的大小和偏移量重定位符号的地址。
在这里插入图片描述

重定位节

与之前的重定位节完全不同,这是链接重定位的结果。
在这里插入图片描述

符号表

链接进行符号解析后,不在main中定义的符号也有了类型(TYPE),符号表不需要加载到内存。
在这里插入图片描述

程序头

程序头部表描述了可执行文件的连续的片与连续内存段的映射,我们可以看到根据可执行目标文件的内容初始化两个内存段,代码段和数据段。
在这里插入图片描述

hello的虚拟地址空间

使用edb加载hello,Data Dump 窗口可以查看本进程的虚拟地址空间各段信息,程序从0x00400000处开始存放。再看程序头,它告诉链接器运行时需要加载的内容,它还提供动态链接的信息。每一个表项提供各段在虚拟地址空间和物理地址空间的各方面的信息。
在这里插入图片描述

其中phdr显示程序头表;interp必须调用的解释器。load表示需要从二进制文件映射到虚拟地址空间的段,保存常量数据、程序的目标代码等。dynamic 保存动态链接器使用的信息;note辅助信息。gnu_stack是权限标志,标志栈是否可执行。gnu_relro:重定位后内存中只读区域的位置。
在这里插入图片描述

链接的重定位过程分析

命令:objdump -d -r hello
分析hello与hello.o的不同:
hello是符号解析和重定位后的结果,链接器会修改hello中数据节和代码节中对每一个符号的引用,使得他们指向正确的运行地址。
1.新的函数:
hello.o与其他库链接,hello.c中使用的库中的函数就被加载进来了,如exit、printf、sleep、getchar、atoi等函数。
在这里插入图片描述

2.条件控制和函数调用地址都有改变
在这里插入图片描述

3.hello中多了.init和.plt节。
在这里插入图片描述

4.总结:
链接的过程:链接就是链接器将各个目标文件组装在一起,文件中的各个函数段按照一定规则累积在一起。
hello重定位:有重定位PC相对引用和绝对引用,对于PC相对引用,将地址改为PC值-跳转目标位置地址。绝对引用则将地址改成该符号的第一个字节所在的地址。

hello的执行流程

通过edb的调试,逐步记录call命令调用的函数。

地址名称
0x401000<_init>
0x401020<.plt >
0x401090< puts@plt>
0x4010a0< printf@plt>
0x4010b0< getchar@plt>
0x4010c0< atoi@plt>
0x4010d0< exit@plt>
0x4010e0< sleep@plt>
0x4010f0<_start>
0x401120<_dl_relocate_static_pie>
0x401125
0x4011b4<_fini>

Hello的动态链接分析

从hello的elf文件可知,.got表的地址为0x0000000000403ff0,在edb中的Data Dump窗口跳转,定位到GOT表处。
在这里插入图片描述
在这里插入图片描述

调用_init后
在这里插入图片描述

初始地址0x00600ff0全为0。程序调用一个由共享库定义的函数,编译器没有办法预测这个函数的运行时地址,因为它定义它的共享模块在运行时可以加载到任意位置。链接器采用延迟绑定的策略解决。动态链接器使用过程链接表PLT + 全局变量偏移表GOT实现函数的动态链接,GOT中存放目标函数的地址,PLT使用该地址跳转到目标位置,其中GOT[1]指向重定位表,GOT[2]指向动态链接器ld-linux.so运行地址。

本章小结

本章介绍了链接的定义和作用,分析了程序链接的过程,查看ELF的信息分析链接生成可执行文件过程中程序发生的变化。使用edb分析了动态链接。

hello进程管理

进程的概念与作用

概念:进程时一个执行中程序的实例。
作用:系统中的每个程序都运行在某个进程的上下文中,进程提供一个假象,就好像我们的程序是系统中当前运行的唯一的程序的一样,我们的程序好像是独占地使用处理器和内存。处理器就好像是无间断地一条接着一条地执行我们程序中的指令。最后,我们程序中的代码和数据好像是系统内存中唯一的对象。

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

作用:Shell能解释用户输入的命令,将它传递给内核,还可以:调用其他程序,给其他程序传递数据或参数,并获取程序的处理结果。在多个程序之间传递数据,把一个程序的输出作为另一个程序的输入。Shell本身也可以被其他程序调用。

处理流程:
1.输出一个提示符,等待输入命令,读取从终端输入的用户命令。
2.分析命令行参数,构造传递给execve的argv向量。
3.检查第一个命令行参数是否是一个内置的shell命令,如果是立即执行。
4.否则用fork为其分配子进程并运行。
5.子进程中,进行步骤2获得参数,调用exceve()执行程序。
6.命令行末尾没有&,代表前台作业,shell用waitpid等待作业终止后返回。
7.命令行末尾有&,代表后台作业,shell返回。

Hello的fork进程创建过程

输入命令执行hello后,父进程判断不是内部指令后,会通过fork创建子进程。子进程几乎但不完全与父进程相同,子进程得到与父进程用户级虚拟空间相同但独立的副本,包括代码和数据段、堆、共享库以及用户栈。子进程可以读写父进程中打开的任何文件,二者最大的区别在于它们有不同的PID。
fork函数只会被调用一次,但会返回两次,在父进程中,fork返回子进程的PID,在子进程中,fork返回0。

Hello的execve过程

execve函数在加载并运行可执行目标文件hello,且带列表argv和环境变量列表envp。当出现错误时,例如找不到hello时,execve会返回到调用程序,与一次调用两次返回的fork不同。在execve加载了hello之后,它调用启动代码。启动代码设置栈,并将控制传递给新程序的主函数,该主函数有如下的原型:int main(intargc , char **argv , char *envp);结合虚拟空间和内存映射过程,有删除已存在的用户区域,映射私有区,映射共享区,设置PC这四个过程,进程地址空间如图所示。
在这里插入图片描述

Hello的进程执行

  1. 逻辑控制流:一系列程序计数器 PC 的值的序列叫做逻辑控制流。这个序列每个PC值唯一对应于包含在程序的可执行目标文件中的指令,或是包含在运行时动态链接到程序的共享对象中的指令,一个进程有一个逻辑控制流,进程交错执行。

  2. 上下文:内核重新启动一个被抢占的进程所需要恢复的原来的状态,由寄存器、程序计数器、用户栈、内核栈和内核数据结构等对象的值构成。

  3. 用户模式和内核模式:处理器使用某个控制寄存器一个控制位提供两种模式的区分。用户模式的进程不允许执行特殊指令,不允许直接引用地址空间中内核区的代码和数据;内核模式进程可以执行指令集中的任何命令,并且可以访问系统中的任何内存位置。

  4. 进程时间片:一个进程和执行它的控制流的一部分的每一时间段。

  5. 上下文切换:
    比如sleep函数,初始时,控制在hello中,处于用户模式,调用系统函数sleep后,转到内核模式,调用进程被挂起,经过设定秒数后,发送中断信号,转回用户模式,继续执行指令。如图是一个进程上下文切换的剖析。
    在这里插入图片描述

  6. 调度过程:
    在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占了的进程,这种决策就叫做调度,是由内核中称为调度器的代码处理的。当内核选择一个新的进程运行,我们说内核调度了这个进程。在内核调度了一个新的进程运行了之后,它就抢占了当前进程,并使用上下文切换机制来将控制转移到新的进程。

hello的异常与信号处理

  1. 异常,如图所示,异常有以下四种,分别是中断、陷阱、故障、终止。它的种类和处理方式如图所示。
    在这里插入图片描述

  2. 信号,信号有很多种,图中展示的是Linux上支持的30种不同类型的信号。

在这里插入图片描述

  1. hello正常运行状态
    在这里插入图片描述

  2. Ctrl+Z
    进程收到 SIGSTP 信号, hello 进程被挂起。ps查看它的进程PID,可知 hello的PID是3333; jobs查看hello的后台 job号是1,用调用 fg 1把它调回前台。
    在这里插入图片描述

  3. Ctrl+C:进程收到 SIGINT 信号,终止 hello。在ps中没有它的PID,在job中也没有,可以看出hello已经被永远地停止了。
    在这里插入图片描述

6.运行中不停乱按,会将屏幕的输入缓存到缓冲区,乱码被认为是命令。
在这里插入图片描述

7.kill,杀死进程,hello被终止。
在这里插入图片描述

本章小结

本章介绍了hello进程的执行过程。主要是hello的创建、加载和终止,通过键盘输入。在hello运行过程中,内核有选择地对其进行管理,决定何时进行上下文切换。在hello运行过程中,接受到不同的异常信号时,异常处理程序将对异常信号做出回应,执行对应指令,每种信号有不同的处理机制,对不同的异常信号,hello有不同的处理结果。

hello的存储管理

hello的存储器地址空间

存储器的地址空间包括逻辑地址,线性地址,虚拟地址,物理地址。
逻辑地址:访问指令给出的地址空间是逻辑地址,经过寻址方式的计算后可以得到存储器的的物理地址。
线性地址:逻辑地址经过段机制后转化为线性地址,然后再经过变化可以变为物理地址。在分段部件,逻辑地址加上基地址就是线性地址,在分页机制中,以线性地址作为输入。
虚拟地址:CPU启动保护模式后,程序运行在虚拟地址空间中。注意,并不是所有的“程序”都是运行在虚拟地址中。
物理地址:是指出现在CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果地址。在存储器里以字节为单位存储信息,每一个字节单元有一个唯一的存储器地址,也就是物理地址。如果启用了分页机制,那么hello的线性地址会使用页目录和页表中的项变换成hello的物理地址;如果没有启用分页机制,那么hello的线性地址就直接成为物理地址了。

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

段是虚拟地址到线性地址转化的基础,段式管理就是把虚拟地址空间中的虚拟内存组织成一些长度可变的成为段的内存单元。一个逻辑地址由两部分组成,段标识符和段内偏移量,一个段有三个参数定义,段基地址,段限长。段属性,这三个参数存储在一个成为段描述符的结构项里面。
转换过程:
(1)先检查段选择符中的TI字段,它决定着段描述符具体保存在哪一个描述符表中;
(2)由于一个段描述符有8字节长,因此它在GDT或LDT内的相对地址是段选择符的前13位的值*8;
(3)利用段描述符校验段的访问权限和范围,确保该段可以访问,偏移量位于段界限内。
(4)最后,Base + offset,得到要转换的线性地址。

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

在这里插入图片描述

MMU会利用页表,完成从虚拟地址到物理地址的映射。页表指的就是一个页表条目(PTE)的数组。线性地址就是虚拟地址。虚拟地址(VA)中分为虚拟页号(VPN)和虚拟页偏移量(VPO)两部分,CPU会先取出虚拟页号,然后通过页表基址寄存器(PIBR)定位页表条目,如果有效位为1,就可以从页表条目中取出对应的物理页号(PPN),物理页偏移量和虚拟页偏移量(VPO)结合,然后把物理地址(PPN)和物理页偏移量(PPO)组合,得到物理地址。

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

在这里插入图片描述

在MMU中包括一个关于PTE的小的缓存,来降低转换过程中的开销,称为翻译后备存储器(TLB)
(1)VA中的VPN会被分成三段(如上图),根据TLBT和TLBI,在TLB中寻找对应的PPN,如果没有找到,即判定为缺页,就需要到页表中去找。
(2)将VPN分成更多段(4段),CR3是对应的L1PT的物理地址,然后一步步递进往下寻址,越往下一层每个条目对应的区域越小,寻址越细致。
(3)在经过4层寻址之后,就能够找到相应的PPN,再和VPO拼接起来即得到PA。
在这里插入图片描述

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

(1)在得到物理地址VA后,使用物理地址的CI进行组索引(每组8路),对8路的各个块分别匹配CT进行标志位匹配。如果能够匹配,且各块的valid标志位为1,则为命中。然后根据数据偏移量CO取出数据并返回。
(2)如果没找到相匹配的CT,或者标志位为0,则为不命中。这时cache就会向下一级cache,比如二级cache或者三级cache中继续查询数据。最后把查到的数据按照顺序,逐级写入cache。
(3)最后在更新cache的时候,需要判断cache中是否有空闲(即有效位为0)的块。若有空闲块,则写入;若不存在,则驱逐。
在这里插入图片描述

hello进程fork时的内存映射

在用fork()函数创建虚拟内存的时候,要经历以下步骤:
(1)创建当前进程(shell)的mm_struct,vm_area_struct和页表的原样副本。
(2)把父子进程的每个页面都标记为只读页面。
(3)把父子进程的每个vm_area_struct都标记为私有,这样就只能在写入时复制。
7.7 hello进程execve时的内存映射
exceve()函数加载和执行程序hello,需要以下几个步骤:
(1)删除原有的用户区域。
清除这个进程曾经运行过的程序留下来的痕迹,初始区域
(2)创建新的私有区域(如.malloc,.data,.bss,.text)。
(3)创建新的共享区域(如libc.so.data,libc.so.text)。
(4)更新程序计数器PC的值,使它指向程序的入口。
这样下一次调用这个进程的时候,就会从这个入口开始

缺页故障与缺页中断处理

处理过程如下图所示:
在这里插入图片描述

其中步骤如下:
1)处理器将虚拟地址发送给MMU
2)MMU将PTEA发送给缓存/主存(页表)
3)从页表中将PTE发送给MMU
4)有效位为0,触发缺页异常
5)缺页处理程序确定物理内存中牺牲页(若页面被修改,则换出到磁盘)
6)缺页处理程序调入新的页面,更新内存中PTE
7)缺页处理程序返回原来进程,再次执行导致缺页的指令

本章小结

本章介绍了hello的存储空间分配,内存映射,Intel的段式管理(从逻辑地址到线性地址),hello的页式管理(从线性地址到物理地址),以及从VA到PA的变换和对缺页故障和缺页终端的处理。

结论

hello经历的过程如下:
1.源文件编写:用文本编辑器写出hello的源程序文件。
2.预处理:预处理器对hello.c进行预处理,生成hello.i文本文件,将源程序中使用到的外部库插入到文件中。
3.编译:编译器对hello.i进行语法分析、优化等操作生成hello.s汇编文件。
4.汇编:as将hello.s翻译机器更易读懂的机器代码hello.o,它是一个二进制文件。
5.链接:链接器ld将hello.o和其他用到的文件进行合并链接,生成可执行文件hello。
6.运行程序:在终端中输入运行命令,shell进程调用fork为hello创建子程序,然后调用execve启动加载器,加映射虚拟内存。
7.执行指令:CPU为程序分配时间片,在一个时间片中hello使用CPU资源顺序执行控制逻辑流。
8.异常处理:hello执行的过程中可能收到来自键盘输入的信号,调用信号处理程序进行处理。
9.进程结束:shell作为父进程回收子进程,内核删除为这个进程创建的所有数据结构。
感悟:通过分析hello的一生,把本课程的知识联系到了一起,不再是以往零碎的知识点了,有了更加完整的知识体系。

附件

名称作用
hello.chello程序c语言源文件
hello.ihello.c预处理生成的文本文件
hello.s由hello.i编译得到的汇编文件
hello.ohello.s汇编得到的可重定位目标文件
elf.txtobjdump生成的hello.o反汇编文件
hello.o.t由hello.i编译得到的汇编文件
hellohello.o和其他文件链接得到的可执行目标文件
elf1.elfreadelf生成的hello的elf文件
hello.tobjdump生成的hello的反汇编文件

参考文献

[1]龚奕利. 深入理解计算机系统.北京:机械工业出版社,2016.
[2]HIT 计算机系统授课PPT

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值