计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 计算机科学与技术
计算机科学与技术学院
2019年12月
摘 要
本文旨在通过对hello的一生的探讨,回顾本学期计算机系统的学习内容。通过对linux环境下hello.c程序的p2p和o2o过程的分析,进一步深入了解计算机系统
关键词:预处理;编译;汇编;链接;进程;存储;IO管理;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的P2P过程:对hello.c的文本文件进行预处理生成hello.i,然后经过编译生成hello.s,然后经过汇编生成hello.o,最后经过链接生成可执行文件。
Hello的O2O过程:shell执行可执行目标文件,对进程进行管理,然后对储存进行管理,映射虚拟内存,分配物理内存,最后将hello的结果输出的显示器上,结束进程,释放内存空间。
1.2 环境与工具
硬件环境:Intel(R)Core(TM)i5——5350U [email protected] 1.80GHz;8GB(RAM);X64CPU
软件环境:Windows10 64 位;VirtualBox;Ubuntu;
工具:VS;codeblocks;gdb;Objdump;Hex Editor Neo
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
1.4 本章小结
简述的P2P,O2O,分析列举了实验的前期工具,环境准备和实验中可能产生的中间文件
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
预处理的概念:是文件再编译前做的对源文件进行简单加工的工作,主要处理头文件和宏。将所有#开头的行解释位预处理指令,预处理指令一般被用来使源代码在不同的执行环境中被方便的修改或者编译。
预处理的作用:
预处理从系统的头文件包中将头文件的源码插入到目标文件中生成.i文件,在编译代码第一时间将标识符替换好,成为中间码后再进行正式的编译工作,便于编译。
2.2在Ubuntu下预处理的命令
预处理的命令:
gcc -m64 -no-pie -fno-PIC -E hello.c -o hello.i
2.3 Hello的预处理结果解析
hello.c:
hello.i:
相较于hello.c文件,文件内的主函数部分基本未变,但是多出了3000多行的#开头的代码(三个被引用的头文件的源码),其中一部分描述了运行库在计算机内的位置,一部分对可能用到的函数进行了申明。
(以下格式自行编排,编辑时删除)
2.4 本章小结
对预处理的概念于过程进行了理解与分析,图文表述了linux下c文件的预处理操作流程,与.i文件的结构与内容。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
编译的概念:将c语言的文本文件,翻译成汇编语言的文本程序。
作用:是将高级语言向最底层的二进制文件翻译的必经之路,汇编语言是距离底层最接近的语言,编译使得二进制文件的生成更加便捷。
3.2 在Ubuntu下编译的命令
编译的命令:gcc -m64 -no-pie -fno-PIC -S hello.c -o hello.s
3.3 Hello的编译结果解析
生成的文件:
生成的hello.s文件
3.31处理变量:
局部变量:
int i
局部变量i存放在栈中,具体在-4(%rbp)
for循环和比较的过程中在L3内
3.3.2 关系操作符与控制语句
Argc与4进行比较在.s文件中用汇编语言表述就是cmpl的-20(%rbp)与立即数4进行比较,然后je指令与cmpl一起实现条件判断,cmpl比较后改变条件数,再用je依据条件数进行跳转或不跳转。
3.3.3四则运算
1加:x=x+y:addq y,x
2减:x=x-y:subq y,x
3乘:x=xy:imulq y,x
4除:z=x/y:movq x,z
cqto
idivq y
5复合:z=x+Ay+B:leaq B(x,y,A),z
3.3.4 对函数的处理
利用了栈帧的结构
函数的传参:%rdi/%edi储存第一个参数
%rsi/%esi:储存第二个参数
%rdx/%edx:储存第三个参数
%rcx/%ecx:储存第四个参数
。。。。
函数的调用:用call来跳转到函数的开头部分。
函数的返回:用ret返回调用函数前保存的返回地址。
函数的返回值:用%rax/%eax来保存函数的返回值,c语言函数只能返回一个返回值。
3.4 本章小结
分析了程序在编译期间做的一系列操作,总结了各个阶段如数据类型的分析、函数操作、类型转换、关系的操作和控制转移、算术操作等。
第4章 汇编
4.1 汇编的概念与作用
汇编的概念:
将.s文件转化位.o文件到最后生成机器语言二进制程序的过程
4.2 在Ubuntu下汇编的命令
汇编命令:gcc -m64 -no-pie -fno-PIC -c hello.s -o hello.o
生成了.o文件
4.3 可重定位目标elf格式
指令:readelf -a hello.o > hello.elf
elf内的文件内容:
elf头:描述字的大小,字节顺序,文件类型,机器类型……
.text段:代码段
.rodata节(只读):跳转表
.data节(数据/可读写):已初始化的全局变量,静态c变量。
.bass节:未初始化全局变量,静态c变量(不占实际空间,只有占位符)
.symtab节(符号表):符号表,函数和静态变量名,节名称和位置。
.rel.text节(可重定位代码):text中的可重定位信息,在可执行文件中需要修改的指令地址。需修改的指令。
.rel.data节(可重定位代码):data中的可重定位信息。在合并后的可执行文件中需要修改的指针数据的地址。
.debug节(调试):为符号调试的信息。
节头表:每个节的偏移量与大小。
4.4 Hello.o的结果解析
指令objdump -d -r hello.o
对比main函数,.s代码与.o反汇编的代码在语句上基本上一致。每一句汇编代码在.o文件中对应一段二进制的机器码。5
在跳转语句上:.s用的是.L3/.L4等等表示,反汇编之后call后面会加上具体的地址。
函数的调用上:.s只是在call后面加函数名,但是反汇编后的call最后要加上具体的地址。
全局变量访问上:.s文件中访问时用段名加%rip,反汇编时用0+%rip。
说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。
4.5 本章小结
汇编的过程相当于重定位,将汇编语言和机器语言以一一映射的关系对应起来翻译。使得程序进一步转化为可识别与可执行。
第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
5.4 hello的虚拟地址空间
PHDR:程序头表
INTERP:程序解释器
LOAD:程序目标代码与常量信息
DYNAMIC:动态链接的信息
NOTE:其它信息
GNU_EH_FRAME:保存异常信息
GNU_STACK:使用系统栈所需要的权限信息
GNU_RELRO:保存在重定位之后的制度信息的位置
(我的电脑edb下载始终不成功,无法使用)
5.5 链接的重定位过程分析
指令:readlf -a hello>hellodo.elf
hellodo.elf:
hello比hello.o多出了一些节头表
interp:保存ld.so的路径
.note.ABI-tag
.note.gnu.build-i:编译信息表
.gnu.hash:gnu的扩展符号hash表
.dynsym:动态符号表
.dynstr:动态符号表中的符号名称
.gnu.version:符号版本
.gnu.version_r:符号引用版本
.rela.dyn:动态重定位表
.rela.plt:.plt节的重定位条目
.init:程序初始化
.plt:动态链接表
.fini:程序终止时需要的执行的指令
.eh_frame:程序执行错误时的指令
.dynamic:存放被ld.so使用的动态链接信息
.got:存放程序中变量全局偏移量
.got.plt:存放程序中函数的全局偏移量
.data:初始化过的全局变量或者声明过的函数
5.6 hello的执行流程
先开始载入:_dl_start、_dl_init
然后开始执行:_start、_libc_start_main、_init
接着执行main函数:main、_printf、_exit……
最后结束退出:exit();
5.7 Hello的动态链接分析
链接器编译到动态链接库中的PIC函数,添加重定位记录,等待动态链接器处理。链接器采用延迟绑定的策略。过程链接表PLT与全局偏移量表GOT实验函数的动态链接。PLT通过GOT中存放的目标地址跳转到目标函数。
5.8 本章小结
概述了链接的过程与作用,分析了程序的elf文件格式,分析了hello的虚拟地址空间,重定位到执行的过程,解析了动态链接的原理。
第6章 hello进程管理
6.1 进程的概念与作用
进程的概念:进程是一个执行中程序的实例。
作用:能够实现计算机并行实现不同任务的构想,使得程序的模式切换更加自如迅速,效率更高。
6.2 简述壳Shell-bash的作用与处理流程
在Linux系统中,Shell是一个交互型应用级程序,
Shell读取输入
处理输入内容,获得输入参数
如果是内核命令则直接执行,否则调用程序执行。
当程序运行时,shell会监视用户输入并响应用户的输入。
6.3 Hello的fork进程创建过程
在shell的命令行,用户完成输入命令。shell在解析输入命令之后创建argv、envp等参数列表,以及参数个数argc。shell作为父进程创建新子进程。子进程与父进程拥有完全相同的虚拟内存地址以及副本,但虚拟地址确是独立的,而且PID也不相同。
6.4 Hello的execve过程
创建子进程之后,判断PID是否=0,判断是否子进程。然后在子进程内调用execve在当前上下文加载运行hello程序。调用了启动加载器来执行程序,然后新的代码段会被初始化好为hello做准备,PC指向_start。
6.5 Hello的进程执行
上下文:上下文通常包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈、和各种内核数据结构等。
Hello在调用sleep时若发生被占的情况,则上下文会发生切换,进入内核状态,内核处理休眠之后主动释放当前进程,定时器发送一个中断信号,由中断处理函数完成处理,将 Hello 进程从等待 队列中移出重新加入到运行队列,从而进行上下文切换进入到 Hello 进程。
6.6 hello的异常与信号处理
正常输入
按control C 进程结束
按 control Z 将进程挂起
Fg命令:让挂起的进程继续执行。
Jobs命令:查看当前命令内容
Pstree命令:用进程树把各个进程用树状图的方式链接起来。
Kill指令:向固定行发送信号。
(以下格式自行编排,编辑时删除)
hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。
6.7本章小结
解析概述了进程的概念,和shell处理过程的方式,介绍了fork和execve两个函数的执行过程与信号异常的处理。
第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址:汇编程序中地址。逻辑地址由选择符和偏移量组成。
虚拟地址:CPU通过虚拟地址来访问主存,这个虚拟地址在被送到主存前先转换成适当的物理地址,并与物理地址一一对应。
物理地址:在存储器里以字节为单位存储信息,每一个字节单元给以一个唯一的存储器地址,称为物理地址。
线性地址:逻辑地址经过段机制后转化为线性地址,为描述符:偏移量的组合形式。分页机制中线性地址作为输入。
7.2 Intel逻辑地址到线性地址的变换-段式管理
段式管理:是指将一个程序分为若干个段进行储存和管理,每个段都是一个实体。8086共设计了20位宽的地址总线,通过将段寄存器左移4位加上偏移地址得到20位地址,这个地址就是逻辑地址。将内存分为不同的段,段有段寄存器对应,段寄存器有一个栈寄存器、一个代码寄存器、两个数据寄存器。
7.3 Hello的线性地址到物理地址的变换-页式管理
系统将每个段分割成虚拟页的大小固定的快来作为数据传输单元,虚拟内存系统中MMU负责地址翻译,MMU使用存放在物理内存中的被称为页表的数据结构将虚拟页到物理页的映射,即虚拟地址到物理地址的映射。
7.4 TLB与四级页表支持下的VA到PA的变换
TLB:每次CPU产生一个虚拟地址,MMU就必须查阅相应的PTE,每次都必然造成缓存不命中等一系列时间开销,为了消除这样的开销,MMU中存在一个全相联高速缓存,称为TLB。
四级页表:如果只需要使用的虚拟内存很小,但仍然需要一个十分巨大的页表,造成了内存的浪费,或者在某些情况页表会变得十分巨大。所以虚拟地址到物理地址的转换过程中还存在多级页表的机制:上一级的页表映射到下一级页表,直到页表映射到虚拟内存。
7.5 三级Cache支持下的物理内存访问
物理地址被分为CT(标记)+CL(索引)+CO(偏移量),然后到1级cache里去找对应的标记位为有效的。若找不到再到2级,3级cache里去找对应的字节,找到后返回,若找不到,则启动替换策略,将新的条目逐级替换插入。
7.6 hello进程fork时的内存映射
父进程创建了自己的儿子进程之后,内核为新生儿进程分配唯一一个PID用以标识身份,然后子进程继承了与父进程当前完全一致的在虚拟内存中的副本
7.7 hello进程execve时的内存映射
将.text,.data,.bss都加载到虚拟地址空间之中。
7.8 缺页故障与缺页中断处理
若未分配状态则发生了缺页,先要根据牺牲策略决定一个牺牲页,然后进行页替换。
7.9动态存储分配管理
基本方法与策略:通过维护虚拟内存(堆),一种是隐式空闲链表,一种是显式空闲链表。显式空闲链表法是malloc(size_t size)每次声明内存空间都保证至少分配size_t大小的内存,双字对齐,每次必须从空闲块中分配空间,在申请空间时将空闲的空间碎片合并,以尽量减少浪费。
7.10本章小结
虚拟内存使得程序的调用更加私密,也使得物理空间得到了最大利用。也使得每个进程都有一个自己的虚拟空间,让进程运行的更加效率。
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
Linux中的I/O设备被表示为文件,输入输出用文件的读和写来执行
设备的模型化:文件
设备管理:unix io接口
8.2 简述Unix IO接口及其函数
打开文件: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的实现分析
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
int getchar(void)
{
char c;
return (read(0,&c,1)==1)?(unsigned char)c:EOF
}
8.5本章小结
本章主要总结了Linux的I/O设备的管理方法和一些输入输出函数以及一些端口函数,分析了最常用的getchar()与printf()。
结论
回顾简简单单的hello在计算机中走完的一生,我们可以大致的将其区分出如下多步:
编写:程序员提供编写好的c文件源码。
预处理:预处理器将.c文件中的宏定义与库先一步处理好生成.i文件
编译:编译器将预处理完的源码转换成了汇编代码,完成了高级语言向低级语言的转化,也有优秀而熟练的程序员能够直接编写汇编代码,汇编代码也是我们能够理解和阅读的最接近底层的文件结构了。
汇编:汇编器将汇编代码转化为二进制机器代码,成为真正在计算机底层运行计算的01二进制代码,到了这一步代码的结构对于程序员已经几乎不可阅读和理解。
链接:链接器将上述生成的可重定位文件链接后生成可执行文件,P2P也就基本完成了。
运行:在shell中运行Hello,先fork后execve,经历了访问内存以及可能出现的异常处理后Hello真正的运行了。
回收:Hello在运行结束后shell回收子程序,Hello又回到了虚无,丝毫不留下痕迹。
附件
hello.c 源文件
hello.i 预处理后的文件
hello.s 编译后的文件
hello.o 可重定位文件
hello.elf可重定位文件的ELF
hello 连接后的可执行文件
hellodo.elf 可执行文件的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分)