计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 计算机科学与技术
学 号 1170300216
班 级 1736101
学 生 张恺欣
指 导 教 师 刘宏伟
计算机科学与技术学院
2018年12月
摘 要
“麻雀虽小,五脏俱全”,hello是每个程序员的第一个程序,本文将追踪hello的整个生命历程,带你一起深入理解计算机系统。
关键词:程序生命周期;编译;进程;P2P;O2O
(摘要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 -
结论 - 14 -
附件 - 15 -
参考文献 - 16 -
第1章 概述
1.1 Hello简介
根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。
P2P:程序到进程,程序从程序员编写的hello.c文件,经过C预处理器的预处理(CPP)变成hello.i(中间文件ASCII),再经过C编译器的编译(CCL)变成hello.s(汇编语言ASCII),再经过汇编器的汇编(AS),变成hello.o(可重定位目标文件),最后经过链接器的链接(LD)变成可执行文件(二进制)hello,然后shell会创建一个进程执行这个程序。
O2O:shell开始执行之后,映射出虚拟内存,然后运行进程时分配并载入物理内存,获取第一条指令的地址,更新PC,开始执行hello程序,将其output的数据显示到屏幕,然后进程结束,由shell回收内存空间。
1.2 环境与工具
列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开发与调试工具。
硬件环境:
CPU:Intel i5-4690k(64位)3.50GHz
内存:12G DDR3 RAM(双通道)
外存:2TB HDD+128G SSD
显卡:GTX1060(6G)
软件环境:
Windows10 64位专业版,VM VirtualBox 5.2.14,Ubuntu 18.04 LTS 64位
使用工具VSCode,objdump,gdb,edp,winhex
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
hello.i(hello.c预处理之后的程序文本)
hello.s(hello.i编译成汇编语言之后的程序文本)
hello.o(hello.s生成的二进制文件)
hello_elf.txt(读取出的hello.o的elf头)
hello_o.s(hello.o反汇编的结果)
hello(可执行的hello二进制文件)
hello_.s(可执行文件hello,直接用objdump反编译之后的汇编代码)
hello.elf(可执行文件hello的elf表)
1.4 本章小结
总结了实验的软硬件环境、需要用到的软件工具和实验的中间结果。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
程序设计领域中,预处理一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。典型地,由预处理器(preprocessor) 对程序源代码文本进行处理,得到的结果再由编译器核心进一步编译。这个过程并不对程序的源代码进行解析,但它把源代码分割或处理成为特定的单位——(用C/C++的术语来说是)预处理记号(preprocessing token)用来支持语言特性(如C/C++的宏调用)。
2.2在Ubuntu下预处理的命令
gcc -E -o xxx.i xxx.c
为了对预处理有一个直观的概念,我们查看一下hello.i的内容
2.3 Hello的预处理结果解析
可以看到hello.i中包含了程序名等信息,还有用到的运行库在计算机中的绝对路径
还声明了可能会用到的库函数
这样做使得编译器的编译更加容易
2.4 本章小结
简单介绍了c语言的预处理过程,明确了什么是预处理,预处理的作用两个问题,实际进行了预处理操作,并简单分析了与处理结果hello.i
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
编译,即利用编译程序从源语言编写的源程序产生目标程序的过程,也就是把高级语言变成汇编语言,把hello.i文件变成hello.s文件。
编译的作用就是帮助计算机理解高级语言。高级语言对于计算机来说过于复杂,计算机只能理解2进制指令和数据,而程序员作为人类更擅长编写类似自然语言的高级编程语言,编译就是二者之间的桥梁(至少是桥梁的一段),它使得高级语言变成与计算机二进制编码极其相似的汇编语言,大大方便了后续转换为二进制编码的汇编和链接操作。
3.2 在Ubuntu下编译的命令
把hello.i文件变成hello.s文件,需要用到的语句为
gcc -S -o hello.s hello.i
打开hello.s,我们发现里面存放的就是我们想要的汇编代码
3.3 Hello的编译结果解析
3.3.1数据
(1)首先注意到程序声明的全局变量
在汇编语言中声明为下图
具体的含义就是程序中有一个叫sleepsecs的object类型的变量,被保存 在.data节中,占用了4个字节的空间
这里由于sleepsecs为int型整数,故2.5被隐式地截断为2
这里的sleepsecs存放在.rodata节中,是一个偏移量,表示sleepsecs在寄 存器中的具体位置
(2)程序中还有一个局部变量i,作为循环变量
但是他不在hello.i开头的声明中,而是在下图中被初始化
然后调用
这里的i就是存在-4(%rbp)中
小结:初始化过的全局变量会被存放在.data节中,在.rodata节中会有一 个他的偏移量
而初始化过的局部变量,只有在初始化时程序才会给他申请一个 内存空间。
变量的类型转换为隐式转换。
3.3.2关系操作符和控制语句
程序中的关系操作符如下
在汇编语言中对应的代码如下
-20(%rbp)中保存的就是argc,与立即数$3相等时不进入分支,而是向下执行, 也就是进入for循环,即汇编中的.L2和.L3。
具体操作如下,首先cmpl是一个比较语句,与下面的je .L2可以看成一组,
cmpl把$3和-20(%rbp)作差,不保存结果,只设置条件码,然后je读取条件 码,遵循如下操作
这样就实现了条件跳转。
3.3.3四则运算
加法:x=x+y
addq y,x
减法:x=x-y
subq y,x
乘法:x=x*y
imulq y,x
除法:z=x/y
movq x,z
cqto
idivq y
如循环控制中的i++
转换为汇编就是如下代码
3.3.4数组/指针/结构操作
取数组第i位就是取数组头指针再加上第i位的偏移量,如printf中对数组元 素的访问
转换为汇编语言为
-32(%rbp)中保存的就是数组的头指针,第一句将头指针保存在rax中,之后
rax=rax+16,即为argv[2],保存为rdx,然后头指针+8即为argv[1]保存为rsi
3.3.5对函数的操作
(1)函数的返回值,一般保存在rax(eax)寄存器中,调用ret指令返回,如hello中的
(2)函数的调用和参数传递
在调用函数前首先要进行参数传递,就是选择几个寄存器将参数存入, 然后执行call指令,这是程序就会跳转到目标函数的头部开始执行(实 际是把目标函数的头部放入了PC)
3.4 本章小结
本章介绍了编译的概念和作用,以及程序的机器级表示,具体包括变量、关 系操作符和程序控制,四则运算,函数的返回,函数的参数构造和调用
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
汇编的概念:把汇编语言翻译成机器语言的过程称为汇编。汇编会将编译好的汇 编语言翻译为可重定向文件hello.o,这时hello.c的代码全部被转换 为二进制代码,但是仍缺少必要的运行库,还需要经过链接之后才 可以变成可执行程序。
汇编的作用:把类似于助记符的汇编语言翻译成计算机能够识别的二进制语言, 即机器指令,等待链接后就可以变为可执行程序了。
4.2 在Ubuntu下汇编的命令
as hello.s -o hello.o
4.3 可重定位目标elf格式
ELF头,描述了生成该文件的系统信息(如大小端,补码)和ELF头的大小、目标文件类型、机器类型等总体的信息。
节头部表,详细地描述了节头部表的文件偏移和节头部表中条目的大小和数量等。
重定位节,.rela.text包含了.text节中需要进行重定位的指令的信息,在进行链接的是时候可用于重定位,可以看到里面包含了printf,getchar,sleep等函数。
.rela.eh_frame包含了en_frame节的重定位信息
符号表,存放了在程序中定义和引用的函数和全局变量的信息。
4.4 Hello.o的结果解析
反汇编hello.o的结果如下
可以看到在汇编指令方面,反汇编结果与hello.s相比无非就是多了一些注释,指令和操作数没什么太大变化,但是每条指令前面多了一串十六进制数字,冒号前面的是运行时机器指令的位置,冒号后的则是机器指令的二进制转十六进制表示。
特别是在分支跳转方面,原来用.L3这样的符号表示的分支现在全部改用具体的地址表示,call的函数名变成了函数的地址。
4.5 本章小结
本章介绍了汇编的概念和作用,探寻了汇编后文件的变化,离最后的执行只剩最后的链接这一步了。另外还分析了elf文件的内容,为链接打下基础。
(第4章1分)
第5章 链接
5.1 链接的概念与作用
链接的概念:链接是指在电子计算机程序的各模块之间传递参数和控制命令,并把它们组成一个可执行的整体的过程。
链接的作用:链接的存在降低了模块化编程的难度。
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中
观察ELF头
内容与hello.o的ELF头差不多,但是可以看到节头数量增长到25个
下面看节头,里面包含了节头部表对应的节的大小、虚拟地址、地址偏移量等信息
下面是程序头
程序头表的说明:
PHDR:程序头表
INTERP:程序执行前需要调用的解释器
LOAD:程序目标代码和常量信息
DYNAMIC:动态链接器所使用的信息
NOTE::辅助信息
GNU_EH_FRAME:保存异常信息
GNU_STACK:使用系统栈所需要的权限信息
GNU_RELRO:保存在重定位之后只读信息的位置
5.4 hello的虚拟地址空间
使用edb加载hello,在Data Dump中可看到虚拟地址情况
程序从0x00400000开始加载,在0x00400ff0结束
这里对应着5.3中的地址
这里对应着5.3中的名称
5.5 链接的重定位过程分析
可以看出有如下不同点:
1、hello.o反汇编结果中的偏移地址变成了虚拟内存地址
2、hello中多了很多的“节”,如下图所示
3、hello中多了许多链接后生成的函数,如下图
4、hello中的跳转和函数调用的地址被更换成了虚拟内存地址
重定位的过程:
第一步:重定位节和符号定义。链接器将所有相同类型的节合并为同一类型的新的聚合节。例如,hello.o反汇编结果(hello_o.s)中只包含main函数和其所在的.text节,而hello反汇编结果(hello.s)中则包含.init .plt .fini等许多节和对应的库函数。如下图所示
然后,连接器将运行时内存地址赋给新的聚合节,赋给输入模块定义的每个节,赋给输入模块定义的每个符号。可以看出hello_o.s中的地址都是链接地址,而hello.s中的地址都是虚拟内存地址。
下一步是重定位节中的符号引用,使之指向正确的地址。
5.6 hello的执行流程
载入:执行dl_start和dl_init
开始执行:
_start
_libc_start_main
_init
执行main:
_main
_printf
_exit
_sleep
_getchar
_dl_runtime_resolve_xsave
_dl_fixup
_dl_lookup_symbol_x
退出:
exit
下面为edb调试过程中的截图
5.7 Hello的动态链接分析
dl_init执行之前
执行之后
可以看出_GLOBAL_OFFSET_TABLE_发生改变,init给程序附上了当前执行的内存地址偏移量。
5.8 本章小结
介绍了链接的概念和作用,分析了hello的ELF头的变化,实际体验了虚拟地 址空间的分配,重定位过程和动态链接过程。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
进程的概念:
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
进程的作用:
在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
6.2 简述壳Shell-bash的作用与处理流程
在计算机科学中,Shell俗称壳(用来区别于核),是指“为使用者提供操作界面”的软件(命令解析器)。它类似于DOS下的command.com和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。
处理流程:
1、读取用户输入
2、解析输入内容,获取参数
3、检查是否是内核命令,是的话直接执行,不是的话调用相应程序执行
4、在程序运行期间监视键盘的输入内容并做出相应的操作
6.3 Hello的fork进程创建过程
父进程和子进程共享代码段,但拥有相同但独立的数据段和堆栈。
6.4 Hello的execve过程
execve 函数加载并运行可执行目标文件filename, 且带参数列表argv 和环境变量列表envp 。只有当出现错误时,例如找不到filename, execve 才会返回到调用程序。所以,与fork 一次调用返回两次不同, execve 调用一次并从不返回。
加载了filename之后,他调用启动代码,启动代码设置栈,并将控制传递给新程序的主函数
下面是对execve函数的参数的说明
filename:包含准备载入当前进程空间的新程序的路径名。既可以是绝对路 径,又可以是相对路径。
argv[]:指定了传给新进程的命令行参数,该数组对应于c语言main函数的 argv参数数组,格式也相同,argv[0]对应命令名,通常情况下该值 与filename中的basename(就是绝对路径的最后一个)相同。
envp[]:最后一个参数envp指定了新程序的环境列表。参数envp对应于新程 序的environ数组。
返回值:由于是将调用进程取而代之,因此对execve的调用将永远不能返回, 也无需检查它的返回值,因为该值始终为-1,实际上,一旦返回就表 明了错误,通常会有error值来判断,下面是几个常用的:
6.5 Hello的进程执行
在图中箭头处调用sleep,这个内核和前端之间的切换动作被称作上下文切换,切换后进程B会在用户模式下运行一会,这是内核进程A会处于等待状态,等待B发出中断信号后再继续执行。
6.6 hello的异常与信号处理
ctrl+z:
这个操作向进程发送了一个sigtstp信号,让进程暂时挂起,输入ps命令符可以发现hello进程还没有被关闭。
ctrl+c:
向进程发送了一个sigint信号,使进程直接结束,ps查看进程可以看到进程已经结束
fg命令:
可以使后台挂起的进程继续执行
jobs命令:
查看后台挂起的进程
pstree命令:
显示进程树
kill指令:
kill -s 信号 PID
结束指定PID的进程,参数表示发送的信号
6.7本章小结
介绍了进程的概念和作用,描述了shell的功能以及基本操作,介绍了shell的内核信号和命令。总结了fork和execve的执行过程和进程在内核模式和用户模式下是如何切换的。
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址:又称相对地址,是程序运行由CPU产生的与段相关的偏移地址部分。他是描述一个程序运行段的地址。
虚拟地址:(即线性地址)
这是对整个内存的抽像描述。它是相对于物理内存来讲的,可以直接理解成“不直实的”,“假的”内存。进程使用虚拟内存中的地址,由操作系统协助相关硬件,把它“转换”成真正的物理地址。有了这样的抽像,一个程序,就可以使用比真实物理地址大得多的地址空间。就是hello里面的虚拟内存地址。
物理地址:
用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。就是hello在运行时虚拟内存地址对应的物理地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
先将逻辑地址分成段选择符+段描述符的判别符(TI)+地址偏移量的形式,然后先判断TI字段,看看这个段描述符究竟是局部段描述符(ldt)还是全局段描述符(gdt),然后再将其组合成段描述符+地址偏移量的形式,这样就转换成线性地址了
7.3 Hello的线性地址到物理地址的变换-页式管理
后备缓冲器(TLB)是一个缓存,VPN(虚拟页号)被分成TLBT(TLB标记)和TLBI(TLB索引),然后去TLB中查找,该页面存在的话则返回一个PTE(虚拟页表),里面存着PPN,转换为物理地址后去高速缓存或内存中查找。若缺页,则从高速缓存或内存中调用,再把这个PTE传回翻译。拿到PPN后与VPO组合便得到了物理地址。
7.4 TLB与四级页表支持下的VA到PA的变换
7.5 三级Cache支持下的物理内存访问
得到物理地址之后,先将物理地址拆分成CT(标记)+CI(索引)+CO(偏移量),然后在一级cache内部找,如果未能寻找到标记位为有效的字节(miss)的话就去二级和三级cache中寻找对应的字节,找到之后返回结果。
7.6 hello进程fork时的内存映射
fork被当前进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的PID。然后给这个新进程创建虚拟内存,即创建当前进程的mm_struct、区域结构和页表的原样副本,将两个进程的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。
当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当两个进程中任意一个进行写操作时,写时复制机制就会创建新页面,也就为每个进程保持了私有地址空间的抽象概念。
7.7 hello进程execve时的内存映射
1、删除已存在的用户区域
2、创建新的私有区域(.malloc,.data,.bss,.text)
3、创建新的共享区域(libc.so.data,libc.so.text)
4、设置PC,指向代码的入口点
7.8 缺页故障与缺页中断处理
情况1:段错误:首先,先判断这个缺页的虚拟地址是否合法,那么遍历所有的合法区域结构,如果这个虚拟地址对所有的区域结构都无法匹配,那么就返回一个段错误(segment fault)
情况2:非法访问:接着查看这个地址的权限,判断一下进程是否有读写改这个地址的权限。
情况3:如果不是上面两种情况那就是正常缺页,那就选择一个页面牺牲然后换入新的页面并更新到页表。
7.9动态存储分配管理
分配器分为两种
显式分配器:要求应用显式地释放任何已分配的块。
隐式分配器:要求分配器检测一个已分配块何时不再使用,那么就释放这个块, 自动释放未使用的已经分配的块的过程叫做垃圾收集。
空闲链表有以下四种组织方式:
带边界标记的空闲块的合并有下面四种情况:
7.10本章小结
本章介绍了储存器的地址空间,讲述了虚拟地址、物理地址、线性地址、逻辑地址的概念,还有进程fork和execve时的内存映射的内容。特别介绍了malloc的实现方法,堆中空闲块的组织方式、合并策略。
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:文件
1、文件的类型:
2、普通文件(regular file):包含任意数据的文件。
3、目录(directory):包含一组链接的文件,每个链接都将一个文件名映射到 一个文件(他还有另一个名字叫做“文件夹”)。
4、套接字(socket):用来与另一个进程进行跨网络通信的文件
5、命名通道
6、符号链接
7、字符和块设备
设备管理:unix io接口
1、打开和关闭文件
2、读取和写入文件
3、改变当前文件的位置
8.2 简述Unix IO接口及其函数
打开和关闭文件:
1.open()函数:这个函数会打开一个已经存在的文件或者创建一个新的文件, 如果open的返回值为-1则说明其打开该文件失败
2.close()函数:这个函数会关闭一个打开的文件
读取和写入文件:
1.read()函数:这个函数会从当前文件位置复制字节到内存位置
2.write()函数:这个函数从内存复制字节到当前文件位置
读写文件时,如果返回值<0则说明出现错误
改变文件位置:
1.lseek()函数
8.3 printf的实现分析
printf函数就是接收一个fmt格式,匹配参数,然后将匹配到的参数按照fmt格式输出。下面看一下它的源码。
源码中调用了vsprintf和write两个函数,前者的功能就是将所有的参数内容格式化之后存入buf,然后返回格式化数组的长度,而后者则是将buf中的i个元素输出到终端的函数。
以下是前者的源码
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
getchar调用了read函数,它将整个缓冲区都读到buf中,然后返回缓冲区的长度。buf长度为0时才会调用read函数,否则直接将buf中最前面的元素返回。
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
本章介绍了linux的I/O设备管理机制,介绍了文件操作的接口和相关函数,分析了printf和getchar的实现方法。
(第8章1分)
结论
诞生:程序员按下保存键的瞬间,hello.c诞生
预处理:在预处理器老师的帮助下,hello.c同学小学毕业——变成了hello.i
编译:升入初中后,hello.i经过多次学(处)习(理),最终通过了毕业考试(编 译),变成了hello.s,里理想中的可执行程序又进了一步。
汇编:在更为严(复)苛(杂)的高中(汇编器)中锻炼了几年(毫秒)后,hello.s 摇身一变变成hello.o——可重定位文件,只等链接后就可以运行了。
链接:经过在高中(汇编器)中的艰苦学习(复杂处理),hello.o顺利考上工大(链接器),在这里,他学到了将来工作(运行)所需要用到的所有只是(运行库),终于顺利实现了理想,变成了一个不出bug的(大概吧)可执行程序。
运行:大学毕业后,hello响应国家(shell)的号召,作为一个子进程,加入了社会(计算机)的运行。
异常:在工作中,总会有种种意外,但hello的朋友——异常处理程序总是会帮他解围。
IO:全靠IO接口,hello才得以把自己的成果展现出来。
回收:hello已经完成了他的使命,到了退休年龄,shell对他进行了回收,退休了的他居住在硬盘中的一个角落,内存中已没有他的痕迹…
这个大作业带我将本书的主要知识点串成一体,深入理解了程序从编译到运行具体经历什么过程,还熟悉了edb的调试过程,离成为一个合格的程序员又近了一步。
(结论0分,缺失 -1分,根据内容酌情加分)
附件
列出所有的中间产物的文件名,并予以说明起作用。
hello.i(hello.c预处理之后的程序文本)
hello.s(hello.i编译成汇编语言之后的程序文本)
hello.o(hello.s生成的二进制文件)
hello_elf.txt(读取出的hello.o的elf头)
hello_o.s(hello.o反汇编的结果)
hello(可执行的hello二进制文件)
hello_.s(可执行文件hello,直接用objdump反编译之后的汇编代码)
hello.elf(可执行文件hello的elf表)
(附件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.
[7] 兰德尔E.布莱恩特 大卫R.奥哈拉伦.深入理解计算机系统 机械工业出版社,2016
[8] 小明难亡 https://blog.csdn.net/qq_44242536/article/details/85231624 CSDN
(参考文献0分,缺失 -1分)