摘 要
本文以“Hello”程序为研究对象,致力于深入分析计算机系统中程序从编写至实验的整个流程,以及其底层运作原理。通过理论探讨与在Ubuntu环境下的实际利用相结合的研究方法,对程序生命周期的各个阶段举行了细致的分解。研究内容涵盖了从预处置惩罚、编译、汇编到链接的各个步调,详细表明了每个阶段的概念、实验命令及其输出效果。同时,深入探讨了历程管理中Shell-bash的运行机制,以及“Hello”程序在fork创建、execve实验和异常信号处置惩罚方面的具体利用。别的,本研究还依托于Intel的段式和页式内存管理机制,结合TLB、四级页表、三级Cache等技能,全面阐释了逻辑地址向物理地址的转换过程,以及“Hello”历程在不同阶段的内存映射和动态存储分配管理。研究成果清晰地展示了“Hello”程序从源代码到可实验文件,再到实际运行的完整过程,不仅加深了对程序运行、内存管理和历程管理理论的明白,而且为软件开发、系统性能优化和故障诊断提供了理论支持和实践指导,对于计算机系统相干领域的学习和研究具有重要的参考意义。
关键词:Hello 程序;计算机系统;链接;编译流程;历程管理;存储管理
第1章 概述
1.1 Hello简介
P2P(Program to progress):
首先,将编写好的C语言源程序生存为hello.c文件(program)。接着,通过预处置惩罚器cpp处置惩罚天生hello.i文件(通常为ASCII码表现的文本文件)。随后,利用编译器ccl天生汇编文件hello.s(呆板级语言程序)。颠末编译器进一步处置惩罚后,天生可重定位目标程序hello.o(二进制文件)。末了,将hello.o与静态库中的printf.o在链接器ld的作用下,天生可实验目标程序hello。在bash中运行该程序时,bash会为其创建一个历程,即progress。
020(0 to 0):
在bash中输入./hello运行hello程序,bash通过调用fork()函数创建子历程,同时在子历程中调用execve()函数来加载hello程序,CPU为运行的hello程序分配时间片并实验逻辑控制流。当程序实验竣事后,bash接纳历程,开释内存,并删除与运行hello程序有关的数据结构,统统重新变为0。
1.2 环境与工具
硬件:64 位利用系统, 基于 x64 的处置惩罚器;3.10 GHz;16.0GRAM;477GDisk
软件:Windows 11 64位;VMware;Ubuntu 18.04,64位
开发工具:Code Blocks;edb;gdb;vim;objdump
1.3 中心效果
文件名
| 功能
| hello.i
| 预处置惩罚后得到的文本文件
| hello.s
| 编译后得到的汇编语言文件
| hello.o
| 汇编后得到的可重定位目标文件
| hello.out
| 链接后得到的可实验文件
| hello.elf
| 用readelf读取hello.o得到的ELF格式信息
| helloo.txt
| 反汇编hello.o可重定位目标文件得到的反汇编文件
| helloout.txt
| 反汇编hello.out可实验文件得到的反汇编文件
| helloout.elf
| 用readelf读取hello.out得到的ELF格式信息
| 1.4 本章小结
本章是大作业的简介性子的章节,重要介绍了此次大作业大抵的流程以及所需要的硬件,软件环境以及所需要的工具,并列出了最终大作业所得到的文件。
第2章 预处置惩罚
2.1 预处置惩罚的概念与作用
2.1.1预处置惩罚的概念
在 C 语言中,预处置惩罚是编译前的准备阶段,在实际编译开始之前实验。预处置惩罚器(如 GCC 的 cpp)会根据#开头的指令对源代码举行文本处置惩罚,如对源代码举行文本更换和宏睁开,天生新的源代码供编译器处置惩罚。
2.1.2预处置惩罚的作用
1.文件包含(#include):插入头文件内容。
2.宏更换(#define):简单文本更换(如常量、函数式宏)。
3.条件编译(#ifdef):根据条件选择编译代码块。
4.布局控制:#pragma设定编译器的状态或指示编译器完成一些特定的动作;
2.2在Ubuntu下预处置惩罚的命令
预处置惩罚命令:cpp hello.c > hello.i
2.3 Hello的预处置惩罚效果解析
利用vim hello.i指令查看该文本文件。颠末预处置惩罚后,与源程序举行对比,所有的解释都被删除了,#include 等预处置惩罚指令均已通过预处置惩罚去掉,原本的头文件全都被更换为了其原本的文件内容,hello.c中的main函数出如今hello.i的3167行的位置
2.4 本章小结
本章为预处置惩罚(cpp),为hello的一生的第一个环节,是编译前的准备阶段,删去了解释,将头文件内容更换为其原本的文件内容,为后续的步调做了充实的准备,包管了后续内容的正常举行,提高了编程的效率,使得程序员无需手动敲入这些头文件的内容。
第3章 编译
3.1 编译的概念与作用
3.1.1编译的概念
编译就是把人类能看懂的代码(如C语言)翻译成计算机能直接实验的指令(呆板码)的过程,也是将代码所构成的文本文件重新编译为计算机可实验的二进制文件的程序。
3.1.2编译的作用
1.查抄代码错误:提前发现语法和逻辑问题(如变量未定义、类型不匹配)。
2.天生可实验文件:将源代码变成能在电脑上运行的程序。
3.优化性能:主动调整代码(如归并重复计算),让程序跑得更快。
3.2 在Ubuntu下编译的命令
gcc -S -o hello.s hello.i
3.3 Hello的编译效果解析
.file 源文件
.text 代码段
.align 对齐格式
.section .rodata 只读代码段
.String 字符串类型数据
.global 全局变量
.type 符号类型
3.2.2数据
hello.c中包含常量,局部变量,全局变量三种数据。
在hello.s中,常量均以立刻数的形式表现,即$x(x为数字),如下图中的$5,$1等均为常量。
hello.s中的局部变量一样平常与赋值利用一同出现。
下图中将立刻数0存至%rbp-4对应的栈地址中,结合hello.c中的源代码可知,此处的利用应为给局部变量i赋值0。
下图中两个赋值利用将参数生存为函数内的局部变量。
- 全局变量:由.global可知,本程序中全局变量只有main函数。
3.3.3赋值
图中的movl和movq均为赋值利用,其中edi生存的是源程序中的argc变量,rsi生存的是argv的地址
下图中的movl利用为在for()循环中,为i赋初值0。其中栈地址rbp-4处生存了迭代变量i.
3.3.4算术利用
for循环中每次循环后迭代变量i举行+1算数利用
3.3.5关系利用
在hello.s中,cmpl $5, -20(%rbp)表现比较立刻数$5和argc的值,je .L2代表如果二者的值相称就跳转到L2举行下一步的程序运行,即如果argc为5就进入正常的循环
在hello.s中,存在判断小于关系的利用,在源程序中表现为for()循环中的边界条件判断。在汇编语言中表现为i与立刻数9的比较,如果i小于便是9则返回循环,否则举行下一步。
3.3.6数组/指针/结构利用
源程序中没有举行结构利用,但在参数传递过程中传递了指针数组,并且字符串的生存也利用了数组利用。将rsi中生存的64位指针数组的首地址存入栈中。
在for循环中,加载字符串指针数组首地址至寄存器rax, 并将寄存器举行+24利用偏移到argv[1](学号),将argv[1]的值存入rcx。准备在printf函数中举行调用,对应打印局部变量argv[1]. 同理可以得到argv[2],argv[3]的访问过程。
3.3.7控制转移
1.if判断
对于argc!=5的判断,利用cmpl语句举行判断,便是5直接举行跳转,不便是5是才举行括号内语句的运行。
2.for循环
在hello.s文件中,对于for语句的翻译,接纳jmp等跳转语句来实现。迭代变量i与立刻数9比较,如果i小于便是9则返回循环,否则举行下一步。
3.3.8函数利用
1.参数传递
hello.c中main函数传递了int argc, 以及字符串指针数组char* argv[],在汇编代码中就是两个寄存器的值存入栈中,即前面所提到的寄存器edi和rsi, rsi生存的是字符串指针数组的首地址。
2.函数调用
hello.c程序中利用了许多的函数,包括sleep, printf, atoi等,这些函数的调用都是通过call指令来实现的。
3.局部变量
hello.c中的局部变量为for循环中用于判断边界条件的i,当i<10时举行循环
4.函数返回
sleep函数的返回值如果在正常运行程序的环境下为0,如果休眠被信号停止,返回剩余未休眠的秒数。
printf函数正常运行返回实际打印的字符数(包括空格和换行符),错误运行返回负数(通常是 -1)
atoi函数正常运行返回转换后的整数值,错误运行时如果字符串无法转换(如 "abc"),返回 0
3.4 本章小结
通过对hello.c举行编译可以得到汇编语言文件hello.s,通过查看汇编指令并查阅源程序中的代码,可以了解到编译器是如何明白一个程序,并把诸多比较复杂的数据结构表达成更为低级的汇编语言。
第4章 汇编
4.1 汇编的概念与作用
4.1.1汇编的概念
利用汇编器(as)把天生的汇编指令逐条翻译成呆板可以辨认的形式,即呆板码。
4.1.2汇编的作用
将文本文件hello.s翻译成呆板码,并将指令打包成可重定位目标程序的二进制文件,天生hello.o
4.2 在Ubuntu下汇编的命令
汇编命令:gcc -m64 -no-pie -fno-PIC -o hello.o hello.s
4.3 可重定位目标elf格式
命令:readelf -a hello.o > hello.elf
Magic:7f 45 4c 46 02 00 00 00 00 00 00 00 00 00 00 00,其中7f 45 4c 46是 ELF 文件的魔数,用于标识这是一个 ELF 文件。02表现 64 位的 ELF 文件(01表现 32 位)。
类别:ELF64,说明这是一个 64 位的 ELF 文件。
数据:2 补码,小端序 (little endian),即数据以小端序存储,补码表现数值。
类型:EXEC (可实验文件),表现该 ELF 文件是可实验的。
系统架构:Advanced Micro Devices X86-64,表明该文件是为 X86-64 架构的 CPU 设计的。
入口点地址:0x4010f0,程序实验的起始地址。
程序头起点:64 (bytes into file),程序头在文件中的偏移量为 64 字节。
节头表:14136 (bytes into file),节头表在文件中的偏移量为 14136 字节。
标志:0x0,表现没有特别标志。
Size of this header:64 (bytes),ELF 头的大小为 64 字节。
Size of program headers:56 (bytes),每个程序头的大小为 56 字节。
Number of program headers:13,程序头的数量为 13 个。
Size of section headers:64 (bytes),每个节头的大小为 64 字节。
Number of section headers:31,节头的数量为 31 个。
Section header string table index:30,节头字符串表的索引为 30。
.interp:包含程序表明器的路径,PROGBITS类型表现该节包含可加载的程序数据,A标志表现该节可分配内存。
.note.gnu.pr...、.note.gnu.bu...、.note.ABI-tag:这些节包含与 GNU 相干的解释信息,如属性、构建 ID、ABI 版本等。
.gnu.hash:包含符号哈希表,用于快速查找符号。
.dynsym:动态符号表,包含动态链接所需的符号信息。
.dynstr:动态字符串表,存储动态符号的名称。
.gnu.version:包含符号的版本信息。
.gnu.version_r:版本需求信息,描述对其他库的版本依靠。
.rela.dyn:重定位信息,用于修正动态符号的地址。
.rela.plt:过程链接表(PLT)的重定位信息,用于修正 PLT 中符号的地址。
.init:包含程序初始化代码。
.plt:过程链接表,用于动态链接时的函数调用。
.plt.sec:PLT 的一部门,具体作用与 PLT 相干。
.text:包含程序的可实验代码,AX标志表现该节可实验且可分配内存。
.fini:包含程序终止代码。
.rodata:只读数据节,包含常量数据。
.eh_frame_hdr:异常处置惩罚帧头,用于异常处置惩罚和栈睁开。
.eh_frame:异常处置惩罚帧信息,记录函数的栈帧布局等信息。
.init_array:包含程序初始化数组,存放初始化函数指针。
.fini_array:包含程序终止数组,存放终止函数指针。
.dynamic:动态链接信息节,包含共享库依靠、符号表、重定位信息等。
.got:全局偏移表,用于动态链接时解析全局符号的地址。
.got.plt:PLT 的全局偏移表,用于 PLT 中符号的地址解析。
.data:包含已初始化的全局变量和静态变量,WA标志表现该节可写且可分配内存。
.bss:包含未初始化的全局变量和静态变量,WA标志表现该节可写且可分配内存,但在文件中不占用实际空间,运行时在内存中分配。
.comment:包含编译器版本等解释信息。
.symtab:符号表,包含程序中定义和引用的符号信息。
.strtab:字符串表,存储符号表中符号的名称。
.shstrtab:节头字符串表,存储节头中节名的字符串。
重定位是将符号引用与实际地址举行绑定的过程。
偏移量:表现需要举行重定向的代码在.text或.data节中的偏移位置
信息:包括symbol和type两部门,其中symbol占前半部门,tyoe占后半部门,symbol代表重定位到的目标在.symtab中的偏移量,type代表重定位的类型
类型:表现重定位的入口的类型
加数:计算重定位位置的辅助信息
在 ELF 文件中,.rela.dyn和.rela.plt节包含重定位信息:
.rela.dyn:
000000403fd8 000000000006 R_X86_64_GLOB_DAT 0000000000000000 __libc_start_main@GLIBC_2.34 + 0:表现在地址0x403fd8处需要举行重定位,类型为R_X86_64_GLOB_DAT(全局数据引用),符号为__libc_start_main@GLIBC_2.34,加数为 0。这意味着在链接时需要将__libc_start_main的实际地址填入该位置。
.rela.plt:
000000404000 000000000007 R_X86_64_JUMP_SLO 0000000000000000 puts@GLIBC_2.2.5 + 0:表现在地址0x404000处需要举行重定位,类型为R_X86_64_JUMP_SLO(跳转槽),符号为puts@GLIBC_2.2.5,加数为 0。这是 PLT 中用于函数调用的重定位,链接时会将puts函数的实际地址填入该位置。
雷同地,其他条目分别对应printf、getchar、atoi、exit、sleep等函数的重定位信息,用于在运行时精确调用这些函数。
通过这些重定位项,链接器可以或许将程序中对外部符号(如库函数)的引用更换为实际的内存地址,使得程序可以或许精确运行。
4.4 Hello.o的效果解析
4.4.1 反汇编分析
objdump -d -r hello.o 分析hello.o的反汇编,并请与第3章的 hello.s举行对照分析。
对比天生的hello.s中的汇编代码,可以看出其中跳转语句原本的label在反汇编后变为了相对偏移地址,即目标指令地址与即将实验的下一条指令之间的差值。
在函数调用过程中,hello.s文件表现为直接call对应的函数名,而在反汇编的到的呆板码文件中可以看到call的目标地址是当前指令的下一条指令。这是由于可重定位目标程序尚未颠末链接器的处置惩罚,调用的函数并不实际存在于实际程序中,需要进一步调用共享库利用连接器才可以具体实现。对于不确定地址的函数调用,在编译过程中计算机会将地址全部设置为0,即指向当前指令的下一条指令,并将该函数名参加符号表中,并在.rela.text中添加重定位条目,等候进一步的静态链接。
对于变量的访问,在hello.s文件中利用rip+段名称的方式访问了printf中的字符串,而对反汇编文件中此处的调用则是利用了rip+0x0的方式,原理同函数调用一样,呆板尚未对程序举行符号解析,需要先放入重定位条目中,等候之后对变量举行重新定位并写入。
4.4.2呆板语言与汇编语言
说明呆板语言的构成,与汇编语言的映射关系。特别是呆板语言中的利用数与汇编语言不同等,特别是分支转移函数调用等。
利用码(决定指令类型) + 利用数(寄存器、立刻数、内存地址)
汇编指令直接对应呆板码的利用码部门
利用数通过寄存器编码、位移量、相对地址等形式编码
相对寻址(rel8/rel32):目标地址 = 当前 PC + 偏移量
重定位项在链接时修正偏移量,确保地址精确
4.5 本章小结
本章重要对由hello.s编译的hello.o--可重定位目标程序及其反汇编文件举行了分析,查阅了ELF头和各节头表信息,并查看了反汇编后的呆板码,于hello.s的汇编代码举行了比较,发现了二者的差别,了解了编译过程的重要作用。
第5章 链接
5.1 链接的概念与作用
5.1.1链接的概念
链接是将多个目标文件(如hello.o)以及它们所依靠的库文件组合成一个可实验文件(如hello)的过程。它重要包括以下两个过程:
- 符号解析:目标文件中会包含一些符号(如函数名、变量名等)的引用,这些符号在编译时可能未被完全解析。链接器会将这些符号与其在其他目标文件或库中的定义举行匹配,确保每个符号都有明白的定义和地址。
- 重定位:编译后的目标文件中的代码和数据通常是按照相对地址举行编写的。链接器会根据可实验文件的最终布局,将这些相对地址转换为绝对地址,使得程序在运行时可以或许精确地访问代码和数据。
5.1.2链接的作用
在大型项目中,通常会有多个源文件分别编译成多个目标文件。链接器将这些目标文件组合在一起,形成一个完整的可实验程序,使各个模块可以或许相互协作,共同完成程序的功能。
当一个源文件中调用了另一个源文件中定义的函数或利用了其他源文件中定义的变量时,链接器负责在链接阶段将这些符号的引用与实际的定义举行关联,确保程序在运行时可以或许精确地找到并利用这些符号。
同时,程序每每会依靠一些标准库或第三方库。链接器可以或许将这些库文件中的相干代码和数据整合到可实验文件中,使程序可以或许利用库中提供的功能,避免了开发者重复编写大量的通用代码。
链接完成内存空间的分配:链接器会根据目标文件和库文件中的信息,为程序中的代码、数据等分配在内存中的合适位置,确定它们在运行时的地址,包管程序可以或许精确地加载和运行。
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等列出其各段的基本信息,包括各段的起始地址,大小等信息。
类型(Type):表现段的类型,如 PROGBITS 表现包含程序代码或数据的段,NOBITS 表现不占用文件空间但需要在内存中分配的段; 起始地址(Address):段在内存中的起始地址。
文件偏移(Off):段在文件中的起始偏移。
大小(Size):段的大小。
旌旗(Flg):表现段的属性,如 R 表现可读,W 表现可写,X 表现可实验。
对齐(Al):段的对齐要求。
NEEDED 表现程序运行时需要的共享库,这里是 libc.so.6。
INIT 和 FINI 分别是初始化和终止函数的地址。
INIT_ARRAY 和 FINI_ARRAY 分别是初始化和终止函数数组的地址及其大小。
HASH 是 GNU 哈希表的地址,用于加速符号查找。
SYMTAB 和 STRTAB 分别是动态符号表和字符串表的地址。
PLTGOT 是 PLT GOT 的地址,PLTRELSZ 和 JMPREL 与 PLT 重定位相干。
RELA、RELASZ 和 RELAENT 与重定位条目相干。
VERNEED 和 VERSYM 与符号版本控制相干。
- 重定位表:记录需要在运行时调整的地址,通过重定位表,链接器和加载器可以或许精确地解析符号引用,将目标文件中的相对地址更换为绝对地址,从而确保程序在内存中精确运行。
5.4 hello的虚拟地址空间
利用edb加载hello,查看本历程的虚拟地址空间各段信息,并与5.3对照分析说明。
Ctrl+M打开Memory Regions查看内存映射。
1.rodata节
ELF 文件中的.rodata(起始地址0000000000402000)被加载到虚拟地址 0x400000,其权限为r(只读)
2.init节:
3.text节:
ELF 文件中的.text段(起始地址0000000000401100)被加载到虚拟地址 0x401000,其权限为r-x(可读可实验),实际加载大小可能因内存页对齐(通常大小为4KB)而扩展。
从401000开始到401fff
4.data节和bss节
ELF文件中的.data段(起始地址0000000000404030)和.bss段(起始地址0000000000404040)被包含在虚拟地址范围 0x403000-0x405000 内,权限为rw(可读写)
5.5 链接的重定位过程分析
5.5.1 objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。
链接器将puts解析为 PLT(过程链接表)中的地址0x4010a0。
链接器将全局变量地址修正为0x402008(数据段中的实际位置)。
链接的过程为:
- 归并:将多个目标文件和库的代码、数据段归并。
- 解析:确定所有符号的最终地址。
- 修正:更新代码中的所有地址引用。
- 动态绑定:为共享库设置运行时加载机制。
5.5.2结合hello.o的重定位项目,分析hello中对其怎么重定位的。
以调用函数puts为例,可以看出在可实验文件反汇编代码中,puts@plt函数名地址为0x4010a0, 由小端法可知fffffe92即-16d, 由此计算可得下一条指令0x40120e减去相对偏移量即可得到实际函数的虚拟地址。
重定位目标程序中的反汇编代码:
相较于原来的hello.o程序,hello.out中增加了puts@plt, sleep@plt等函数,这是因为动态链接器将共享库中的hello.c用到的函数参加进了可实验文件中。
5.6 hello的实验流程
利用gdb/edb实验hello,说明从加载hello到_start,到call main,以及程序终止的所有过程(重要函数)。请列出其调用与跳转的各个子程序名或程序地址。
利用edb单步调试运行程序,观察其调用的函数,这里可以发如今调用main之前重要举行了初始化的工作调用了_init,在这个函数之后动态链接的重定位工作已经完成,我们可以看到在这个函数的调用之后是一系列在这个程序中所用到的库函数(printf,exit,atoi等等)这些函数实际上在代码段并不占用实际的空间只是一个占位的符号,实际上他们的内容在共享区(高地址)处。之后调用了_start这个就是起始的地址,准备开始实验main的内容。
下面列出了部门函数的名称与地址:
00000000004010a0 <puts@plt>
00000000004010b0 <printf@plt>
00000000004010c0 <getchar@plt>
00000000004010d0 <atoi@plt>
00000000004010e0 <exit@plt>
00000000004010f0 <sleep@plt>
5.7 Hello的动态链接分析
分析hello程序的动态链接项目,通过edb/gdb调试,分析在动态链接前后,这些项目标内容变化。要截图标识说明。
编译器没有办法预测函数的运行时地址,以是需要添加重定位记录,等候动态链接器处置惩罚,为避免运行时修改调用模块的代码段,链接器接纳延长绑定的策略。动态链接器利用过程链接表PLT+全局偏移量表GOT实现函数的动态链接,在GOT中存放函数目标地址,PLT利用GOT中地址跳转到目标函数,在加载时,动态链接器会重定位GOT中的每个条目,使得它包含目标的精确的绝对地址。
根据节头表中给出的偏移地址可以看到.got和.got.plt段位置在0x403fd8和0x403fe8处, 即可在data dump中观察数据的变化。
调用dl_init前后的.got和.got.plt:
5.8 本章小结
本章介绍了链接的概念和作用,并且读取了可实验文件hello的ELF格式文本,与可重定位目标程序的ELF头等信息举行了比较,探究了链接的过程以及重定位的原理。之后又比较了可重定位目标程序以及可实验文件的反汇编代码,分析了二者的区别,进一步加深了对于重定位以及动态链接的明白。
(第5章1分)
第6章 hello历程管理
6.1 历程的概念与作用
6.1.1历程的概念
历程是指在系统中正在运行的一个程序实例,是对正在运行程序的一种抽象。它由程序、数据和历程控制块(PCB)构成。程序是历程运行的代码底子,数据是程序处置惩罚的对象,而历程控制块则是利用系统用于管理历程的焦点数据结构,包含了历程的各种信息,如历程 ID、状态、优先级、程序计数器、寄存器值等。
6.1.2历程的作用
1.资源分配:历程是利用系统举行资源分配的基本单位。利用系统为每个历程分配独立的内存空间、CPU 时间片、文件描述符等系统资源,确保各个历程可以或许独立、安全地运行,互不干扰。例如,当多个历程同时运行时,利用系统会根据肯定的算法为每个历程分配内存,使它们可以或许存储各自的代码和数据;同时,通过调治算法分配 CPU 时间片,让每个历程都有机会利用 CPU 举行计算。
2.并发实验:允许多个历程并发实验是现代利用系统的重要特性。多个历程可以在同一时间间隔内同时运行,提高了系统的资源利用率和团体性能。例如,在计算机同时运行多个应用程序时,每个应用程序都作为一个历程在后台或前台运行,用户可以在不同的应用程序之间举行切换,感觉多个程序在同时运行。实际上,CPU 在多个历程之间快速切换,由于切换速度很快,给用户造成了并发实验的错觉。
3.程序隔离:历程为程序提供了隔离的实验环境。每个历程都有自己独立的地址空间,一个历程无法直接访问另一个历程的内存空间和数据,这包管了历程之间的安全性和稳固性。如果一个历程出现错误或异常,通常只会影响到该历程本身,而不会导致整个系统瓦解,从而提高了系统的可靠性。
4.实现模块化和条理化设计:在软件开发中,历程可以用于实现模块化和条理化的设计。不同的功能模块可以作为不同的历程来实现,它们之间通过历程间通讯(IPC)机制举行协作和数据交换。这种设计方式使得软件系统的结构更加清晰,易于维护和扩展。例如,在一个大型的分布式系统中,不同的服务可以作为不同的历程运行在不同的节点上,它们通过网络举行通讯,共同完成复杂的使命。
6.2 简述壳Shell-bash的作用与处置惩罚流程
Shell 是用户与利用系统内核之间的接口程序,Bash 是 Shell 的一种常见类型,也是 Linux 系统中默认的 Shell。
作用:
1.命令表明实验:用户在终端输入的命令由 Bash 举行表明,它会分析命令的语法和参数,并将其传递给利用系统内核来实验相应的利用。例如,用户输入ls -l命令,Bash 会辨认这是列出当前目录下文件详细信息的命令,并调用相应的系统函数来实现该功能。
2.脚本编写:Bash 支持将一系列命令编写成脚本文件,实现主动化使命处置惩罚。通过编写脚本,可以将多个复杂的利用组合在一起,按照预定的顺序实验,提高工作效率。例如,编写一个备份脚本,定期备份指定目录下的文件。
3.环境设置与管理:Bash 用于设置和管理用户的工作环境,包括设置环境变量、别名、命令路径等。例如,通过设置PATH环境变量,用户可以指定系统查找命令的路径;通过设置别名,用户可以为常用的命令设置简短的别名,方便利用。
处置惩罚流程:
1.读取输入:Bash 启动后,会在终端等候用户输入命令。用户输入的内容会被 Bash 读取,以行为单位举行处置惩罚。
2.解析命令:Bash 对读取到的命令举行语法分析,将其分解为命令名称、参数和选项等部门。它会查抄命令的格式是否精确,以及是否存在语法错误。例如,对于ls -l /home命令,Bash 会辨认出ls是命令名称,-l是选项,/home是参数。
3.查找命令:根据命令名称,Bash 在系统的命令路径(由PATH环境变量指定)中查找对应的可实验文件。如果找到命令对应的可实验文件,就准备实验该命令;如果找不到,Bash 会显示错误信息,提示用户该命令不存在。
4.实验命令:找到命令后,Bash 会创建一个子历程来实验该命令。子历程会继承父历程(Bash)的一些环境信息,如环境变量等。命令在子历程中实验,实验过程中可能会产生输出,这些输出会被返回给 Bash,并显示在终端上。
5.等候命令完成:Bash 会等候命令实验完成,在命令实验期间,它会停息接受新的命令输入,直到当前命令实验竣事。命令实验完成后,Bash 会获取命令的退出状态,用于判断命令是否实验乐成。通常,退出状态为 0 表现命令实验乐成,非 0 表现命令实验过程中出现了错误。
6.循环处置惩罚:完成一个命令的实验后,Bash 会再次回到等候用户输入的状态,重复上述过程,以处置惩罚用户输入的下一个命令。这样,用户就可以通过不绝输入命令,与利用系统举行交互,完成各种使命。
6.3 Hello的fork历程创建过程
打开Shell, 输入参数./Hello 2023111569 孙海洋 17331613874 4,带参数实验hello程序。
fork历程创建过程:首先,带参实验当前目录下的可实验文件hello,父历程会通过fork函数创建一个新的运行的子历程hello。子历程获取了与父历程的上下文,包括栈、通用寄存器、程序计数器,环境变量和打开的文件相同的一份副本。子历程与父历程的最大区别是有着跟父历程不一样的PID,子历程可以读取父历程打开的任何文件。当子历程运行竣事时,父历程如果仍然存在,则实验对子历程的接纳,否则就由init历程接纳子历程。
6.4 Hello的execve过程
execve函数加载并运行一个可实验目标文件,这里是hello,而且带参数列表argv[]和环境变量列表envp[],只有当找不到可实验目标文件时才会但回到调用程序,否则execve函数会将控制转移到新的可实验文件去实验,与fork函数调用一次返回两次不同,execve函数调用一次从不返回。
调用函数fork创建新的子历程之后,子历程会调用execve函数,在当前历程的上下文中加载并运行一个新程序hello。它将删除该历程的代码和地址空间内的内容并将其初始化,然后通过跳转到程序的第一条指令或入口点来运行该程序。将私有的区域映射进来,例如打开的文件,代码、数据段,然后将公共的区域映射进来。后面加载器跳转到程序的入口点,即设置PC指向_start 地址。_start函数最终调用hello中的 main 函数,这样,便完成了在子历程中的加载。
6.5 Hello的历程实验
结合历程上下文信息、历程时间片,阐述历程调治的过程,用户态与焦点态转换等等。
用户向Shell输入可实验目标文件hello及其参数,运行程序时,Shell首先会调用fork函数创建一个新的子历程,然后在这个新历程的上下文中调用execve函数加载可实验目标文件hello。
hello历程将提供两个关键的抽象——
一个独立的逻辑控制流,好像我们的程序独占地利用处置惩罚器。
一个私有的地址空间,好像我们的程序独占地利用内存系统。
而时间片,则被表述为多个逻辑控制流重叠时又称并发运行时,由于多个历程之间轮流运行导致产生的逻辑流分段,也即多使命或时间分片。
对于用户模式和内核模式,设置模式位时,历程运行在内核模式,内核模式下的历程可以或许实验指令会合的任何指令,并且可以访问系统中任何内存位置。没有设置模式位时,历程就运行在用户模式中,用户模式下的历程不允许实验特权指令,也不允许用户模式中的历程直接引用地址空间中内核区内的代码和数据。
对于上下文切换,利用系统内核利用上下文切换来实现多使命。内核为每个历程维持一个上下文。上下文就是内核重新启动先前一个被强占的历程所需要的状态。在历程实验的某些时刻,内核可以决定抢占当前历程,并重新开始一个先前被抢占了的历程。这种决策就叫做调治,是由内核中称为调治器的代码处置惩罚的。
历程实验过程:
在程序运行时,Shell为hello fork了一个子历程,这个子历程与Shell有独立的逻辑控制流。在hello的运行过程中,若hello历程不被抢占,则正常实验;若被抢占,则进入内核模式,举行上下文切换,转入用户模式,调治其他历程。直到当hello调用sleep函数时,为了最大化利用处置惩罚器资源,sleep函数会向内核发送请求将hello挂起,并举行上下文切换,进入内核模式切换到其他历程,切换回用户模式运行抢占的历程。与此同时,将 hello 历程从运行队列参加等候队列,由用户模式变成内核模式,并开始计时。当计时竣事时,sleep函数返回,触发一个停止,使得hello历程重新被调治,将其从等候队列中移出,并内核模式转为用户模式。此时 hello 历程就可以继承实验其逻辑控制流。
6.6 hello的异常与信号处置惩罚
hello实验过程中会出现哪几类异常,会产生哪些信号,又怎么处置惩罚的。
程序运行过程中可以按键盘,如不绝乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处置惩罚。
正常运行:将打印8次信息输入回车竣事程序。
乱按及回车时不影响程序运行,程序运行竣事后除去一个回车会被作为竣事标志,别的输入均将被作为Shell的指令输入至命令行中。
中途按下ctrl-Z,Shell历程中收到了SIGSTP信号,Shell提示停止信息并挂起hello历程,利用ps和jobs查看hello历程,可以验证hello确实被挂起而并没有被接纳,并且作业编号为1.查看历程树,输入pstree,输入fg 1,则将hello程序规复实验,实验尚未实验完的语句,并完成历程接纳。输入kill杀死历程后再调用jobs查看,发现确实历程被杀死
输入ctrl+c,Shell将受到SIGINT信号,并终止历程
6.7本章小结
本章从历程的角度介绍了hello的实验过程,了解了Shell的功能和利用方法,明白了Shell是如何利用fork函数创建子历程并且调用execve函数加载可实验程序的,同时实操了利用Shell命令行管理hello历程,对历程的知识有了更深的了解。
第7章 hello的存储管理
7.1 hello的存储器地址空间
结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。
7.1.1物理地址
物理地址是实实在在存在于硬件层面的地址。以内存为例,它是内存中每个存储单元真正的地址,用于在物理内存芯片上定位和访问数据。就如同实际生活中每栋房子都有一个唯一的实际地址,通过这个地址就能找到对应的房子。在计算机里,物理地址是内存控制器用于访问物理内存的最终地址。
7.1.2逻辑地址
逻辑地址是由程序产生的地址。在编写程序时,我们利用的都是逻辑地址。这个地址是相对于当前历程的起始地址而言的,对程序员可见,并且在程序内部是一连的。可以把它想象成一个虚拟的 “房间号”,程序认为自己在利用这样的地址来访问数据。由程序产生的与段相干的偏移地址部门,hello中要颠末寻址方式的计算或变更才得到内存储器中的实际有效地址(物理地址)。
7.1.3线性地址
线性地址是逻辑地址颠末分段机制转换后得到的地址。分段机制会把逻辑地址拆分成段选择符和段内偏移量,然后通过查找段描述符表,将这两部门组合成线性地址。线性地址可以看作是一个中心层的地址。
7.1.4虚拟地址
虚拟地址是通过分页机制从线性地址转换而来的。分页机制把线性地址分别成页目录、页表和页内偏移量,再通过查询这些表,将线性地址映射为物理地址。虚拟地址让每个历程都觉得自己拥有一连完整的地址空间,而实际上这些地址可能被映射到物理内存的不同位置,乃至可能暂时存储在磁盘上。
7.2 Intel逻辑地址到线性地址的变更-段式管理
在 Intel x86 架构中,段式内存管理是将逻辑地址转换为线性地址的过程。在分段机制下,逻辑地址由两部门构成:
段选择符 (Segment Selector):16 位,用于标识一个段
偏移量 (Offset):32 位,用于在段内定位具体位置
段式管理的焦点是通过段选择符找到对应的段描述符,再结合偏移量计算出线性地址。
段选择符结构
段索引 (Index):高 13 位,用于在段描述符表中查找对应的段描述符
TI(Table Indicator):2 位,指示利用哪个段描述符表
0 = GDT (全局描述符表)
1 = LDT (局部描述符表)
RPL(Request Privilege Level):2 位,表现请求的特权级别 (0-3)。RPL=00,为第0级,位于最高级的内核,RPL=11,为第3级,位于最低级的用户状态
段描述符结构
段描述符是一个 64 位的数据结构,存储在 GDT 或 LDT 中,包含段的基地址、边界和属性:
段基地址 (Base Address):32 位,指定段在内存中的起始位置
段边界 (Limit):20 位,指定段的大小
访问权限 (Access Rights):描述段的类型 (代码段 / 数据段)、特权级等
G(Granularity):粒度标志,决定段边界的单位 (字节或 4KB 页)
逻辑地址到线性地址的转换过程
转换公式为:线性地址 = 段基地址 + 偏移量
具体步调如下:
1.解析段选择符:
提取 TI 位,确定利用 GDT 还是 LDT
提取段索引,计算在描述符表中的位置 (段索引 × 8)
提取 RPL,用于特权级查抄
2.查找段描述符:
根据 TI 选择 GDT 或 LDT
根据段索引找到对应的段描述符
3.验证访问权限:
查抄段是否存在 (Present 位)
举行特权级查抄 (RPL 与段的 DPL)
查抄偏移量是否超出段边界
4.计算线性地址:
从段描述符中提取段基地址
将段基地址与偏移量相加得到线性地址
7.3 Hello的线性地址到物理地址的变更-页式管理
Linux下,虚拟地址(VA)到物理地址(PA)的转化与翻译是依靠页式管理来实现的,虚拟内存作为内存管理的工具。概念上而言,虚拟内存被构造为一个由存放在磁盘上的N个一连的字节大小的单元构成的数组. 磁盘上数组的内容被缓存在物理内存中 (DRAM cache)这些内存块被称为页 (每个页面的大小为P = 2p字节)。
通过段式管理可以得到线性地址/虚拟地址,虚拟地址可被分为两个部门:VPN(虚拟页号)和VPO(虚拟页偏移量),根据计算机系统的特性可以确定VPN与VPO的具体位数,由于虚拟内存与物理内存的页大小相同,因此VPO与PPO(物理页偏移量)同等。而PPN(物理页号)则需通过访问页表中的页表条目(PTE)获取,如图所示。
若PTE的有效位为1,则发生页命中,可以直接获取到物理页号PPN,PPN与PPO共同构成物理地址。
若PTE的有效位为0,说明对应虚拟页没有缓存到物理内存中,产生缺页故障,调用利用系统的内核的缺页处置惩罚程序,确定捐躯页,并调入新的页面。再返回到原来的历程,再次调用导致缺页的指令。此时发生页命中,获取到PPN,与PPO共同构成物理地址。
7.4 TLB与四级页表支持下的VA到PA的变更
多级页表的概念:如果在计算机内部页表是将所有的项都全部表现出来并且都存在主存中,那么会出现许多问题,假设:4KB (212) 页面, 48位地址空间, 8字节 PTE 那么将需要一个大小为 512 GB 的页表!这512GB的页表如果存放在主存中,这笔开销是十分巨大的。但是事实是由于程序良好的局部性和程序的每一个段并不是一连的,如下图所示,中心会有大量的页表的映射是用不上的,即每次我们访问的页面大概率只有几个为了办理这个问题,我们可以接纳页表分级的策略减少常驻内存的页表的开销,依照多级cacahe的原理,将页表举行一级一级缓存。
针对Intel Core i7 CPU研究VA到PA的变更。
Intel Core i7 CPU的基本参数如下:
- 虚拟地址空间48位(n=48)
- 物理地址空间52位(m=52)
- TLB四路十六组相连
- L1,L2,L3块大小为64字节
- L1,L2八路组相连
- L3十六路组相连
- 页表大小4KB(P=4x1024=2^12),四级页表,页表条目(PTE)大小8字节
由上述信息可以得知,VPO与PPO有p=12位,故VPN为36位,PPN为40位。单个页表大小4KB,PTE大小8字节,则单个页表有512个页表条目,需要9位二进制举行索引,而四级页表则需要36位二进制举行索引,对应着36位的VPN。TLB有16组,故TLBI有t=4位,TLBT有36-4=32位。
如图所示, CPU产生虚拟地址VA,并将其传送至MMU,MMU利用前36位VPN作为TLBT(前32位)+TLBI(后4位)在TLB中举行匹配,若命中,则得到PPN(40bit)与VPO(12bit)组合成物理地址PA(52bit)。若TLB没有命中,则MMU向页表中查询,由CR3确定第一级页表的起始地址,VPN1(9bit)确定在第一级页表中的偏移量,查询出PTE,如果在物理内存中且权限符合,则实验下一步确定第二级页表的起始地址,以此类推,最终在第四级页表中查询到PPN,与VPO组合成PA,并向TLB中添加条目。多级页表的工作原理展示如下:
若查询PTE时发现不再物理内存中,则引发缺页故障。若发现权限不敷则引发段错误。
7.5 三级Cache支持下的物理内存访问
在完成了7.4中的VA到PA的转换后,得到了一个52位的物理地址。首先图中的L1cache有64组,每组8行,每个块的大小为64B,以是块偏移为6,s=6,以是将这52位的地址分为3部门,分别是40位的CT高速缓存标志,6位的CI高速缓存索引,6位的CO缓冲块内的字节偏移量。
- 先根据CI得到需要查找的组序号
- 查找该组的8个行,根据CT与行中的标志位举行匹配,且行的有效为为1,则该高速缓存行命中。
- 命中后,由CO得到我们需要的字节起始位置,取出返回CPU即可。
- 若不命中,就需要到L2,L3,乃至于到主存中去寻找请求的块,然后将该请求的块放置或更换到组索引所对应的那组的某一个高速缓存行中,如有一行的有效位位0,就放置在该行,若所有行的有效位都为1,就选择一个最近倒霉用的一行举行更换。
7.6 hello历程fork时的内存映射
子历程得到与父历程用户级虚拟地址空间相同的(但是独立的)一份副本,包括代码和数据段、堆、共享库以及用户栈。子历程还得到与父历程任何打开文件描述符相同的副本,这就意味着当父历程调用 fork 时,子历程可以读写父历程中打开的任何文件。该子历程除了PID是最大的不同以外,别的与父历程险些没有什么不同,shell会在这个新子历程的上下文中运行这个子历程。
当fork函数被当前历程hello调用时,内核为新历程hello创建各种数据结构,并分配给它一个唯一的PID。为了给这个新的hello创建虚拟内存,它创建了当前历程的mm_struct、区域结构和页表的原样副本。它将两个历程中的每个页面都标志为只读,并将两个历程中的每个区域结构都标志为私有的写时复制。
7.7 hello历程execve时的内存映射
execve函数调用驻留再内核区域的启动加载器代码,在当前历程中加载并运行包含在可实验目标文件hello中的程序,步调如下:
删除已存在的用户区域,删除当前历程中虚拟地址的用户部门中的已存在的区域结构。映射私有区域,为新程序的代码,数据,.bss段和栈区域创建新的区域结构,所有这些新的区域都是私有的、写时复制的。代码和数据区域被映射为hello文件中的.text和.data区,bss区域是请求二进制0的,映射至匿名文件,其大小包含在hello中,栈和堆地址也是请求二进制0的,初始长度为0.映射共享区域,hello程序和共享对象libc.so动态链接,然后再映射到用户虚拟地址空间中的共享区域内。末了设置程序计数器(PC),使之指向代码区域的入口点。
7.8 缺页故障与缺页停止处置惩罚
缺页故障处置惩罚:DRAM缓存不命中称为缺页,缺页异常调用内核中缺页异常处置惩罚程序,该程序从内存选择一个捐躯页,将其复制回磁盘,随后内核将所需页面复制到内存相应位置,更新页表后返回,重新启动导致缺页的指令,该指令会把导致缺页的虚拟地址重发送到地址翻译硬件。
缺页停止处置惩罚:缺页处置惩罚程序是系统内核中的代码,选择一个捐躯页面,如果这个捐躯页面被修改过,那么就将它交换出去,换入新的页面并更新页表。当缺页处置惩罚程序返回时,CPU重新启动引起缺页的指令,这条指令再次发送VA到MMU,这次MMU就能正常翻译VA了。
7.9动态存储分配管理
动态内存分配器维护着一个称为堆的历程的虚拟内存区域。分配器将堆视为一组不同大小的块的聚集来维护。每个块就是一个一连的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序利用。空闲块可用来分配。空闲块保持空闲,直到它显式地被应用所分配。一个已分配的块保持已分配状态,直到它被开释,这种开释可以由应用程序显式实验或内存分配器自身隐式实验。
分配器基天职为两种:显式分配器、隐式分配器。
- 显式分配器:要求应用显式地开释任何已分配的块。
- 隐式分配器:要求分配器检测一个已分配块何时不再利用,那么就开释这个块,主动开释未利用的已经分配的块的过程叫做垃圾收集。
下面介绍动态存储分配管理中较为重要的概念:
隐式链表:
堆中的空闲块通过头部中的大小字段隐含地连接,分配器通过遍历堆中所有的块,从而间接遍历整个空闲块的聚集。
显式链表:
在每个空闲块中,都包含一个前驱(pred)与后继(succ)指针,从而减少了搜刮与适配的时间。
带边界标志的归并:
采取利用边界标志的堆块的格式,在堆块的末端为其添加一个脚部,其为头部的副本。添加脚部之后,分配器就可以通过查抄前面一个块的脚部,判断前面一个块的起始位置和状态。从而实现快速归并,减小性能消耗。
分离存储:
维护多个空闲链表,其中,每个链表的块具有相同的大小。将所有可能的块大小分成一些等价类,从而举行分离存储。
7.10本章小结
本章对于hello运行过程中的内存分配管理做了概括式的介绍,同时阐述了虚拟地址向物理地址的转换方式,借助TLB和多级页表的映射方法,同时讲解了如何对hello历程利用fork和execve函数,并对其内存的映射过程做了表明。并介绍了缺页故障、缺页停止处置惩罚等概念。
结论
- 预处置惩罚:hello.c文件颠末cpp预处置惩罚器处置惩罚,天生hello.i文件,此阶段删去解释,更换头文件内容。预处置惩罚器根据以#开头的指令对源代码举行文本处置惩罚。例如,#include指令会将对应的头文件内容插入到源代码中,#define指令会举行宏更换等利用。这一阶段为后续的编译过程做好了准备,使得源代码更加规范和完整。
- 编译:hello.i文件经gcc编译,输出hello.s汇编文件,完成了语法查抄、代码优化等利用。编译器将预处置惩罚后的文本文件翻译成汇编语言文件。在此过程中,编译器会对代码举行语法分析和语义分析,查抄代码是否存在错误,并举行肯定的优化处置惩罚。优化的目标是提高程序的运行效率和质量,例如通过消除冗余代码、归并变量等手段。
- 汇编:hello.s经汇编器处置惩罚,天生可重定位目标程序hello.o,将汇编语言代码转换为呆板码。汇编器将汇编语言指令逐条翻译成呆板代码,并天生可重定位目标文件。可重定位目标文件中的代码和数据是按照相对地址举行构造的,包含了程序中各个函数和全局变量的定义,以及它们之间的引用关系等信息。
- 链接:hello.o与printf.o等文件经链接器处置惩罚,天生可实验目标程序hello,完成了符号解析和重定位。链接器将多个目标文件以及它们所依靠的库文件组合成一个完整的可实验文件。它将各个目标文件中的符号引用与实际的定义举行匹配,并根据可实验文件的最终布局,将相对地址转换为绝对地址,使得程序在运行时可以或许精确地访问代码和数据。
- 历程创建:在bash中运行hello程序,bash调用fork()函数创建子历程,并在子历程中调用execve()函数加载hello程序。当用户在命令行输入./hello命令时,bash shell会创建一个新的子历程。这个子历程通过调用execve()函数来加载并运行hello程序。execve()函数会将hello程序的代码和数据加载到子历程的内存空间中,并准备好程序运行所需的环境。
- 程序实验:CPU实验hello程序,按照逻辑控制流举行运算、判断、跳转等利用,访问内存数据,调用系统函数,最终完成程序功能。程序在CPU上运行时,会按照指令的顺序依次实验。CPU根据程序计数器(PC)的值获取下一条要实验的指令,然后举行解码和实验。在实验过程中,程序会访问内存中的数据,举行各种运算和判断,并根据效果举行相应的跳转利用。同时,程序还会调用系统提供的函数,如printf()等,来完成输入输出等利用。
- 历程终止与资源接纳:程序实验竣事后,bash接纳历程,开释内存,并删除与运行hello程序相干的数据结构。当hello程序实验完毕后,它会向利用系统发送一个退出信号。bash shell会接纳这个子历程所占用的资源,包括内存、文件描述符等。利用系统会清算与该历程相干的数据结构,如历程控制块(PCB)等,为其他历程开释系统资源。
2.通过对hello程序的研究,我对计算机系统工作原理及各阶段细节有了更深入的明白。计算机系统的复杂性和良好性让我对其设计与实现有了更深切的感悟。它不仅要有高效的处置惩罚器、存储器等硬件支持,还要具备美满的利用系统和工具链。各部门精密协作,才能顺利完成使命。
在系统设计上,我认为应强化模块化设计,提升可维护性与可扩展性。例如,可以将利用系统的设计分为多个独立的模块,如历程管理模块、内存管理模块、文件系统模块等。每个模块都有明白的接口和职责,相互之间通过定义好的接口举行通讯。这样可以方便系统的维护和升级,当需要修改或扩展某个功能时,只需对相应的模块举行修改即可,而不会影响到其他模块的正常运行。
在实现方法上,要注重性能优化,要接纳先辈的编译技能和链接技能,提高程序运行效率。例如,编译器可以接纳更智能的代码优化算法,如全局优化、循环优化等,来减少代码的冗余和提高代码的实验效率。链接器可以接纳增量链接等技能,减少链接时间,提高开发效率。同时,还可以利用现代处置惩罚器的多核特性,设计并实现多线程并发的程序,提高程序的实验速度。
同时,要善于运用现代工具和技能,如更智能的调试器、更高效的编译器等,提升开发效率和程序质量。例如,可以利用集成了静态代码分析、语法高亮、代码补全等功能的集成开发环境(IDE),资助程序员更快地编写出高质量的代码。还可以利用主动化测试工具,对程序举行各种测试,及时发现和修复程序中的缺陷,提高程序的可靠性和稳固性。
总之,通过对hello程序的深入研究和分析,我不仅对计算机系统的各个阶段有了更详细的了解,还对如何设计和实现一个高效的计算机系统有了更深刻的感悟。在以后的学习和工作中,我将不绝探索和实践,努力提升自己在计算机系统领域的技能水平。
附件
作用:源代码文件,包含用 C 语言编写的程序代码。
说明:源代码。
作用:预处置惩罚后的文件。
说明:由预处置惩罚器(cpp)天生,包含了颠末宏更换、头文件插入等预处置惩罚利用后的代码。
作用:汇编语言文件。
说明:由编译器(gcc)将预处置惩罚后的文件编译天生,包含了汇编指令。
作用:可重定位目标文件。
说明:由汇编器(as)将汇编语言文件汇编天生,包含了呆板码和符号表等信息,但尚未链接。
作用:可实验文件。
说明:由链接器(ld)将目标文件和库文件链接天生,可以直接在利用系统上运行。
作用:ELF 格式信息文件。
说明:由 readelf 工具读取 hello.o 文件天生,包含了 ELF 格式的详细信息,如段头部、程序头部等。
作用:反汇编文件。
说明:由 objdump 工具对 hello.o 文件举行反汇编天生,用于查看汇编代码和呆板码的对应关系。
作用:反汇编文件。
说明:由 objdump 工具对 hello.out 文件举行反汇编天生,用于查看可实验文件的汇编代码。
作用:ELF 格式信息文件。
说明:由 readelf 工具读取 hello.out 文件天生,包含了可实验文件的 ELF 格式详细信息。
参考文献
为完本钱次大作业你翻阅的册本与网站等
[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.
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |