计算机系统基础
大作业
题 目 程序人生-Hello’s P2P
专 业 经济管理试验班
学 号 1171000317
班 级 1710003
学 生 崔津浩
指 导 教 师 史先俊
计算机科学与技术学院
2019年3月
摘 要
本文主要讲述了Hello.c从预处理到回收的一生。运用在深入理解计算机系统这门课上所学习的知识逐步分析了用户、系统、计算机硬件在Hello的一生中进行了什么样的操作,较为详细的介绍了一个程序生命周期中基于较底层的软硬件结合的实现机制,对于研究底层软硬件结合与操作系统的实现原理以及深入理解计算机系统具有一定的帮助作用。以及对于日后更好的操控计算机,编写对计算机更友好更加有效的代码奠定了基础。
关键词:计算机系统、Linux、cpu、dram、汇编、编译、内存
目 录
第1章 概述 - 4 -
1.1 HELLO简介 - 4 -
1.2 环境与工具 - 4 -
1.2.1 硬件环境 - 4 -
X64CPU i7-8750H;2.20GHz;16G RAM;128G ROM - 4 -
1.2.2 软件环境 - 4 -
1.2.3 开发工具 - 4 -
1.3 中间结果 - 4 -
1.4 本章小结 - 4 -
第2章 预处理 - 5 -
2.1 预处理的概念与作用 - 5 -
2.2在UBUNTU下预处理的命令 - 5 -
2.3 HELLO的预处理结果解析 - 5 -
2.4 本章小结 - 6 -
第3章 编译 - 7 -
3.1 编译的概念与作用 - 7 -
3.2 在UBUNTU下编译的命令 - 7 -
3.3 HELLO的编译结果解析 - 7 -
3.3.1 常量 - 7 -
3.3.2 变量 - 8 -
3.3.3 类型 - 8 -
3.3.4 赋值 - 9 -
3.3.5 类型转换 - 9 -
3.3.6 算术操作 - 9 -
3.3.7 关系操作和控制转移 - 10 -
3.3.8 数组操作 - 10 -
3.3.9 函数操作 - 10 -
3.4 本章小结 - 11 -
第4章 汇编 - 12 -
4.1 汇编的概念与作用 - 12 -
4.2 在UBUNTU下汇编的命令 - 12 -
4.3 可重定位目标ELF格式 - 12 -
4.4 HELLO.O的结果解析 - 14 -
4.5 本章小结 - 15 -
第5章 链接 - 16 -
5.1 链接的概念与作用 - 16 -
5.2 在UBUNTU下链接的命令 - 16 -
5.3 可执行目标文件HELLO的格式 - 16 -
5.4 HELLO的虚拟地址空间 - 17 -
5.5 链接的重定位过程分析 - 18 -
5.6 HELLO的执行流程 - 19 -
5.7 HELLO的动态链接分析 - 19 -
5.8 本章小结 - 19 -
第6章 HELLO进程管理 - 20 -
6.1 进程的概念与作用 - 20 -
6.2 简述壳SHELL-BASH的作用与处理流程 - 20 -
6.3 HELLO的FORK进程创建过程 - 20 -
6.4 HELLO的EXECVE过程 - 20 -
6.5 HELLO的进程执行 - 21 -
6.6 HELLO的异常与信号处理 - 21 -
6.7本章小结 - 22 -
第7章 HELLO的存储管理 - 23 -
7.1 HELLO的存储器地址空间 - 23 -
7.2 INTEL逻辑地址到线性地址的变换-段式管理 - 23 -
7.3 HELLO的线性地址到物理地址的变换-页式管理 - 23 -
7.4 TLB与四级页表支持下的VA到PA的变换 - 23 -
7.5 三级CACHE支持下的物理内存访问 - 24 -
7.6 HELLO进程FORK时的内存映射 - 25 -
7.7 HELLO进程EXECVE时的内存映射 - 25 -
7.8 缺页故障与缺页中断处理 - 25 -
7.9动态存储分配管理 - 26 -
7.10本章小结 - 26 -
第8章 HELLO的IO管理 - 27 -
8.1 LINUX的IO设备管理方法 - 27 -
8.2 简述UNIX IO接口及其函数 - 27 -
8.3 PRINTF的实现分析 - 27 -
8.4 GETCHAR的实现分析 - 28 -
8.5本章小结 - 28 -
结论 - 28 -
附件 - 30 -
参考文献 - 30 -
第1章 概述
1.1 Hello简介
Hello这个程序首先程序员通过文本编辑器或者是ide编译器编写出来得到Hello.c(Hello.cpp),然后通过预处理器(cpp)将其预处理为Hello.i;然后通过编译器(ccl),将其中的符号,变量处理后变成Hello.s;然后通过链接器与一些动态库,静态库相链接生成Hello可执行文件;然后shell认定他不是内核程序,为其fork产生进程,然后Hello就成为了一个进程,完成了P2P: From Program to Process;
然后shell为其execve,mmap将其的代码段,数据段等加入到虚拟内存中,然后进行内存访问,在经历了缺页之后成功访问了代码段、数据段的内存,然后存储分级系统将其一步一步往上传输,在经历了缓存不命中之后成功读取了Hello,然后在Hello运行完之后,shell令其父进程回收Hello,将他的数据以及一切痕迹全部清除干净实现了O2O: From Zero-0 to Zero-0;
1.2 环境与工具
1.2.1 硬件环境
X64CPU i7-8750H;2.20GHz;16G RAM;128G ROM
1.2.2 软件环境
Windows10 64位;Vmware 14.1.1;Ubuntu 18.04.1 64位
1.2.3 开发工具
Visual Studio 2017 64位;CodeBlocks 64位;devc++ 32位;vi/vim/gedit+gcc
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
1.4 本章小结
第2章 预处理
2.1 预处理的概念与作用
概念:在程序编译之前通过C Preprocessor(cpp)预处理器进行处理。
作用:
1.宏定义:又称为宏替换,即将#define里的东西转换为文本,带有参数的宏还需要进行参数代换。
2.文件包含:将头文件中含有的文件复制到源代码中。
3.条件编译:对于满足if条件的代码进行筛选,只有满足的代码才进行编译
2.2在Ubuntu下预处理的命令
gcc -m64 -no-pie -fno-PIC -E hello.c -o hello.i
图2-2
2.3 Hello的预处理结果解析
图2-3
Hello.i文件相比于Hello.c文件内容有20多行变为了3000多行,其中上述所说的#define已经消失变成了字符串与参数,而头文件中包含的库也已经复制到了文件中。
2.4 本章小结
总结了预处理的工作概念作用,并以Hello.c这个文件进行了实际模拟,真正观察到了预处理文件与源文件的差别。
第3章 编译
3.1 编译的概念与作用
编译是指将i文件变成.s 文件,即预处理后的文件到生成汇编语言程序
3.2 在Ubuntu下编译的命令
gcc -m64 -no-pie -fno-PIC -S hello.c -o hello.s
图3-2
3.3 Hello的编译结果解析
3.3.1 常量
程序在编译后,如printf括号中包含的语句这些被编译器处理为常量,存放在.rodata段中,如下图所示
图3.3.1-1
LC0段对应的是程序中第一个printf中的内容:
图3.3.1-2
其中的汉字“用法 学号 姓名”等都进行了相应的编码。
LC1段则对应着第二个printf所对应的内容:
图3.3.1-3
3.3.2 变量
变量分为全局变量与静态变量,其中全局变量放在数据段中,而局部变量则放在堆栈中,特别的是静态的局部变量也放在数据段中。
1.全局变量:
全局变量又分为已初始化的全局变量和未初始化的全局变量。已初始化的全局变量储存在.data节,未初始化的全局变量储存在.bss节中,特别的是未赋值的全局变量放在COMMON中。
在Hello.c的全局变量如下图所示
他被赋了初值,储存在.data节中,是强符号,类型是int整形,存储的值实际为2,长度为4字节。
图3.3.2-1
·局部变量
局部变量在程序运行时堆栈为其分配了空间,一旦程序停止运行,局部变量也就被舍弃了。在hello.c中的循环变量i便是局部变量:
其在汇编中存在于堆栈中:
图3.3.2-2
如上图所示 addl与cmpl两句便实现了循环,而i变量是以%rbp – 4实现的,并没有特定的符号。
3.3.3 类型
对于不同的变量类型,编译器采用了不同的大小长度进行定义,以x86-64为例,其中他的变量可以总结为下表
C声明 Intel数据类型 汇编代码后缀 大小(字节)
char 字节 b 1
short 字 w 2
int 双字 l 4
long 四字 q 8
图3-3-9-1
在Hello.c中出现了5个函数,分别为main, printf, sleep, exit, getchar:
第4章 汇编
4.1 汇编的概念与作用
汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。在这次中即是指Hello.s到Hello.o的过程,如果直接打开Hello.o的话会出现乱码,无法查看
4.2 在Ubuntu下汇编的命令
gcc -m64 -no-pie -fno-PIC -c hello.s -o hello.o
图4-2
4.3 可重定位目标elf格式
分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。readelf -a hello.o > hello.txt
ELF头描述的信息如下图所示:
图4-3-1
节头信息如下图所示:
图4-3-2
重定位信息如下图所示:
图4-3-3
符号表信息如下图所示:
图4-3-4
4.4 Hello.o的结果解析
objdump -d -r hello.o
通过两个的对比,在main函数中,汇编代码和机器代码反汇编之后的代码在语句方面基本一致。反汇编的代码在每一句汇编代码语句之前有着一段与之对应的机器代码。同时,汇编代码在控制转移方面和反汇编代码有着如下的映射关系:比如在L2中存在着 29
图4-4
4.5 本章小结
汇编的过程相当于重定位的过程,是将汇编语言与机器语言一一映射的过程,使得程序可以被机器能够识别并且可以执行。
第5章 链接
5.1 链接的概念与作用
概念:链接是指在电子计算机程序的各模块之间传递参数和控制命令,并把它们组成一个可执行的整体的过程。这里是指将各种代码合并在一起,形成一个可执行文件,该文件可以被执行。
5.2 在Ubuntu下链接的命令
图5-2
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头如下图所示:
图5-3-1
各个节:
图5-3-1
5.4 hello的虚拟地址空间
使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。
图5-4-1
使用EDB对hello的可执行文件进行调试,可以看到hello的可执行文件通过段头部表把连续的文件节映射到运行时的内存段。
左边的是段头部表的结构,总共有八个变量,这和读取到的elf文件中的程序头所表示的八个数据是相同的。这些分别记录了各段在虚拟地址空间和物理地址空间的大小、位置、标志、访问权限和对齐方面的信息。
图5-4-2
_init 初始化
gmon_start 程序通过gprof可以输出函数调用等信息
_dl_relcoate_static_pie 静态库链接
.plt 动态链接
puts@plt(等) 动态链接各个函数
_start 编译器为可执行文件加上了一个启动例程
__libc_csu_init 程序调用libc库来对程序初始化函数
_fini 当程序正常终止时需要执行的代码
5.5 链接的重定位过程分析
Hello.o程序因为没有进行链接,所以没有进行重定位,main函数的地址是从0开始。重定位方式:有两种R_X86_PC32:PC相对地址寻址,R_X86_64_32:绝对地址寻址。
5.6 hello的执行流程
加载程序的过程:
ld-2.23.so _dl_start ld-2.23.so _dl_init LinkAddress _start libc-2.23.so _libc_start_main libc-2.23.so _cxa_atexit LinkAddress _libc_csu.init libc-2.23.so _setjmp
以上是执行main函数的过程:
LinkAddress main
以上是结束程序的过程:
libc-2.23.so exit
5.7 Hello的动态链接分析
当编译器编译到动态链接库中PIC函数时,函数的运行的地址不得而知,所以需要添加重定位记录,等待动态链接器处理。由于共享库中的代码和数据,在存储空间的相对位置一直是固定的。动态链接器使用过程链接表PLT+全局偏移量表GOT实现函数的动态链接,GOT中存放函数目标地址,PLT使用GOT中地址跳转到目标函数。PLT是一个数组,其中每个条目是16字节代码。每个库函数都有自己的PLT条目,PLT[0]是一个特殊的条目,跳转到动态链接器中。从PLT[2]开始的条目调用用户代码调用的函数。GOT同样是一个数组,每个条目是8字节的地址,和PLT联合使用时,GOT[2]是动态链接在ld-linux.so模块的入口点,其余条目对应于被调用的函数,在运行时被解析。每个条目都有匹配的PLT条目。
延迟绑定的步骤如下:
1.建立一个 GOT和PLT 表,该表用来放全局函数的实际地址。
2.对每一个全局函数,链接器生成一个与之相对应的函数。
3.所有对全局函数的调用,都换成对对应函数调用。
5.8 本章小结
ELF具有.text,.data等节,在经过链接之后,可重定位文件便会变成可执行文件,链接器会将程序与静态链接库相链接,以及与一些动态库相链接,并且将地址重定位,从而保证寻址被确保是正确的。
第6章 hello进程管理
6.1 进程的概念与作用
概念:进程是程序关于数据集合的一次调用。让我们觉得程序总是独占的使用处理器。而实际上程序在不停的进行着上下文切换。
作用:进程通过切换上下文实现多组程序能够独占cpu的假想,使得任务在用户模式与内核模式之间切换的更为迅速,同时也使得计算机效率更高。
6.2 简述壳Shell-bash的作用与处理流程
Linux系统中,Shell是一个交互型应用级程序,代表用户运行其他程序(是命令行解释器,以用户态方式运行的终端进程)。
其基本功能是解释并运行用户的指令,重复如下处理过程:
1.终端进程读取用户由键盘输入的命令行。
2.分析命令行字符串,获取命令行参数,并构造传递给execve的argv向量
3.检查第一个(首个、第0个)命令行参数是否是一个内置的shell命令
4.如果不是内部命令,调用fork( )创建新进程/子进程
5.在子进程中,用步骤2获取的参数,调用execve( )执行指定程序。
6.如果用户没要求后台运行(命令末尾没有&号)否则shell使用waitpid(或wait…)等待作业终止后返回。
7.如果用户要求后台运行(如果命令末尾有&号),则shell返回;
6.3 Hello的fork进程创建过程
在shell的命令行,用户完成输入命令。shell在解析输入命令之后创建argv、envp等参数列表,以及参数个数argc。shell作为父进程创建新子进程。子进程与父进程拥有完全相同的虚拟内存地址以及副本,但虚拟地址确是独立的,而且PID也不相同。
6.4 Hello的execve过程
在创建了子进程之后,通过判断PID == 0来判断是否是子进程。然后在子进程中调用execve函数在当前上下文中加载并运行Hello程序。该函数通过调用启动加载器来执行程序,然后会有新的代码段数据段被初始化为Hello准备,然后设置PC指向_start函数,然后设置堆栈,最后将控制传递给main函数正式开始运行程序。
6.5 Hello的进程执行
上下文:上下文通常包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈、和各种内核数据结构等。
进程时间片:一个进程执行它的控制流的一部分的每一个时间段叫时间片。
Hello进程调度的过程:
Hello在运行到sleep之前无异常,调用sleep时发生被抢占的情况,会进行上下文切换,进入内核状态,内核处理休眠请求并主动释放当前进程,并将 Hello 进程加入到等待队列,定时器计时开始,上下文切换后控制转移到 shell 进程, 定时器到时后发送一个中断信号,由信号处理函数完成处理,将 Hello 进程从等待 队列中移出重新加入到运行队列,从而进行上下文切换进入到 Hello 进程。
6.6 hello的异常与信号处理
下图是正常执行Hello程序的截图,再输出8个输入后再输入一个回车Hello停止了运行。
图6-6-1
下图是在Hello运行时乱按的截图,可以发现这并不影响Hello的运行。
图6-6-2
下图是在运行Hello按ctrl c,可以发现在ctrl c 之后shell收到信号并进行了回收子程序,所以Hello正常退出。
图6-6-3
下图时在Hello运行时按ctrl z时的情况,发现Hello被暂时挂起,通过ps指令找到后台运行的Hello程序,通过观察发现Hello的编号为1,所以通过fg 1指令从新运行Hello程序。
图6-6-4
6.7本章小结
本章主要研究了Hello在运行的生活,先是fork一个子进程,再进行execve从而真正的运行Hello,而且内核通过切换上下文的形式巧妙的让我们以为cpu正在为Hello独自运行。本章还了解了shell的工作原理以及一些指令,这让以后对于程序的运行有了更为深刻的认识。
第7章 hello的存储管理
7.1 hello的存储器地址空间
物理地址:在存储器里以字节为单位存储信息,每一个字节单元给以一个唯一的存储器地址,称为物理地址。
虚拟地址:CPU通过虚拟地址来访问主存,这个虚拟地址在被送到主存前先转换成适当的物理地址,并与物理地址一一对应。
逻辑地址:是指由程序产生的与段相关的偏移地址部分。
线性地址:是逻辑地址到物理地址变换之间的中间层。程序代码会产生逻辑地址,或者说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
段式管理,是指把一个程序分成若干个段进行存储,每个段都是一个逻辑实体。
原理:
为了实现段式管理,操作系统需要如下的数据结构来实现进程的地址空间到物理内存空间的映射,并跟踪物理内存的使用情况,以便在装入新的段的时候,合理地分配内存空间。
·进程段表:描述组成进程地址空间的各段,可以是指向系统段表中表项的索引。每段有段基址。
·系统段表:系统所有占用段。
·空闲段表:内存中所有空闲段,可以结合到系统段表中。
7.3 Hello的线性地址到物理地址的变换-页式管理
将各个进程的虚拟空间划分成若干个长度相等的页,页式管理把内存空间按页的大小划分成片或者页面,然后把页式虚拟地址与内存地址建立一一对应页表,并用相应的硬件地址变换机构,来解决离散地址变换问题。页式管理采用请求调页或预调页技术实现了内外存储存器的统一管理。
同任何缓存一样,虚拟内存系统必须有某种方法来判定一个虚拟页是否缓存在DRAM中的某个地方。这些功能由操作系统软件、MMU中的地址翻译硬件和一个存放在物理内存中叫做页表的数据结构,页表将虚拟页映射到物理页。每次地址翻译硬件将一个虚拟地址转换为物理地址时,都会读取页表。
7.4 TLB与四级页表支持下的VA到PA的变换
TLB:每次CPU产生一个虚拟地址,MMU就必须查阅相应的PTE,每次都必然造成缓存不命中等一系列时间开销,为了消除这样的开销,MMU中存在一个全相联高速缓存,称为TLB。
四级页表:如果只需要使用的虚拟内存很小,但仍然需要一个十分巨大的页表,造成了内存的浪费,或者在某些情况页表会变得十分巨大。所以虚拟地址到物理地址的转换过程中还存在多级页表的机制:上一级的页表映射到下一级页表,直到页表映射到虚拟内存。下图是书上的四级页表示意图:
图7.4-1
7.5 三级Cache支持下的物理内存访问
所有的存储器都有一个相同的特点,那便是存储器越快存储器造价越贵,而且其容量越小,但大型存储区其速度十分的慢,无法满足当前cpu的速度需要,因此人们发明了cache缓冲器。其作用便是将下一级的内存先缓存到自己中,以便cpu的快速调用。cache分为三种:直相联高速缓冲,组相连高速缓冲,全相联高速缓存。
图7.5-1
2. 组相联高速缓存
基本与直接映射高速缓存相同,只是每一个组中有两行(E=2),在行匹配的时候可以并行地检测标记是否匹配。
图7.5-2
3. 全相联高速缓存
只有一个组(S=1),组中有很多行(E>=2),组选择总是默认选择第0组,行匹配与组相联缓存基本相同,由于匹配标记是并行的,所以造价十分昂贵,典型的TLB。
7.5-3
7.6 hello进程fork时的内存映射
由上一节可知,在父进程创建子进程后,内核为其分配一个唯一的PID,然后子进程得到了与父进程完全一致的虚拟内存中的副本。
7.7 hello进程execve时的内存映射
execve将Hello的.text、.data、.bss段都加载到虚拟地址空间中。
7.8 缺页故障与缺页中断处理
通过虚拟地址空间寻找它的PTE数组是否对应了物理地址,如果时未分配状态则发生了缺页,缺页后通常会首先确定一个牺牲页,然后进行页替换,倘若牺牲页已经被修改过,它便会被放回到磁盘中。
7.9动态存储分配管理
分配器将堆视为一组不同大小的块的集合来维护,每个块就是一个连续的虚拟内存片,要么是已分配的要么是空闲的。已分配的块显式地保留为供应用程序使用,空闲块可以用来分配。空闲块保持空闲直到它显式地被应用分配。一个已分配的块保持已分配状态,直到它被释放。
7.10本章小结
虚拟内存使得程序的调用更加私密,也使得物理空间得到了最大利用。也使得每个进程都有一个自己的虚拟空间,让进程运行的更加效率。
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
在Linux中,所有的I/O设备(例如网络、磁盘和终端)都被模型化为文件 , 而所有的输入和输出都被当作对相应文件的读和写来执行。这种将设备优雅地映射为文件的方式,允许Linux内核引出一个简单、低级的应用接口,称为Unix I/O,这使得所有的输入和输出都能以一种统一且一致的方式来执行。
设备的模型化:文件
设备管理:unix io接口
8.2 简述Unix IO接口及其函数
Linux/unix I/O:将设备映射为文件的方式,允许Unix内核引出一个简单、低级的应用接口。
1.打开文件,内核会记录文件的信息
2.shell会由三个打开的文件:标准输入,标准输出,标准错误。
3.改变文件位置
4.读写文件
5.关闭文件
Unix I/O接口函数:
打开文件:int open(char *filename, int flags, mode_t mode);
关闭文件:int close(int fd);
读文件: ssize_t read(int fd, void *buf, size_t n);
写文件: ssize_t write(int fd, const void *buf, size_t n);
8.3 printf的实现分析
https://www.cnblogs.com/pianist/p/3315801.html
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
int printf(const char fmt, …)
{
int i;
char buf[256];
va_list arg = (va_list)((char)(&fmt) + 4);
i = vsprintf(buf, fmt, arg);
write(buf, i);
return i;
}
8.4 getchar的实现分析
int getchar(void)
{
char c;
return (read(0,&c,1)==1)?(unsigned char)c:EOF
}
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
本章主要介绍了Linux的IO管理方法,UnixIO以及一些接口函数。分析讨论了我们最经常使用的printf和getchar函数。而且程序能够输入输出完全依赖于Linux最底层的输入输出接口,通过这个接口用户才可以通过命令的方式调用一些函数。
结论
Hello的一生看似平淡无奇实则奥妙无穷,我们可以用以下总结来描述Hello的一生:
1.出生:一切的一切都源于程序员们在文本编辑器写下的那几行看似简单却融合了智慧结晶的代码。
2.预处理:预处理器将宏定义转换为字符串,并将库复制粘贴到源代码中为编译做准备。
3.编译:编译器将预处理后的源代码转换成汇编语言。
4.汇编:汇编器将汇编语言变成机器语言。
5.链接:链接器将上述生成的可重定位文件链接后生成可执行文件,P2P也就基本完成了。
6.运行:在shell中运行Hello,先fork后execve,经历了访问内存以及可能出现的异常处理后Hello真正的运行了。
7.回收:Hello在运行结束后shell回收子程序,Hello又回到了虚无,丝毫不留下痕迹。
附件
hello.c 源文件
hello.i 预处理后的文件
hello.s 编译后的文件
hello.o 可重定位文件
hello_BELF.txt 可重定位文件的ELF
hello_ASM.txt 可重定位文件的反汇编
hello 连接后的可执行文件
hello_AELF.txt 可执行文件的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.
(参考文献0分,缺失 -1分)