计算机系统

大作业

题 目 程序人生-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简介

P2P:当一个个ASCII字符被有规律的敲下时,hello就慢慢被孕育了。Hello的c源文件被敲下后,经过预处理,删除注释,替换宏,展开宏,再进行编译,变为汇编代码,进一步进行汇编,变成一个二进制的可重定位目标文件,最后链接,成为一个可执行的hello程序(program)。然后,shell或bash为hello fork一个新的进程(process),并将其加载。

O2O: 在加载之后,hello开始在cpu上一条一条指令的往下执行, 按照存储器的层次结构,出现数据不命中时便一层一层的向下寻找数据,使hello的执行加快了很多。在运行结束后,shell或bash接收到信号,将其回收,释放其占用的内存空间,删除其上下文,hello的一次执行到此结束,归零!

1.2 环境与工具

Ubuntu 64位,edb,shell,readelf,objdump

1.3 中间结果

hello.i:预处理后的hello程序

hello.o:汇编后的可重定位目标文件

hello.s:编译后的hello程序

hello.s2:从hello.o反汇编得到的汇编语言文件

rhello:从hello可执行程序反汇编得到的汇编语言文件

1.4 本章小结

简要解释了P2P,O2O的含义和过程,列出了所需的环境、工具以及在研究过程中生成的结果。

第2章 预处理

2.1 预处理的概念与作用

预处理:根据以#开头的命令,修改原始c程序,并删除注释

作用:预处理器cpp对.c文件进行展开、替换宏定义,将头文件插入,并删除注释内容。

2.2在Ubuntu下预处理的命令

图2.1 预处理命令

2.3 Hello的预处理结果解析

预处理得到的文件仍然是可读的文本文件,打开后可以看到有三千多行,其中大部分都是include进来的头文件,只在最后的一小部分是main函数的内容。

图2.2 hello.i文件中插入的头文件内容

图2.3 hello.i文件中原main函数中的内容

2.4 本章小结

本章主要介绍了预处理的概念和作用,说明了hello.c的预处理过程,包括预处理命令以及生成的.i文件。

第3章 编译

3.1 编译的概念与作用

概念:编译就是将预处理后的.i文件编译成汇编语言程序.s。

作用:编译生成用低级机器语言指令描述的hello代码。与高级语言相比,汇编语言更接近计算机能够理解的语言。

3.2 在Ubuntu下编译的命令

图3.1 编译命令

3.3 Hello的编译结果解析

3.3.1 参数的存储

从以下代码可以看出,main的参数存在栈中。在跳转时比较的是%rbp-20中的值和4的关系,因此这个地址中存放的就是main的argc参数。同时,也可以猜到%rbp-32开始应该存放的就是argv参数了。

另外,printf的字符串常量参数存放在LC0和LC1代码段的.string中。

图3.2 main的参数

图3.3
字符串常量

3.3.2 if语句的处理

首先,当argc的值与4不相等时,条件成立,不进行跳转,继续执行。可以看到,leaq指令将要输出的字符串的地址放到rdi中作为参数,然后调用函数进行打印输出。最后把立即数1放入edi作为输出状态参数,然后调用exit函数退出。若条件不成立,就跳转到L2处进行循环。

图3.4 条件成立

3.3.3 for循环的处理

循环是从L2开始的,然后L2、L3、L4三个代码段都包含循环的部分。L2段是对循环变量i进行初始化,这个变量存储在栈上%rbp-4的位置。赋初值后跳转到L3的位置。L3是进行循环结束条件的判断,如果不到结束的条件,就跳转到L4;如果到了结束的条件,就向下执行函数最后的部分。L4部分是循环体部分,call前的一系列操作都是在准备参数,将参数转移到寄存器上。最后参数被存在了rdi,rsi,rdx中,然后调用了printf函数进行打印输出。然后是将atoi的参数放入寄存器rdi,再将其输出值从rax中转移到rdi中,调用sleep休眠,然后让i增加1。

