计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 计算机科学与技术
学 号 2021113538
班 级 2103101
学 生 张纬如
指 导 教 师 刘宏伟
计算机科学与技术学院
2022年5月
本文结合《深入理解计算机系统》一书和对该对应课程的学习,对一个程序Hello的一生,各个阶段,预处理、编译、汇编、链接,进程管理,和存储等等方面进行了系统分析,经历了P2P的丰富过程:From Program to Process,从程序的结构和执行以及在系统上运行程序两大方面,从程序员的视角,对程序的一生以及计算机系统进行探讨和讲述。
关键词:计算机系统;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 -
参考文献....................................................................................... - 16 -
第1章 概述
1.1 Hello简介
P2P:From Program to Process
理解:当我们在各种IDE或者文本编辑器中完成了基于c语言语法的hello源程序代码,hello.c,我们需要一个过程,将program变成process,首先是从源文件到可执行目标文件的生成:
源文件经过预处理、编译、汇编、链接后就生成了可执行目标文件。
然后是对目标文件的运行:
在bash(shell)中,输入./hello,对可执行目标文件进行运行,载入内存,由OS进行管理,为它fork建立子进程,并用execve进行替换,然后进行执行,由CPU、ISA和对应二进制的指令确定程序的具体执行过程,涉及流水、进程切换、管理、异常等等。
O2O:Zero to Zero
理解:是hello可执行目标文件从执行到最后被回收的整体过程。在bash(shell)中运行后,bash给该程序建立了对应的子进程,然后是虚拟内存的相关管理和设计。运行完成后,bash回收子进程,内核删除相关数据,释放内存资源。就是完整的从无到无。
1.2 环境与工具
硬件:
X64 CPU;3.20GHz;16.0G RAM;512GHD Disk
软件:
Windows10 64位;Vmware; Ubuntu 64位
1.3 中间结果
中间结果文件:
hello.c hello.i hello.s hello.o hello. elf.txt hello_elf.txt
1.4 本章小结
本章介绍了hello程序对应的P2P和O2O内容,对其进行了大体上的描述,也介绍了本文所使用的工具环境,并列出了中间结果文件及其相应的作用。
第2章 预处理
2.1 预处理的概念与作用
概念:C语言的预处理器在源代码编译之前对其进行一些文本性质的操作。
作用:它的主要任务包括删除注释、插入被#include指令包含的文件内容、定义和替换由#define指令定义的符号,同时确定代码的部分内容是否应该根据一些条件编译指令进行编译。
2.2在Ubuntu下预处理的命令
命令:gcc -E hello.c -o hello.i
-E 只进行预处理生成预处理文件
2.3 Hello的预处理结果解析
源文件:
编译后:可以看到文件从23行填充为3060行,并且在末尾我们找到了和源文件相同的代码部分,注释已经被删除,并将#include均进行了展开
通过两者的比较,我们可以发现:预处理程序对以#开头的12个命令进行了处理:如#include、#ifdef、#ifndef等等;并将头文件包含的文件进行了插入,删除了注释。
2.4 本章小结
本章介绍了hello预处理的详细过程,根据规定的预处理命令,修改源程序,进行插入,注释删除等文本操作,从源程序的文本文件,得到预处理后的文本文件。
第3章 编译
3.1 编译的概念与作用
概念:将人所理解的c语言文本文件,变成容易被机器理解的汇编语言文本文件。
作用:让hello.i变成hello.s,为后来的汇编成机器码的二进制文件作准备
注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序
3.2 在Ubuntu下编译的命令
命令:gcc -S hello.i -o hello.s
-S只进行编译
3.3 Hello的编译结果解析
我们将对汇编程序文件的数据类型和操作都进行逐步解析。
3.3.1整型常量:
(还有0和9同理,不过因为i<9,在汇编程序中以和8比较的形式替代)
汇编代码:该处作为立即数,进行比较操作,然后设置条件码,进行条件跳转
3.3.2字符串常量:
汇编代码:printf中有一个字符串,这个字符串保存在.LC0处
3.3.3局部变量:
汇编代码:该i作为循环变量保存在内存中-4(%rbp)处
3.3.3指针、数组:
汇编代码:argv是一个指针数组,每一个元素为八个byte,内存中-32(%rbp)处为数组起始地址,加3*8后为argv[3]
3.3.4函数参数
内容同3.3.3,调用atoi函数,参数为argv[3],该数从内存中被取出后,放入了%rdi,%rdi为其参数
3.3.5比较操作
同3.3.1,进行了argc与4的比较,然后进行条件跳转
3.3.6算术操作
汇编代码:该处的循环变量加一操作,后设置条件码,进行比较
3.3.7函数操作调用和返回
汇编代码:由于sleep、getcchar函数均没有在该文件中定义,所以存在文件中的ret只有main函数的返回,将返回值传递回系统进行处理。Call后面存在的getchar@PLT之后在链接过程中会重定位为相应函数的地址。
3.4 本章小结
本章总结了由预处理文件到汇编语言文件的转换,具体分析了编译器对各个类型和各个操作的处理,及其对应的汇编语言代码。将人所理解的代码转换为机器容易理解的代码。
第4章 汇编
4.1 汇编的概念与作用
概念:将汇编程序文件变为可重定位目标文件。
作用:将文本文件转为二进制文件,从机器容易理解的程度提高至,能够真正被机器所理解执行的程度,是真正的机器语言,以二进制文件的形式保存。
注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。
4.2 在Ubuntu下汇编的命令
命令:gcc -c hello.s -o hello.o
-c 只生成可重定位目标文件
4.3 可重定位目标elf格式
通过readelf进行对格式文件的获取
4.3.1 ELF头和节头部表
分析:首先是16字节的序列,描述了生成该文件的系统的字大小和字节序,然后是elf头部表的大小,目标文件类型可重定位文件,节头部表的偏移和其条目大小和数量
4.3.1 节
在ELF头和节头部表之间都是各个节的内容
.text .rodata .data .bss .symtab等等节
.rel.text(.rela.text):重定位节,包含了.text中需要进行重定位的信息,其中描述的信息需要在生成可执行文件时,进行对.text的修改,如call后的地址等等。
重定位条目:常见有两种类型:
R_X86_64_32:绝对寻址,直接使用32位的绝对地址作引用进行替换。
R_X86_64_PC32:PC相对寻址,将要使用的地址和运行的指令下一条指令地址的值作差,用差值进行替换。
4.4 Hello.o的结果解析
4.4.1 分支跳转:
跳转语句后,hello.s使用的是段名称,而反汇编的代码中则是PC相对寻址或者绝对寻址,其对应的地址
4.4.2 函数调用:
Call后,hello.s使用的是函数名称,而反汇编代码后则是PC相对寻址或者绝对寻址,其对应的地址。
4.4.3 操作数的表示
常数,hello.s使用的是十进制数,而反汇编使用十六进制表示。
4.5 本章小结
本章对汇编结果进行了分析,经过汇编过程,汇编语言转化位机器语言,比较hello.s文件和hello.o反汇编形式我们很清楚的看出了其中的不同之处,可以看出重定位节对后面链接过程的铺垫作用。
第5章 链接
5.1 链接的概念与作用
概念:链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可以被加载到内存并执行。链接可以执行于编译时,也就是在源代码被编译成机器代码时;也可以执行于加载时,也就是在程序被加载器加载到内存并执行时;甚至于运行时,也就是由应用程序来执行。
作用:把可重定位目标文件和命令行参数作为输入,产生一个可以加载运行的可执行目标文件。
注意:这儿的链接是指从 hello.o 到hello生成过程。
5.2 在Ubuntu下链接的命令
命令:
5.3 可执行目标文件hello的格式
5.3.1 ElF头
同4.3.1
5.3.2头节部表
描述了30个节,比可重定位目标文件多出了很多节:
.dynamic .rela.dyn等等
5.4 hello的虚拟地址空间
5.5 链接的重定位过程分析
5.5.1代码起始位置
Hello从0x401000开始,而hello.o则是从0x0开始
5.5.2函数的引入,如下:
将各个可重定位目标文件进行合并、链接,形成一个可执行文件。
5.6 hello的执行流程
-init
puts@plt
printf@plt
getchar@plt
atoi@plt
exit@plt
sleep@plt
_start
main
5.7 Hello的动态链接分析
PlT:PLT是一个条目数组,每个条目是16个字节的代码,PLT[0]是一个特殊条目。跳转到动态链接器中,每个条目负责一个函数
GOT:GOT同样是一个条目数组,每个条目8个字节,和PLT联合使用,GOT的前两个条目,提供解析函数地址时会使用的信息,第三个是动态链接器在ld-linux.so模块的入口点。其余条目对应于一个被调用函数,在运行时解析。
5.8 本章小结
本章主要研究了链接的内容,重点研究了链接后的hello与hello.o之间的区别,通过链接,将可重定位文件转化为了可执行文件。
第6章 hello进程管理
6.1 进程的概念与作用
概念:一个执行中程序的实例,系统的每个程序都运行在某个进程的上下文。而上下文是由程序正确运行所需要的状态组成,包括内存力的程序的代码、数据、栈、寄存器内容、pc、环境变量等等状态。
作用:通过进程,使得我们的程序好像是当前唯一运行的程序,独占处理器和内存,也独占虚拟空间。
6.2 简述壳Shell-bash的作用与处理流程
作用:是用户和系统之间的桥梁,提供给用户与内核进行交互的窗口,并可以使用shell提供的命令进行操作。
处理流程:用户在命令行进行输入,shell接收并解析,对内置命令直接执行,非内置,检查是不是可执行文件,是就在子进程中执行;
1)读取命令,解析为对应参数;
2)判断内置,是就立即执行;
3)不是内置,检查是不是可执行文件,是fork子进程,execve加载并运行程序;
4)判断前后台,前台则等待,后台则进入下次循环。
6.3 Hello的fork进程创建过程
输入./hello后,bash(shell)发现不是内置命令,尝试用子进程运行该可执行目标文件。
当bash调用fork生成子进程时,子进程保存了父进程几乎所有信息、状态。两者不同在pid。
6.4 Hello的execve过程
execve函数在当前进程的上下文中加载并执行一个新的程序,直接替换之前的程序内容在头部表的引导下,加载器将可执行文件加载到对应的代码段和数据段,然后跳转到程序入口,启动函数,初始化环境,调用main函数,处理返回值,返回内核。
6.5 Hello的进程执行
操作系统内核使用上下文切换的异常控制流来实现多进程并发执行。上下文是一个进程运行所需要的状态。
执行过程中,上下文切换,是由内核决定的,什么时候抢占当前进程,开始另一进程,称之为调度:
1)进程将控制交给内核;从用户模式进入内核模式
2)保存当前进程上下文;内核模式
3)恢复重启进程的上下文;内核模式
4)将控制传递给重启进程,完成上下文切换;从内核模式进入用户模式
Hello程序和系统其他进程通过内核进行调度,切换上下文,在各自的时间片内,由内核决定一个进程运行足够长的时间或是有异常、信号等进行上下文切换,暂停当前进程,重启其他进程。
6.6 hello的异常与信号处理
异常种类:中断、陷阱、故障、终止
1)中断:唯一的异步进行,来自外部IO设备的信号,中断信号到达后,当前指令执行完后,内核调度处理程序进行处理,然后执行被中断的程序的下一条指令;
2)陷阱:同步,由执行指令产生的结果,有意的异常,通过它系统调用来从用户模式进入内核模式;
3)故障:同步,由错误引起,可能返回到原进程的原指令处,或下一条指令处,也可能无法修正故障,导致程序直接终止。
4)终止:同步,由不可恢复的错误引起,通常是机器硬件错误,直接将控制返回给abort例程,终止引起终止的程序。
6.6.1不停乱按,包括回车
在未能完成该进程时,由于是前台任务,乱按不会产生影响,在完成后,之前的乱按会以回车为分界进行缓存为多个命令,进行解析,发现不是内置命令和可执行文件,就输出提示,跳过。
6.6.2 Ctrl Z
输入后会发送SIGTSTP信号给前台进程组的每个进程,将前台挂起
ps:可以看到正在执行的进程包括暂停的进程,所对应的名称和PID编号
jobs:可以看到唯一一个job hello被暂停了
pstree:通过pstree可以清晰看到父进程和子进程之间的关系
fg:将后台第一个作业变为前台,输入fg后,发现hello是第一个,所以使得hello程序又开始在前台运行。
kill:kill后通过ps查看,发现对应的进程被终止回收。
6.6.3 Ctrl C
发送了一个SIGINT信号给前台进程组的所有进程,终止前台的程序,通过ps我们发现hello对应的进程已经被回收。
6.7本章小结
本章介绍了进程的概念和作用,shell的概念和操作,如何fork子进程,execve加载进程,以及内核调度上下文切换,完成多任务多进程的并发,并且对异常和信号进行了研究和探索。
结论
Hello的经历异常丰富,从program到process经历了许许多多,最后在计算机系统整体配合下,完美落幕。
首先hello在系统中由hello.c经历了预处理成为,又经过编译成为,再是汇编,链接为,最后为可执行程序。
后来,为了运行,再内核的调度下,它得以在进程中加载,在异常控制流,上下文切换中,hello得以占据所有资源空间和cpu、内存的假象。而后在虚拟内存和IO部分的配合下,使得hello能够成功运行。
创新和尝试:
查阅资料后,我尝试使用了动态链接中的运行时链接的方式,使用dlopen、dlsym、dlcose等函数,对动态链接过程进行了探索,更深刻的理解了链接的过程,和不同的方式。
附件
hello.c hello.i hello.s hello.o hello. elf.txt hello_elf.txt
hello.c:源程序文件,文本文件。
hello.i:经过预处理后的文件,文本文件,展示预处理的过程和操作。
hello.s:汇编文件,文本文件,展示源文件对应汇编语言。
hello.o:可重定位目标文件,二进制文件,机器语言,为下一步链接成可执行文件做准备。
hello. elf.txt:hello.o的ELF文本内容
hello_elf.txt:hello的ELF文本内容
参考文献
[1] 深入理解计算机系统 第三版
[2] CSDN
[3] Github
[4] bilibili
[5] 学习通http://www.bdgxy.com/