本文旨在阐述C语言程序如何从源代码状态转化为可执行文件的过程。以hello.c程序为实例,本文深入剖析了计算机在构建hello可执行文件时,所经历的预处理、编译、汇编、链接以及进程管理等关键环节。本文不仅从理论上探讨了这些工具和技术背后的原理与机制,还通过实际操作展示了这些步骤的具体应用与结果,从而解释了计算机系统的运作原理与架构。通过这一较为详尽的解析,旨在帮助读者更全面地理解并掌握C语言程序的编译与执行流程,深化对计算机编程和操作系统原理的认识。
关键词:计算机系统;预处理;编译;汇编;链接;进程;体系结构
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
目 录
第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 -
参考文献....................................................................................... - 16 -
第1章 概述
1.1 Hello简介
P2P :即From Program to Process,从hello.c程序(Program)到进程(Process)的转换过程。当我们想要运行hello.c,我们首先需要将其转化为可执行文件。这一转化过程包含了四个步骤:程序通过预处理阶段处理宏定义和条件编译等指令;编译器将预处理后的代码转化为汇编语言代码;汇编器将这些汇编语言代码转换为机器语言指令,形成目标文件;链接器将所有目标文件和必要的库链接起来,生成可执行文件。得到可执行文件后,就可以在shell环境中运行它,shell会为它分配所需的进程空间,从而使hello.c程序得以在操作系统中运行起来。
020: 即From Zero-0 to Zero-0。在hello程序运行之前,内存中并无关于它的任何内容。当shell通过execve系统调用启动hello程序时,它会为程序在虚拟内存中分配空间,并将这些空间映射到物理内存上。随后,程序从预设的入口点开始执行,进入main函数并运行其中的代码。当hello程序执行完毕后,shell作为父进程会回收其资源,而内核则会清理与hello程序相关的数据结构,使系统恢复到程序运行前的状态。
1.2 环境与工具
X64 CPU;2GHz;2G RAM;256GHD Disk 以上
Windows7/10 64位以上;VirtualBox/Vmware 11以上;Ubuntu 16.04 LTS 64位/优麒麟 64位 以上;
gcc/as/ld/edb/readelf
1.3 中间结果
hello.i 预处理后得到的文本文件
hello.s 编译后得到的汇编语言文件
hello.o 汇编后得到的可重定位目标文件
hello.asm 反汇编hello.o得到的反汇编文件
hello1.asm 反汇编hello可执行文件得到的反汇编文件
1.4 本章小结
本章首先概述了hello程序的P2P以及020的流程,涵盖了其设计思路以及实现细节。接着对实验所需的硬件配置进行了详细说明,包括必要的软件平台和开发工具。此外,还对实验过程中生成的各个中间结果文件进行了阐述,指出了它们的名称和各自在实验过程中的功能作用。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
2.1.1预处理的概念
预处理步骤是编程过程中的一个重要环节,它在程序实际编译之前,由预处理器对源代码文件进行预处理。在这个过程中,预处理器主要执行代码文本的替换工作,特别是针对以#开头的预处理指令。这些指令包括但不限于宏定义、条件编译、头文件包含等,它们被预处理器解析后,会替换为实际的代码内容。此外,预处理过程还会移除源代码中的注释和多余的空白字符,以便后续的编译过程能够更高效地进行。可以理解为预处理指令在预处理阶段被解析并转换为实际代码的一部分。
2.1.2预处理的作用
在编程过程中,预处理阶段并不直接解析程序源代码的逻辑内容,而是对源代码进行特定的分割、处理和替换。具体而言,预处理的作用包括:将所包含的头文件指令替换为实际的头文件内容;将宏定义替换为宏所代表的实际代码;根据条件编译指令决定是否包含某段代码;以及删除源代码中的注释和多余的空白字符等。这些操作都是为了准备源代码,使其更易于后续的编译过程。简而言之,预处理是一个由预处理器执行的文本插入与替换的过程。
2.2在Ubuntu下预处理的命令
预处理的命令:gcc -E hello.c -o hello.i
2.3 Hello的预处理结果解析
在Linux系统中,当我们打开hello.i文件时,与原始的.c源文件进行对比,我们注意到除了预处理指令(如头文件包含和宏定义)被扩展成了相应的实际代码,导致文件行数大幅增加之外,源程序中的其他代码部分(如函数定义、变量声明和语句等)在逻辑上应当保持不变。这表明预处理阶段主要影响的是预处理指令的扩展,而不是直接修改.c文件中的代码。
2.4 本章小结
本章阐述了在Linux环境下,如何使用命令行工具对C语言程序进行预处理操作,并深入探讨了预处理的定义及其重要性。随后,通过一个具体的hello程序示例,展示了从hello.c源文件到预处理后输出文件hello.i的转换过程。接着详细分析了预处理后的hello.i文件内容,发现其中包含了标准输入输出库stdio.h的完整内容,以及程序中定义的各种宏和常量。此外,还包含了必要的行号信息和可能的条件编译指令,这些都是预处理阶段由预处理器自动插入或修改的。通过这一分析,读者可以更加清晰地理解预处理在C语言编译过程中的作用和影响。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
计算机程序编译是将用高级程序设计语言编写的源程序,通过一系列处理步骤(包括词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成等),转化为等价的汇编语言或机器语言程序的过程。这一转换旨在提高编程效率、保证程序的可读性和可移植性,并确保源代码在多种平台上的兼容执行。
3.2 在Ubuntu下编译的命令
编译的命令:gcc -S hello.i -o hello.s
3.3 Hello的编译结果解析
立即数:以$符号开头
赋值:用mov指令加上对应的数据类型长度,如movl表示对四位的数据进行操作,此外Linux中movX指令对应的方向是从前面到后面,即mov 源操作数 目的操作数
数学运算、逻辑运算:都有对应的指令,如add sub(也分别需要加上对应的操作数的大小),乘除法除了相应专属的指令之外,还可以用移位操作来代替,同时,lea取地址操作也可以用来作为运算的一种方式
类型转换:hello程序中,只有通过atoi函数转换这一部分
数组操作:因为数组分布具有地址连续的特征,即可以通过对地址的加减然后取内容来访问数组中的元素
条件跳转:即hello程序中的if和for两个语句,通过比较然后设置标志位来判断是否满足跳转条件,如下图就是判断rbp-8这个位置的元素和8的大小关系,然后满足le(less or equal)就进行跳转
函数调用:通过call将代码转移到另一个函数去,同时包含控制转移和数据传递,控制转移是当程序执行到jump语句这一行时,原本程序计数器rip应该保存当前执行语句下一行的地址,但是如果此条语句是jump,rip就保存要jump到的目的地的语句地址;参数传递则是通过rdi、rsi等六个寄存器传递(多于六个的情况放在栈上),考虑到寄存器里原来可能存有值,为了防止以前的内容丢失,寄存器被分为调用者保存和被调用者保存,便于从另一个函数返回时恢复原始值;函数在调用时也会将返回地址压栈,就可以在被调用函数ret时返回到调用函数的下一条。
3.4 本章小结
本章阐述了C编译器将hello.i中间代码文件转换成hello.s汇编语言文件的过程。首先简要介绍了编译的概念,即编译器将高级语言编写的源代码转换为机器可执行的低级语言代码的过程。接着,本章通过具体的编译指令展示了编译过程,并深入分析了生成的hello.s汇编代码。
在hello.s文件部分探讨了编译器如何处理数据、函数调用、赋值、算术运算、关系运算以及控制跳转和类型转换等关键操作。具体来说,我们比较了源代码中的这些操作在汇编代码中的实现方式,从而揭示了编译器在转换过程中的工作原理和技巧。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
汇编是一个编程过程中的关键步骤,它涉及使用汇编器(as)将包含汇编语言的.s文件转化为机器可以直接执行和理解的机器语言指令,并将这些指令打包成一个可重定位的目标文件格式(.o文件)。这个过程确保了高级语言的抽象性被转化为机器能够直接读取和执行的二进制编码,从而实现了程序从源代码到可执行文件的转变。
4.2 在Ubuntu下汇编的命令
在Ubuntu系统下,对hello.s进行汇编的命令为:
gcc -m64 -no-pie -fno-PIC -c hello.s -o hello.o
4.3 可重定位目标elf格式
获得 hello.o 文件的 ELF 格式的命令为:
readelf -a hello.o > hello.elf
(1)ELF头
ELF头以一个l6字节的序列开始,该序列描述了生成该文件的系统的字的大小和字节顺序。ELF头剩余部分为帮助链接器语法分析和解释目标文件的信息,其中包括ELF头的大小、目标文件的类型(如可重定位、可执行或者共享的)、机器类型、节头部表的文件偏移,以及节头部表中条目的大小和数量等。不同节的位置和大小由节头部表描述的,其中目标文件中每个节都有一个固定大小的条目。ELF头如下图:
(2)节头
记录各节名称、类型、地址、偏移量、大小、全体大小、旗标、链接、信息、对齐。节头如下图:
(3)重定位节
.rel.text节是一个.text节中位置的列表,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。正常情况下,任何调用外部函数或者引用全局变量的指令都需要修改,而调用本地函数的指令不需修改。可执行目标文件中不包含重定位信息。重定位节如下图:
(4)符号表
.symtab节中包含ELF符号表,该符号表包含一个条目的数组,存放一个程序定义和引用的全局变量和函数的信息。该符号表不包含局部变量的信息。符号表如下图:
4.4 Hello.o的结果解析
使用objdump -d -r hello.o > hello.asm 指令输出hello.o的反汇编文件hello.asm,并将其与第3章的hello.s文件进行对比。
objdump -d -r hello.o 分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。
在汇编语言的上下文中,无论是.s文件还是反汇编代码,分支转移和函数调用都是通过指令来实现的。在.s文件中,分支跳转通过判断条件和跳转标签(如.L3)指定目标位置,而在反汇编后的代码中,这些标签被替换为具体的内存地址。函数调用则通过call指令调用目标函数或地址。此外,立即数在.s文件中以十进制形式表示,而在反汇编代码中则采用16进制形式(以0x开头),这些差异体现了不同层次的汇编表示,但核心功能保持不变,机器指令与汇编指令一一对应,可以直接通过机器指令解读汇编语言的执行意图。
4.5 本章小结
本章深入探讨了汇编语言的含义及其核心功能。以Ubuntu系统下的hello.s文件为例,详细描述了将其汇编成可重定位目标文件hello.o的过程,并进一步生成了ELF(可执行与可链接格式)的可执行文件hello.elf。通过将可重定位目标文件转换为ELF格式,我们能够更清晰地观察文件内容,并对其中的各个节(如代码段、数据段等)进行简单解析。通过对比hello.o的反汇编代码(保存在hello.asm中)与原始的hello.s文件,本章揭示了汇编语言到机器语言的转换过程,以及为了支持链接操作而进行的准备工作,为读者提供了一个深入理解汇编语言及其编译过程的机会。
(第4章1分)
第5章 链接
5.1 链接的概念与作用
概念:将各种代码和数据片段收集并组合成一个单一文件的过程,这个文件可被加载到内存并且执行。
作用:可以是程序分离为各个模块,然后整合在一起执行,因此方便了后续对程序各个模块的分离修改和编译,这使得程序更加易于管理。
5.2 在Ubuntu下链接的命令
链接的命令如下:
5.3 可执行目标文件hello的格式
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等,得到下面的信息。
(1)ELF头
(2)节头
(3)程序头:程序头表是一个结构体数组,其中存放组成最终可执行程序几个段的基本信息,包括虚拟地址、物理地址、大小、旗标等。
(4) 动态链接段表:因为链接引入动态库,所以有动态链接段表,用于存放动态链接的相关信息。
(5) 重定位节:
(6) 符号表:
5.4 hello的虚拟地址空间
使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。
如下图,Data Dump中反应了hello程序的虚拟内存的分配,(只截取其中一部分)再结合5.3中的节头部表,就可以对应地看到节中的内容.
5.5 链接的重定位过程分析
连接过程:
在编译和汇编阶段之后,源代码被转化为机器码并存储在目标文件(如 .o 文件)中。然而,这些目标文件通常还包含对其他目标文件或库中的函数和变量的引用。连接过程的主要任务是解析这些符号引用,并将它们与定义在其他目标文件或库中的实际符号定义相关联。
连接过程包括以下几个主要步骤:
符号解析:链接器读取所有的目标文件和库,并构建一个符号表,其中包含了所有定义的符号以及它们的地址。然后,链接器会遍历所有的符号引用,并在符号表中查找对应的定义。
重定位:在符号解析完成后,链接器需要确定每个符号在最终可执行文件中的确切地址。由于目标文件中的代码和数据通常使用相对地址进行引用,因此链接器需要将这些相对地址转换为绝对地址。这个过程称为重定位。
重定位是通过以下步骤完成的:
首先,链接器会确定每个节在最终可执行文件中的起始地址(基地址)。然后,对于每个节中的每个需要重定位的项目,链接器会计算其绝对地址。这通常是通过将节头的位置(即节的基地址)加上该项目相对于节头的偏移量来完成的。最后,链接器会修改目标文件中的指令和数据,将相对地址替换为计算出的绝对地址。
合并节:在重定位之后,链接器会将所有目标文件和库中的相同类型的节合并到最终的可执行文件中。例如,所有的代码段会被合并到一个单一的代码段中,所有的数据段会被合并到一个单一的数据段中。
创建可执行文件:最后,链接器会生成一个可执行文件,该文件包含了所有必要的代码、数据和符号信息,以便操作系统能够加载和执行它。
通过这个过程,链接器能够将多个目标文件和库连接成一个单独的可执行文件,该文件包含了程序运行所需的所有代码和数据。
5.6 hello的执行流程
使用edb执行hello,其调用与跳转的各个子程序名或程序地址如下:
_start 0x4010f0(程序入口点)
_libc_start_main 0x2f12271d(函数进行初始化并且调用main函数)
main 0x401125
_printf 0x401040
_exit 0x401070
sleep 0x401080
_getchar 0x401050
5.7 Hello的动态链接分析
GOT:全局偏移表,(包含每个被这个目标模块引用的全局数据目标的表目),加载时,动态连接器会重定位GOT中的每个表目,使得它包含正确的绝对位置
PLT:过程链接表,把位置独立的函数调用重定向到绝对位置
两者交互使用,使得在重定位时第一次调用函数开销较大,以后调用都只花费一条指令和一个间接的存储器引用。
在这个hello程序中,执行start和init都是为了修改GOT,使得下一次调用时,函数能够指向正确的内存地址。
5.8 本章小结
本章首先详细阐述了链接技术的概念及其重要性,随后通过执行特定的链接命令,成功生成了名为hello的可执行文件。接着探索了hello文件在ELF(可执行与可链接格式)标准下的内部架构和内容细节。利用edb调试工具,观察hello文件在虚拟内存空间中的布局和占用情况。最后以hello程序为范例,深入剖析了链接过程中的重定位机制、程序的执行流程以及动态链接的实现原理。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
进程的经典定义是执行中程序的实例,它代表了程序关于特定数据集合的一次运行活动。作为系统进行资源分配和调度的基本单位,进程不仅是操作系统结构的基础,在传统操作系统中还担当了资源分配的基本单元和执行的基本单元。通过进程,程序能够享有独占处理器和内存的假象,确保指令的无间断执行,而实际上每个程序都在其进程的独立上下文中运行。
6.2 简述壳Shell-bash的作用与处理流程
Shell作用:交互型应用级程序,可以识别用户输入的命令并且做出相应反应
处理流程:首先处理命令行命令(以空格划分单个命令和参数),然后将命令和参数分别划分到相应的位置,判断命令是否是内置命令,如果是,直接执行,如果不是,fork子进程,调用execve函数加上刚刚划分好的参数执行命令行的程序。
6.3 Hello的fork进程创建过程
使用fork()创建子进程之后,子进程与父进程映射到相同的地址空间,但拥有私有的写时复制区域,且父子进程的pid不同,fork函数调用一次返回两次,子进程返回0,父进程返回子进程pid,在此处hello程序中,shell是父进程,而我们要执行的hello是子进程。
6.4 Hello的execve过程
子进程被fork创建出来之后,调用execve()加载可执行文件到当前进程,loader加载器函数会删除当前子进程的虚拟内存段上的内容,创建一个新的段,也就是说,原来的子进程在调用了execve函数之后,除了pid保持原子进程的不变,其余代码,数据,栈等,都被我们接下来将要执行的hello函数所对应的覆盖。
6.5 Hello的进程执行
进程调度利用进程的上下文切换实现,如下图:
6.6 hello的异常与信号处理
(1)所有的异常类型如下:
中断 | IO设备传送信号 | 异步 | 返回到下一条指令 |
陷阱 | 有意的异常 | 同步 | 返回到下一条指令 |
故障 | 可能可以恢复的错误 | 同步 | 可能返回到当前指令 |
终止 | 不可恢复的错误 | 同步 | 终止 |
(2) 异常的处理方式:
(3) 运行结果及相关命令
正常运行状态:
运行时按下Ctrl + C
按下Ctrl + C,Shell进程收到SIGINT信号,Shell结束并回收hello进程:
运行时按下Ctrl + Z
按下Ctrl + Z,Shell进程收到SIGSTP信号,Shell显示屏幕提示信息并挂起hello进程:
对hello进程的挂起可由ps和jobs命令查看,可以发现hello进程确实被挂起而非被回收,且其job代号为1。
不停乱按+按下ctrl-Z
乱按进程不会受到影响,按下ctrl+Z之后进程挂起:
6.7本章小结
本章的主要内容是探讨计算机系统中的进程和shell,首先通过一个简单的hello程序,简要介绍了进程的概念和作用、shell的作用和处理流程,还详细分析了hello程序的进程创建、启动和执行过程,最后,本章对hello程序可能出现的异常情况,以及运行结果中的各种输入进行了解释和说明。
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址空间:汇编代码中的地址,由 段寄存器:偏移量 构成。
线性地址空间:逻辑地址经过转换、计算后得到的非负整数地址的序列,线性地址空间就是指这个地址序列的集合。
虚拟地址空间:与线性地址空间等价。
物理地址空间:数据存储在的真实地址。
Hello中汇编代码里面出现的左侧的数据应该是线性地址
结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。
7.2 Intel逻辑地址到线性地址的变换-段式管理
逻辑地址的构造由段寄存器和偏移量共同确定。段寄存器中存储了段选择符,这个选择符包含了三个关键信息:TI位(指示使用全局描述符表GDT还是局部描述符表LDT)、RPL位(反映当前进程的执行权限状态,如内核态或用户态)、以及索引(用于定位描述符表中对应的段描述符)。当这些信息确定后,系统会根据段选择符中的索引在相应的描述符表中查找对应的段描述符,从而获取段基址。最终,将段基址与偏移量相加,即可得到线性地址,这是程序在内存中访问数据的关键步骤。
7.3 Hello的线性地址到物理地址的变换-页式管理
线性地址与虚拟地址等价,经过MMU翻译得到物理地址,具体过程:虚拟地址n位可被分为VPN(虚拟页号)和VPO(虚拟地址偏移量),通过VPN可在页表里面寻找得到对应的PPN(物理页号),PPO(物理地址偏移量)与VPO(虚拟地址偏移量)相同,最后通过物理页号和物理地址偏移量可以对应寻得物理地址。
7.4 TLB与四级页表支持下的VA到PA的变换
为了更有效地管理页表空间,CPU普遍采用多级页表结构。在寻址过程中,VPN(虚拟页号)k首先被用来定位VPN k+1级页表的基址。随后,使用VPN k+1作为下一级页表的索引进行寻址,以此类推,直到最后一级页表。这最后一级页表所指向的,就是物理页帧号(PPN),它指示了数据在物理内存中的具体位置。
为了提高寻址的效率和速度,CPU引入了TLB(Translation Lookaside Buffer,转换旁视缓存)作为页表的缓存。基于局部性原理,TLB缓存了最常访问的页表项,当CPU需要访问某个页时,会首先检查TLB中是否已有对应的缓存项。如果命中,则可以直接从TLB中获取物理页帧号,避免了频繁访问内存中的页表,从而显著提高了页表访问的效率。
7.5 三级Cache支持下的物理内存访问
CPU对内存访问较慢,于是为内存提供了更快的三级Cache结构,之前访问过的内存块都被暂存在cache中,每次访存都从L1cache开始逐渐向下寻找,即如果L1缓存命中,直接返回数据,如果不命中,然后去L2找,以此类推,如果从下级缓存加载到上级缓存时没用空闲,就使用最近最少访问策略等替换策略进行替换。
7.6 hello进程fork时的内存映射
Fork创建子进程后,内核为新进程创建各种数据结构,并分配给它一个唯一的PID,同时为这个新进程创建虚拟内存,创建当前进程的mm_struct、区域结构和页表的原样副本。它将两个进程中的每个页面都标记位只读,并将两个进程中的每个区域结构都标记为私有的写时复制。
7.7 hello进程execve时的内存映射
调用execve时,loader会删除所有已存在的用户区域,然后用execve函数所代用的信息去映射新的私有区域和共享区域。
7.8 缺页故障与缺页中断处理
CPU执行某条指令的内存访问时,发现虚拟内存中的字不在物理内存中,这时就会导致缺页,程序进入内核态,执行缺页中断处理程序,然后该程序将存在磁盘上的页通过一定的替换策略加载到物理内存,然后更新页表,缺页中断处理完毕。CPU可以重新执行该条指令。
7.9本章小结
本章主要介绍了hello的存储器地址空间、intel的段式管理、hello的页式管理,以intel Core i7在指定环境下介绍了虚拟地址VA到物理地址PA的转换、物理内存访问,分析了hello进程fork时的内存映射、hello进程、execve时的内存映射、缺页故障与缺页中断处理。
(第7章 2分)
结论
hello 程序从编写到执行所经历的过程如下:
编写与预处理:程序员通过键盘输入 hello 程序的代码,并保存为 hello.c。预处理器(cpp)将 hello.c 进行预处理,包括宏展开、头文件包含等,生成预处理后的文件 hello.i。
编译:编译器(ccl)将 hello.i 文件编译成包含汇编指令的 hello.s 文件。
汇编:汇编器(as)将 hello.s 文件转换成机器可执行的机器代码,并打包成可重定位目标文件 hello.o。
链接:链接器(ld)将 hello.o 和其他需要的可重定位目标文件及库文件链接在一起,生成一个可执行目标文件 hello。
运行程序:在终端(shell)中输入 ./hello 2021113211 郑文翔 来启动程序。
创建进程:shell 检查输入命令,发现不是内置命令,于是调用 fork() 系统调用创建一个新的子进程来执行 hello 程序。
加载程序:子进程调用 execve() 系统调用来加载 hello 程序。加载器将程序映射到虚拟内存中,并设置程序的运行环境,之后程序开始执行,进入 main 函数。
CPU调度:CPU 为进程分配时间片,在分配给 hello 的时间片内,它享有CPU资源,顺序执行自己的控制逻辑流。
内存访问:MMU(内存管理单元)将程序中使用的虚拟地址通过页表映射成物理地址,以便访问物理内存。
信号处理:当程序运行时,如果用户输入 Ctrl+c,内核会发送 SIGINT 信号给进程,并默认终止它;而输入 Ctrl+z 时,内核会发送 SIGTSTP 信号给进程,将其挂起。
进程终止:当 hello 程序执行完毕后,子进程退出。内核会通知父进程(shell)回收子进程的资源,并将子进程的退出状态传递给父进程。内核随后删除为这个进程创建的所有数据结构,完成进程终止。
通过本次实验,我深切感受到计算机系统的精细和强大,每一个看似简单的任务背后,都蕴藏着计算机复杂而精细的操作流程,这不仅彰显了计算机科学的严谨逻辑,更体现了现代工艺与技术的绝妙与精彩。
(结论0分,缺失 -1分,根据内容酌情加分)
附件
列出所有的中间产物的文件名,并予以说明起作用。
hello.c 源文件
hello.i 预处理之后的文件
hello.s 编译之后的汇编文件
hello.o 汇编之后的可重定位目标文件
hello 链接之后的可执行目标文件
hello.elf hello.o的elf文件
hello1.elf hello的elf文件
hello.asm hello.o的反汇编文件
(附件0分,缺失 -1分)
参考文献
为完成本次大作业你翻阅的书籍与网站等
[1] 林来兴. 空间控制技术[M]. 北京:中国宇航出版社,1992:25-42.
[2] 辛希孟. 信息技术与信息服务国际研讨会论文集:A集[C]. 北京:中国科学出版社,1999.
[3] 赵耀东. 新时代的工业工程师[M/OL]. 台北:天下文化出版社,1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).
[4] 谌颖. 空间交会控制理论与方法研究[D]. 哈尔滨:哈尔滨工业大学,1992:8-13.
[5] KANAMORI H. Shaking Without Quaking[J]. Science,1998,279(5359):2063-2064.
[6] 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.
(参考文献0分,缺失 -1分)