图3.5 for循环的机器代码

3.4 本章小结

本章阐述的编译的概念和作用,说明了编译的linux指令,并以参数存储、if,for结构为线索解释了程序中各要素在机器指令中的运行逻辑。

第4章 汇编

4.1 汇编的概念与作用

概念:将汇编语言转化成二进制的机器语言指令。

作用:生成可重定位目标程序

4.2 在Ubuntu下汇编的命令

图4.1 汇编命令

4.3 可重定位目标elf格式

4.3.1 ELF头

ELF头中包含一些基本信息,比如文件类型为可重定位目标文件,节头部表的偏移,ELF头的大小,机器类型等。

图4.2 ELF头

4.3.2 节头部表

节头部表中记录了每个节的位置和大小。

图4.3 节头部表

4.3.3 重定位信息

这些重定位条目里有链接生成可执行文件时需要的重定位信息。其中包括重定位类型Type(相对寻址还是绝对寻址),重定位位置的偏移量Offset,被修改引用指向的符号等。

图4.4 重定位信息

4.3.4 符号表

给出了各个符号及其类型。

图4.5 重定位信息

4.4 Hello.o的结果解析

汇编后的机器指令中不再有代码段的各个编号,而是所有的指令都有了自己的一个地址,根据地址和偏移值进行跳转和函数调用。而且需要重定位的一些偏移值变成了0。另外,反汇编后的立即数为16进制,反汇编前用的是10进制。

图4.6 反汇编文件

4.5 本章小结

本章介绍了汇编的概念、功能、ELF头中的各项信息,并且针对汇编得到的机器代码进行反汇编后得到的文件与原汇编文件进行了对比分析。

第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.1链接命令

5.3 可执行目标文件hello的格式

Hello的ELF文件中有ELF头,段头部表,最后的节头部表以及各个数据节,如.text代码节,.data数据节等。

5.2 段信息

5.4 hello的虚拟地址空间

INTERP段:

5.3 INTERP段内容

可以看到动态链接库。

LOAD段

5.4 LOAD段内容

DYNAMIC段:

5.5 DYNAMIC段内容

5.5 链接的重定位过程分析

在链接的过程中,链接器通过可重定位目标文件中的信息进行符号解析和重定位

查看hello反汇编后的main函数,可以看到所有的需要重定位的位置都已经填上了地址,而不再是原来的0。链接器将地址重新分配,使每条指令都有自己的地址后,再根据重定位条目中的信息,比如相对或绝对寻址,重定位项的偏移量等确定要跳转的位置,然后将这个绝对或相对地址直接写到指令中。

图5.6 链接后的main函数

同时,可以看到,链接后的文件最开始多了PLT表,链接器通过加入这个表来实现延迟绑定一个过程的地址。

图5.7 链接得到的PLT表

5.6 hello的执行流程

_init

puts@plt-0x10

puts@plt

printf@plt

__libc_start_main@plt

getchar@plt

atoi@plt

exit@plt

sleep@plt

.plt.got

_start

main

__libc_csu_init

__libc_csu_fini

_fini

5.7 Hello的动态链接分析

由于链接器采用延迟绑定的策略,在执行的过程中,.plt表的内容会发生变化,这种方式使得函数被调用过一次后下一次的跳转就会快很多,可以直接跳转到目标函数。

在跳转之后,通过edb调试可以看到,0x601000后的某些字节的数据发生了改变。

图5.8 动态链接分析

5.8 本章小结

本章介绍了编译的概念和作用,分析了hello可执行文件的格式和虚拟空间,通过与可重定位目标文件进行比较,分析了重定位过程。另外,还分析了hello的执行流程和动态链接项目。

第6章 hello进程管理

6.1 进程的概念与作用

进程概念:运行中程序的实例。

进程的作用:进程使多个任务可以在cpu上并发执行,且每个任务都可以看作自己是独占cpu的。

