计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 计算机科学与技术
学 号
班 级
学 生
指 导 教 师 刘宏伟
计算机科学与技术学院
2022年5月
摘 要
本篇文章介绍了hello作为一个程序从程序到进程,从零到零的过程。他经历了以下的步骤:预编译,编译,汇编,链接等步骤生成了一个可执行文件。在以这个可执行文件视角,介绍了程序作为进程的生命历程,本篇文章以《深入了解计算机系统》一书为参考将这些阶段讲述出来。
关键词:计算机系统;hello程序的一生;底层
目 录
第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(program to process):即从程序变为进程。hello程序从用户在编辑器的环境下利用键盘进行输入到一个C语言文件之中,经过编译器的软件的以下几个阶段:预处理器、汇编器、编译器、链接器,从一个文本文件变成了一个可执行文件。当我们在shell中键入运行该可执行文件的命令之后,shell生成子进程,这样,可执行文件就变成了一个实际上正在运行的进程。这就是hello从程序变为进程的概述。
hello程序的020过程(zero to zero):hello在作为进程被创建之前不会占用任何资源,在进程结束之后也不占用任何资源。创建hello进程的时候,CPU为其分配内存、时间片,而hello进程被意外终止或者自然结束之后,该进程就会被shell所回收,内部创建的所有数据结构也被删除。
1.2 环境与工具
硬件环境:
Intel X86-64平台的处理器
软件环境:
Windows11 64-bit
VMware Workstation 16下运行的Ubuntu 22.04.1 LTS系统
开发与调试工具:
gcc,gedit,CodeBlocks IDE,objdump,readelf
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
hello.i: .c文件经过预处理之后的文本文件。
hello.s: hello文件编译之后的汇编文件。
hello.o: hello文件汇编之后形成的可重定位目标执行文件。
hello: 链接之后生成的可执行文件。
1.4 本章小结
本章是对整个实验过程的一个概括,简述了hello的程序生命历程,这个程序运行的环境,以及这个实验产生的中间文件。
第2章 预处理
2.1 预处理的概念与作用
概念:程序设计领域中,预处理一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。典型地,由预处理器对程序源代码文本进行处理,得到的结果再由编译器核心进一步编译。这个过程并不对程序的源代码进行解析,但它把源代码分割或处理成为特定的单位——(用C/C++的术语来说是)预处理记号用来支持语言特性(如C/C++的宏调用)。
作用:对原有的C程序进行预处理之后,程序中的一些代码被替换。例如我们引用的该库之中的内容直接加入到代码之中,我们使用宏定义#define定义的语句也直接被替换掉,还有根据#if决定是否处理之后的代码。
经过这样的处理,程序会变得更加方便阅读、修改、移植与调试,也有利于模块化的程序设计。
2.2在Ubuntu下预处理的命令
gcc -E hello.c -o hello.i(以下是在bash中输入的结果)
2.3 Hello的预处理结果解析
打开hello.i生成文件,可以看到,原来的main函数并没有发生什么大的变化,但是,这个文件与原文件相比展开到了数百行,这是一直对函数中#include进行库的搜索和代码的加入所引发的变化。
2.4 本章小结
本章主要讲述了预处理的作用,对了hello.c预处理后发生的变化。
第3章 编译
3.1 编译的概念与作用
概念:编译器将目标源程序hello.i翻译成汇编语言程序hello.s。
作用:为不同的源程序语言提供了统一的汇编语言输出,更接近于机器的底层语言,便于机器以相同的标准执行。
3.2 在Ubuntu下编译的命令
gcc -S hello.i -o hello.s
3.3 Hello的编译结果解析
以下是生成的hello.s文件截图:
- 该函数的数据:
a.两个字符串常量
分别对应原函数中的”用法: Hello 学号 姓名 秒数!\n”和”Hello %s %s\n”,这里由于含有汉字,变成了乱码的表示。
b.main函数传入的参数argv以及argc,为int和char**型变量,分别指明了传入的参数以及传入参数的个数:
在参数传递时分别保存在了%edi和%rsi寄存器中,之后作为局部变量,被程序保存在了栈中。
c.循环变量:
这里是一个循环体的初始化以及更新判断语句,其中i被存储在程序的栈当中
- 该程序的操作:
- 运算操作
为栈分配空间以及循环变量i的递增,其他基本上就是加减的操作。
- 关系跳转
一处为在循环体当中的跳转语句,这里判断i的大小,截图与原函数的对应如下
还有一处是对传入的参数的数量上的判断
在这个函数里截取与cmp相应行数有关的函数上下两行。
c.数组/指针/结构体的操作:
该函数中访问的数组主要为argv[]数组。在原函数中操作如下:
对应的汇编代码:
这里保存argv的地址到%rax当中,argv[2]的值也保存在%rdx当中,当作传入printf的第一个参数,而argv[1]则保存在%rsi中,当作传入的第二个参数
而argv[3]则在printf函数调用完之后进行计算。
通过atoi函数的调用最后存储在寄存器%edi当中
d.循环操作:这里主要是对i每次进行递增,然后进行条件判断,再决定跳转
这里用了jump to middle的策略,将for转换为了do while型。程序声明了一个局部变量i,赋初值为0,然后跳转到.L3位置,比较i的值与8的大小关系,如果i<=8,则跳转到.L4位置处,否则继续向下执行。
e.几个函数的调用
首先是printf函数的调用,这里x传参的内容与前面的数组操作重合。
atoi函数类似,传入的参数为argv[3]
以atoi返回值为第一个参量来传入sleep函数
还有getchar和put的函数调用
这里put函数直接将.LC0字符串当作第一个参数传入。
最后还有exit函数,这里传入参数1.
3.4 本章小结
本章主要讨论了从与预处理文件到汇编代码文件的过程,对汇编程序的具体执行过程深入的分析,对数据、变量赋值、类型转换、算术操作、关系操作、控制转移、指针数组操作和函数操作和原文件当中而对应语句做了详细对比和语句的分析,学习了编译器如何将抽象的高级语言转化成低级的汇编语言。
第4章 汇编
4.1 汇编的概念与作用
概念:汇编是指汇编器将以.s结尾的汇编程序翻译成机器语言指令,并把这些指令打包成可重定位目标程序格式,最终结果保存在.o 目标文件中的过程。
作用:将汇编语言翻译成机器语言,更加接近机器的底层语言。
4.2 在Ubuntu下汇编的命令
gcc hello.s -c -o hello.o
4.3 可重定位目标elf格式
利用readelf工具,列出elf各节的基本信息。这里输入readelf -a hello.o指令来获取hello.o文件的相应格式.
- 首先是ELF头结构
该部分以一个16字节的magic数为开始, 其中7f 45 4c 46描述了该文件是否满足ELF格式,后一个个字节02描述了该系统架构为x86-64,再后一个字节01表示使用小端法,第7个字节01表示ELF的主版本号。ELF 头剩下的部分包含帮助链接器语法分析和解释目标文件的信息,其中包括 ELF 头大小、目标文件类型、机器类型、节头部表的文件偏移,以及节头部表中条目的大小和数量等相关信息。
- 节头部表:
包含了各个节的相关信息,包含每一个节的名称、大小、类型、地址、偏移量、旗标、链接、信息、对齐这些比较基本的信息. 值得注意的是,在这里注明了文件中没有节组、程序头和动态节.
- 重定位节
这里的信息将用于连接符号的引用以及符号的重定义.而这里的类型就表示了这些符号的地址表示,例如,PC32表示PC相对寻址,PLT32为动态链接内容.
这里,8 条重定位信息分别是对.L0和.L1字符串、puts 函数、exit 函数、printf 函数、sleep 函数、atoi函数、getchar 函数进行重定位声明。
除了rel.text节,还包含有.rela.en_frame重定位节
- 符号表Symbol table
存放着程序定义和引用的符号信息,符号一般有包含又该模块定义且可以被其他模块引用的全局符号,有其他模块定义且可以被该模块引用的全局符号,被该模块定义却只可以被该模块引用的局部符号三类.在这里出现的是前两类,symtab并不包含后面的一类.
- Hello.o的结果解析
objdump -d -r hello.o这条指令从而得到反汇编程序:
相对于.s文件的变化如下:
- 分支跳转操作的变化:由原来的符号变成了PC相对偏移的地址(当然这也要看这个符号的寻址方式)
- 函数调用发生的微小变化:在原来的hello.s的文件当中,函数的调用call指令后面跟随的是函数的符号,即函数的名称.而在hello.o当中反汇编所得到的汇编函数,call的目标地址为当前指令的下一条,同时调用的也成为了与main函数的相对地址. 这是因为该程序调用的函数是共享库中的函数,最终需要通过动态链接器才能确定函数的运行时执行地址。在这里,call指令中的相对偏移量暂时编码0,然后在.rela.text节添加重定位条目,等待链接时由链接器确定。
- 本章小结
在这一章中介绍了汇编的概念以及作用.分析了由hello.s汇编成hello.o文件中ELF表的基本组成,以及反汇编得来的机器代码与hello.s文件的不同之处
第5章 链接
5.1 链接的概念与作用
链接的概念: 链接是将各种代码和数据片段收集并组合称为一个单一文件的过程,这个文件可被加载到内存并执行。链接可以执行于编译时,也就是在源代码被编译成机器代码时;也可以执行于加载时,也就是在程序被加载器加载到内存并执行时;甚至执行于运行时,也就是由应用程序来执行。在现代系统当中,链接是由叫做链接器(Linker)的程序自动执行的
作用:在软件开发中的角色至关重要。在这里,因为它使得分离编译成为可能. 开发者不需要一个大型应用程序组织成巨大的源文件,而是可以分解为更小,更好管理的模块,可以独立修改和编译这些模块,当我们改变这些模块中的一个时,只需简单编译,并且重新链接应用,不必重新编译其他文件。
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的格式
这里使用指令readelf -a hello > hello out.elf生成hello的ELF文件:
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。
1.ELF表头
这里的文件类型发生了变化,由REL的可重定位文件变成了EXEC的可执行文件.
2.节头
值得注意的是有了程序头和动态节的内容.
3.重定位节
4.符号表
5.4 hello的虚拟地址空间
这里用edb加载hello程序
这里用Memery Region功能查看第一个项目的地址,这里可以看见比较熟悉的Magin数,可以知道,这就是ELF文件的头
而第二段是指令的装载地址.
这里由hello可以看出这里是编码了字符串等常量数据的地方.
上面的一部分是运行时堆
5.5 链接的重定位过程分析
利用objdump -d -r hello指令执行反汇编:
1.相比于之前,重定位微程序分配了虚拟地址,这里call以及分支跳转的相关指令的指令值也发生了一定的变化,现在这些指令指向了相应的地址
2.在该函数当中,一些库函数和main的入口函数_start的函数代码也被包含了进来:
3.text段有汇编代码外,还有.init段、.plt段、.fini段,而相比而言,hello.o文件仅有.text文件有代码段
4.hello为例的重定位过程:以上可知hello中是通过给定具体的虚拟地址和添加必要的节、函数(库函数)来实现重定位。主要步骤分为两步:
a.符号定义:链接器将所有相同类型的节合并为同一类型的新的聚合节。然后,链接器将运行时内存地址赋给新的聚合节,赋给输入模块定义的每个节,以及赋给输入模块定义的每个符号。这一步完成时,程序中的每条指令和全局变量都有唯一的运行时内存地址。
b.符号引用:链接器修改代码节和数据节中对每个符号的引用,使得它们指向正确的运行时地址。要执行这一步,链接器依赖于可重定位目标模块中成为重定位条目(relocation entry)的数据结构。
5.6 hello的执行流程
在edb当中打开hello程序,有分析可得调用的子程序以及hello的执行过程:
加载hello;进入hello,调用_start call __libc_start_main;执行main函数:调用puts@plt,printf@plt、getchar@plt、atoi@plt、sleep@plt;终止hello:exit@plt
以下是这些函数的地址:
_start 04010f0
__libc_start_main 0x2ed2(%rip)
puts@plt 0401090
printf@plt 04010a0
getchar@plt 04010b0
atoi@plt 04010c0
sleep@plt 04010e0
exit@plt 04010d0
main 0401125
5.7 Hello的动态链接分析
动态链接:共享库是一个目标模块,在运行或加载时,可以加载到任意的内存地址,并和一个在内存中的程序链接。编译器没有办法预测一个由共享库定义的函数运行时的地址,因为定义它的共享模块在运行时可以加载到任意位置。GNU利用延迟绑定技术解决该问题,延迟绑定用到两个数据结构过程链接表(PLT)、全局偏移量表(GOT)。
可以在节头部表中可以找到got和plt的相关信息。
可以看到相关的地址为404000,在edb中找到相应的地址
以上为调用init前
以上为调用后的结果,可以看到数据发生了比较明显的变化,可以看到404010中出现了两个动态连接之后函数的最终的地址,注意这里的地址是以小端法来标示的。
5.8 本章小结
本章中对链接的概念及作用进行了概述、分析hello了ELF格式以及在运用edb和objdump为工具的前提之下,对hello的虚拟地址空间、重定位过程、执行流程和动态链接过程进行了一定的比较和深入分析。
第6章 hello进程管理
6.1 进程的概念与作用
概念:1.狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。
2.广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
进程的概念主要有两点:第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。
作用:提供给应用程序两个抽象:一是,一个独立的逻辑控制流,提供一个假象,好像我们的程序独占使用处理器;二是,一个私有的地址空间,提供一个假象,好像我们的程序独占地使用内存系统。
6.2 简述壳Shell-bash的作用与处理流程
作用:Shell除了能解释用户输入的命令,将它传递给内核,还可以调用其他程序,给其他程序传递数据或参数,并获取程序的处理结果。在多个程序之间传递数据,把一个程序的输出作为另一个程序的输入。Shell本身也可以被其他程序调用。由此可见,Shell是将内核、程序和用户连接了起来。
处理流程:1.用户在shell中输入相应的指令。2.命令解释器构造传入参数argc和argv。3.创建相应的子进程,地址空间与shell父进程相同,但是相互独立。4.在子进程中调用相应函数,在当前进程上下文加载并运行相应的程序。5.调用相应程序的main函数,开始程序的执行。6. 如果命令行以&符号结尾,那么作业将在后台运行,这意味着在打印提示符并等待下一个命令之前,shell 不会等待作业终止。否则,作业在前台运行,这意味着 shell 在作业终止前不会执行下一条命令行。 因此,在任何时候,最多可以在一个作业中运行在前台。 但是,任意数量的作业可以在后台运行。
6.3 Hello的fork进程创建过程
一般shell会调用fork函数常见一个子进程,在这里,先输入./hello 2021114154 1919 3
这里,经过fork函数创建子进程后,子进程可以得到和父进程用户及虚拟地址空间相同但是独立的一个副本。包括代码和数据段、堆、共享库和用户栈。子进程还获得与父进程任何打开文件描述符相同的副本,这样子进程也可以读写任何父进程打开的文件,这里而这最大的差别是他们的pid,二者并发执行。
6.4 Hello的execve过程
execve函数在当前进程的上下文中加载并运行一个新程序,原型为:
int execve(const char *filename, const char *argv[], const char *envp[])
execve加载并运行可执行文件filename,且带参数列表argv和环境变量列表envp。只有在错误的时候,或者找不到filename时才会返回。这里与fork函数的返回两次并不同。这里的filename就是我们的程序hello。
在execve加载了hello之后,它利用启动代码设置栈,并将控制传递给新程序的主函数,该主函数有如下的原型:
int main(int argc , char *argv[] , char *envp[]);
这里函数会创建一组新的代码、数据、堆和栈段。新的栈和堆段被初始化为零,通过将虚拟地址空间中的页映射到可执行文件的页大小的片,新的代码和数据段被初始化为可执行文件中的内容。最后加载器设置PC指向_start地址,_start最终调用hello中的main函数。
6.5 Hello的进程执行
上下文信息:一系列程序计数器 PC 的值的序列叫做逻辑控制流。hello进程与其他进程的执行时交错的。关键点在于hello进程与其他进程是轮流使用处理器的。每个进程执行他的流的一部分,然后被抢占(暂时挂起),然后轮到其他进程。对于运行在这些进程之一的上下文中的程序,看上去就像独占处理器一样。
内核模式和用户模式:为了保证系统安全,需要限制应用程序所能访问的地址空间范围。因而存在用户模式与内核模式的划分,内核拥有最高的访问权限,而用户态的访问权限会受到一些限制。处理器使用一个寄存器作为模式位来描述当前进程的特权。进程只有故障、中断或陷入系统调用时才会得到内核访问权限,或者使用/proc文件系统将一般信息送给进程,其他情况下始终处于用户权限之中,一定程度上保证了系统的安全性。这里一般处理器会通过控制寄存器的控制位来切换模式。
上下文切换:内核为hello进程维持了一个上下文。上下文就是内核重新启动一个被抢占的进程所需的状态,由寄存器、程序计数器、用户栈、内核栈和内核数据结构等对象的值构成。内核是利用上下文切换机制来实现多任务并实现hello与其他进程的切换。
6.6 hello的异常与信号处理
1.函数正常运行的状况:
2.Ctrl+Z的情况:
这里可以知道向进程发送了一个SIGTSTP,虽然被挂起,但可以通过ps指令可以看到进程并未终止。
3.Ctrl+C的情况:
与上面Ctrl+Z的情况相对比,可以看得出来这里进程是被终止的,当前的作业只有前面被挂起的几个hello进程,并没有此次的进程。
4.Enter的情况
这是一个异步中断,来自于运行期间不断敲入的回车,于是程序启动处理程序,完成后正常执行下一条指令,因此正常结束。
这里仍然有留在缓冲区的Enter信号,所以会输出回车。
5.乱按的情况:
乱按的字符串留到缓冲区,程序做出的反应是直接打印在屏幕上。但对于输入回车的情况而言,那么这之前到上一次输出回车的输入将会被视为命令行。
6.fg指令的使用:
首先利用Ctrl+Z来挂起程序,可以看到该进程仍在后台运行,之后利用fg、指令,可以将挂起的程序重新运行,而在这次让其正常退出,可以看到进程中已经没有hello程序。
7.kill程序的使用
在这里向一个挂起的hello进程发送终止信号
这里先将进程挂起,然后利用kill发送终止信号,可以看见此时该进程被终止。
8.pstree
输入该指令,可以看见进程树,hello也在其中。
6.7本章小结
在这一章当中,深刻的体会了hello作为一个进程的一生。从进程的基本概念,到进程是如何从shell当中诞生,同时也知道解了与其他进程相互沟通的方式。从进程创建,到函数执行,到信号的发送和进程的查询,从hello这个最基础的程序窥见了linux当中进程的机制。
结论
从hello.c这个c语言文件,到hello这个可执行文件,他经历了以下的步骤:
1.hello.c经过预编译,得到hello.i预处理文件
2.hello.i经过编译,得到汇编代码hello.s汇编文件
3.hello.s经过汇编,得到二进制可重定位目标文件hello.o
4.hello.o经过静态和动态链接,生成了可执行文件hello
同样的作为一个程序,hello的运行也蕴含着进程的各种机制。shell bash调用fork函数,生成子进程;这个子进程调用execve函数在当前进程的上下文中加载并运行新程序hello。而这个hello的程序也可以在运行的过程当中受到外界的各类影响,例如外界的信号可以让程序挂起和终止,挂起的程序也可以被重启或终止。
当然,hello的生命历程还远不止如此,其在虚拟内存、IO设备上的相关机制,由于课程时间原因并未涉及,匆忙当中也无法一一详细回顾。但是以hello这个每个程序员接触的第一个程序来作为这个实验的对象,可以让学习的人以一个程序员的角度去思考整个计算机的系统。
我们教科书的名称就叫Computer System:A Programme’s Perspective,一个程序员若想真正的了解计算机,这本书是必不可少的,其带领学习的人着眼于计算机的底层,这才是未来计算机技术人员的学习方向,也符合学校功夫到家的校训。
附件
hello.i: .c文件经过预处理之后的文本文件。
hello.s: hello文件编译之后的汇编文件。
hello.o: hello文件汇编之后形成的可重定位目标执行文件。
hello: 链接之后生成的可执行文件
参考文献
[1] Compuer System:A Programmer’s Perspective,Randal E. Bryant,David R. O’Hallaron