6.2 简述壳Shell-bash的作用与处理流程

Shell是一个命令解释器,是操作系统的最外层,是一个操作系统与用户进行交互的应用程序。

Shell首先读入命令行输入,然后分析判断是否是内置命令,如果是内置命令就执行这个内置命令,若不是内置命令,就作为一个程序,根据命令行输入构建参数列表,创建新的进程,运行加载器,运行这个程序。同时还可以根据命令行参数选择前台或后台执行。

6.3 Hello的fork进程创建过程

当输入./hello时,sheel判断为非内置命令,就会根据命令行输入构造参数列表,然后用fork函数创建新的进程,刚创建的进程与父进程有相同的上下文,但可以用pid的不同来区分。

6.4 Hello的execve过程

被fork出的子进程调用 execve 函数在当前进程的上下文中加载并运行一hello。加载器创建一组新的代码、数据、堆和栈段,新的代码和数据段初始化成可执行文件中的内容。最后再设置程序计数器指向程序入口点,就完成了加载过程。

6.5 Hello的进程执行

上下文是指内核重新启动一个被抢占的进程所需要的状态,它由寄存器,程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构等对象的值构成。

Hello进程有自己的上下文,同时,由于hello是与其他程序并发执行的,cpu会执行上下文切换,在其他程序的时间片将寄存器等的状态改变成另一个进程的上下文,并将控制传给将运行的进程,或者在要继续运行hello时将上下文切换回hello的上下文,并将控制传回。

6.6 hello的异常与信号处理

程序执行过程中可能出现的异常有中断,陷阱,故障,终止四种。其中,中断和陷阱总会返回到下一条指令执行,故障将会根据故障处理的结果来确定到底是返回出现故障的指令还是结束程序,终止被看做是不可解决的问题,将永远不会返回,直接结束程序。

可以看到,乱按不会影响程序正常进行,只是按下的字符将被打印在屏幕上。

图6.1 乱按

Ctrl-c发送的是SIGINT信号,将会使程序结束。

图6.2 Ctrl-C

Ctrl-z发送的是SIGTSTP信号,将会使程序挂起。

图6.3 Ctrl-Z

在程序挂起后可以运行ps、jobs、pstree等,可以看到刚才挂起的程序。

图6.4 挂起后运行pstree、ps、jobs等

用kill给hello发送终止信号后,可以看到hello程序已经消失。

图6.5 kill发送终止信号

6.7本章小结

本章说明了进程的概念和作用,介绍了shell的处理流程和加载一个程序的过程。同时也介绍了hello的异常和信号处理。

结论

Hello整个程序从被写出来到加载成一个进程到最后生命结束程序终止,经历了以下阶段:

1.预处理

将hello.c 中include 的所有外部的头文件头文件内容直接插入程序文本中,完成字符串的替换。

2.编译

将程序翻译成汇编代码。

3.汇编

将汇编代码翻译成二进制的可重定位目标文件。

4.链接

通过链接器,将hello 的程序编码与动态链接库等收集整理成为一个单一文件,生成完全链接的可执行的目标文件hello。

5.加载运行

在shell中输入命令 ./hello 2021112637 刘俊杰 10,shell为其fork 新建进程,并通过execve 加载程序,程序开始执行;

6.执行指令

在该进程被调度时,CPU 为hello 其分配时间片,在一个时间片中,hello享有CPU 全部资源,PC 寄存器不断更新。

附件

hello.i:预处理后的hello程序

hello.o:汇编后的可重定位目标文件

hello.s:编译后的hello程序

hello.s2:从hello.o反汇编得到的汇编语言文件

rhello:从hello可执行程序反汇编得到的汇编语言文件

参考文献

[1] Randal E.Bryant, David O’Hallaron. 深入理解计算机系统[M]. 机械工业出版社.2018.4

[2] xiao_chen.ELF .https://blog.csdn.net/qq_32014215/article/details/76618649.