计算机系统大作业

目 程序人生 -Hello’s P2P
专业 计算机科学与技术
2021112966
班级 2103101
学生 黄毅彬
指 导 教 师刘宏伟
计算机科学与技术学院
2022 年 5 月
计算机系统基础课程报告
摘 要
本文主要介绍了 hello.c 文件经过预处理、编译、汇编、链接变成可执行目
标程序 hello 的过程。本文从头到尾介绍了 hello 从编译到运行再到被回收的一
生。同时也以此为例,简要介绍了计算机系统。
关键词: 计算机系统;P2P;O2O;预处理;编译;汇编;链接;进程;shell
- 1 - 计算机系统基础课程报告
目 录
1 章 概述 ................................................................................................................- 4 -
1.1 H ELLO 简介 ........................................................................................................- 4 -
1.2 环境与工具 ....................................................................................................... - 4 -
1.3 中间结果 ........................................................................................................... - 5 -
1.4 本章小结 ........................................................................................................... - 5 -
2 章 预处理 ............................................................................................................- 6 -
2.1 预处理的概念与作用 ....................................................................................... - 6 -
2.2 U BUNTU 下预处理的命令 ............................................................................ - 6 -
2.3 H ELLO 的预处理结果解析 ................................................................................- 6 -
2.4 本章小结 ........................................................................................................... - 6 -
3 章 编译 ................................................................................................................- 9 -
3.1 编译的概念与作用 ........................................................................................... - 9 -
3.2 U BUNTU 下编译的命令 ................................................................................- 9 -
3.3 H ELLO 的编译结果解析 ....................................................................................- 9 -
3.4 本章小结 ......................................................................................................... - 15 -
4 章 汇编 ..............................................................................................................- 16 -
4.1 汇编的概念与作用 ......................................................................................... - 16 -
4.2 U BUNTU 下汇编的命令 ..............................................................................- 16 -
4.3 可重定位目标 ELF 格式 ..................................................................................- 16 -
4.4 H ELLO . O 的结果解析 .......................................................................................- 20 -
4.5 本章小结 ......................................................................................................... - 20 -
5 章 链接 ..............................................................................................................- 22 -
5.1 链接的概念与作用 ......................................................................................... - 22 -
5.2 U BUNTU 下链接的命令 ..............................................................................- 22 -
5.3 可执行目标文件 HELLO 的格式 .....................................................................- 22 -
5.4 HELLO 的虚拟地址空间 ...................................................................................- 26 -
5.5 链接的重定位过程分析 ................................................................................. - 28 -
5.6 HELLO 的执行流程 ...........................................................................................- 28 -
5.7 H ELLO 的动态链接分析 ..................................................................................- 31 -
5.8 本章小结 ......................................................................................................... - 33 -
6 HELLO 进程管理 .......................................................................................- 34 -
6.1 进程的概念与作用 ......................................................................................... - 34 -
- 2 - 计算机系统基础课程报告
6.2 简述壳 S HELL - BASH 的作用与处理流程 ....................................................... - 34 -
6.3 H ELLO FORK 进程创建过程 ........................................................................- 34 -
6.4 H ELLO EXECVE 过程 ....................................................................................- 34 -
6.5 H ELLO 的进程执行 ..........................................................................................- 35 -
6.6 HELLO 的异常与信号处理 ...............................................................................- 35 -
6.7 本章小结 ..........................................................................................................- 37 -
结论 ............................................................................................................................- 44 -
附件 ............................................................................................................................- 46 -
参考文献 ....................................................................................................................- 47 -
- 3 - 计算机系统基础课程报告
第 1 章 概述
1.1 Hello 简介
P2P
P2P Program to Progress hello.c 源文件,要生成可执行文件首先要进
行预处理:预处理器根据以 # 开头的命令,修改初原始的 C 程序,生成 hello.i
文件;其次要进行编译,将文本文件 hello.i 翻译成文本文件 hello.s ;接着进行
汇编,将 hello.s 翻译成机器语言指令,把这些指令打包成可重定位的目标程
序,并打包在文件 hello.o 中。最后进行链接,由链接器将可重定位的目标文
件进行合并,生成可执行文件。整个过程如下图所示:
1.1 编译图
020
020 from 0 to 0 。可执行文件需要执行环境,它可以在 linux 下通过 shell
进行运行,与计算机其他可执行文件同步与运行,并通过异常处理机制对响
应信号进行处理。 shell 执行 hello 后为其映射虚拟内存,随后载入物理内存开
始执行 hello 的程序,将其输出到屏幕,随后结束进程。程序运行结束后,父
进程回收子进程并清除相关数据。此时即为 hello 程序生命的终结。
1.2 环境与工具
1.2.1 硬件环境
X64 CPU 1GHz 16G RAM 256G GHD Disk
1.2.2 软件环境
Windows10 64 位; VMware Workstation 16.0 Ubuntu20.04 LTS 64
- 4 - 计算机系统基础课程报告
- 5 -
1.2.3 开发工具
Visual Studio Code 2019 gcc gdb objdump readel
1.3 中间结果
1. hello.i
hello.c 预处理之后的文件
2. hello.s
hello.i 编译成汇编语言后的文件
3. hello.o
hello.s 生成的二进制文件
4. hello
hello 的可执行文件
5. hello_1.txt
objdump hello.o 反汇编的结果
6. hello_2.txt
objdump hello 反汇编的结果
7. hello_3.txt
hello readelf 生成的 ELF
8. hello_4.txt
hello.o readelf 生成的 ELF
1.4 本章小结
本章主要介绍了 hello P2P 020 的整个过程以及实验的环境、工具和中间产
物。 计算机系统基础课程报告
- 6 -
第 2 章 预处理
2.1 预处理的概念与作用
预处理一般指由预处理器对程序源代码进行处理的过程。预处理器 (cpp) 根据
字符 # 开头的命令,修改原始的 C 程序。将 # 的部分的详细源码替换掉原来那行。
比如 hello.c 中第一行的 #include<stdio.h> 命令告诉预处理器读取系统头文件 stdio.h
的内容,并把它直接替换插入进程序文本中。
作用 :
1. #define 宏定义 : 将宏名替换为文本 : 字符串或者代码。
2. #include 文件包含 : cpp 将查找指定的被包含头文件 , 并将其复制插入到
#include 命令出现的位置上。
3. #if #else #endif 等条件编译 : 有些语句希望在条件满足时才编译 , 预处理过
程中根据条件决定需要编译的代码。
2.2 在 Ubuntu 下预处理的命令
2.2.1 预处理命令
2.3 Hello 的预处理结果解析
预处理以后文件是对 #include 的内容进行扩展替换 , 其中 3047 行后的内容对
hello.c 中第 10 行之后的内容。
3047 行之前的内容是头文件 stdio.h unistd.h stdlib.h
等内容。其中包含了很多可能用到的函数 :
2.3.1 fprintf 函数
而且通过 extern 关键词我们更加可以确定,这是引用声明别的文件的函数。
以及库在 Linux 系统的具体位置等 : 计算机系统基础课程报告
2.3.2 库函数声明
- 7 - 计算机系统基础课程报告
2.3.3 hello.i
2.4 本章小结
本章简单介绍了 c 语言在编译前的预处理过程 , 简单介绍了预处理过程的概
念和作用 , 对预处理过程进行了演示 , 并说明了预处理的结果还有解析预处理的
过程。
- 8 - 计算机系统基础课程报告
第 3 章 编译
3.1 编译的概念与作用
概念 :
利用编译程序把由高级语言源程序转化为汇编语言程序的过程。
过程 :
(1) 词法分析 : 将源代码程序输入扫描器 , 将源代码的字符序列分割成一系列
记号。
(2) 语法分析 : 基于词法分析得到的一系列记号 , 生成语法树。
(3) 语义检查 : 由语义分析器完成 , 指示判断是否合法 , 并不判断对错。
(4) 中间代码生成 : 中间代码 ( 语言 ) 使得编译器分为前端和后端 , 前端产生
与机器 ( 或环境 ) 无关的中间代码 , 编译器的后端将中间代码转换为目标机器代
码。目的 : 一个前端对多个后端 , 适应不同的平台。
(5) 代码优化 : 代码生成器依赖于目标机器 , 依赖目标机器的不同字长 , 寄存
, 数据类型等。
(6) 目标代码生成 : 目标代码优化器选择合适的寻址方式 , 左移右移代替删除 ,
删除多于指令。
作用 :
将高级语言翻译为机器能理解的汇编语言。值得注意的是 , 这里的编译是指
.i .s 即预处理后的文件到生成汇编语言程序。
3.2 在 Ubuntu 下编译的命令
3.2 编译命令
3.3 Hello 的编译结果解析
- 9 - 计算机系统基础课程报告
3.3.1 数据
1. int i:
i 是一个局部变量 . 局部变量一般存储在寄存器或者栈中 . i 的赋值
语句可以看出 i 存放的位置是 -4(%rbp):
3.3.1.1 int i
2. 字符串 :
程 序 中 有 两 个 字 符 串 : “ 用 法 : Hello 学 号 姓 名 秒 数 ! \n”
“Hello %s %s\n”.
由分析已知都存在只读数据段 (.rodata ) .
3.3.1.2 字符串
3. 数组
程序中有一个数组 argv[] argv[1] argv[2] argv[3] 作为 for 循环中
printf 的参数。
由该循环的汇编语句可以知道 argv 的首地址是 -32(%rbp)
3.3.1.3 数组
4. 其他数据为立即数
- 10 - 计算机系统基础课程报告
3.3.2 赋值
1. int i = 0:
3.3.2 int i=0
此语句的意思是将一个双字的立即数 0 传送到 %rbp – 4 所指向的内存单元 .
3.3.3 算术操作
程序中出现的算数操作为 :
3.3.3.1 i++
.s 文件中被编译为 :
3.3.3.2 i++
该语句的意思是 : %rbp – 4 所指向的内存中的数与立即数 1 相加 , 并将
所得的和传送到 %rbp – 4 所指向的内存地址 .
3.3.4 类型转换
atoi 即为一个类型转换的函数,将一个字符串转换成 int 类型的数据。
3.3.4.1 atoi(argv[3])
编译成为:
3.3.4.2 atoi(argv[3])
3.3.5 关系操作与控制转移
程序中的控制转移通过比较和跳转实现 .
3.3.5.1 argc!=4
被编译为 :
- 11 - 计算机系统基础课程报告
3.3.5.2 argc!=4
代表的意思是用 %rbp – 20 所指向的内存中的数与立即数 4 相减 , 并设置条
件码 , 若条件码 ZF 0, 则跳转至 .L2, 若不为 0, 则不跳转 .
3.3.5.3 i<9
上式中的 i < 9 被编译为 :
3.3.5.4 i<9
代表的意思是用 %rbp – 4 所指向的内存中的数与立即数 8 相减 , 并设置条
件码 , 若条件码 SF ^ OF 1, 则跳转至 .L4 若为 0, 则不跳转 .
3.3.6 数组 / 指针 / 结构操作
循环体
3.3.6.1 数组操作
中的数组元素的操作被编译成:
3.3.6.2 数组操作
程序对 argv[1] argv[2] argv[3] 的寻址被编译为基址 + 偏移的寻址方式 .
由前面的分析得知 : argv 的首地址存放在 -32(%rbp) 的位置 , 由于每一个数
据元素的字长为 8 个字节 , 所以首地址 +8 得到 argv[1] 的地址 , 首地址 +16
得到 argv[2] 的地址。
3.3.6.3 数组操作
首地址 +24 得到 argv[3] 的地址。
- 12 - 计算机系统基础课程报告
3.3.7 函数操作
函数操作调用包括以下几个步骤 :
(1) 参数传送 : x86-64 , 大部分过程间的数据传送是通过寄存器
实现的 , 通过寄存器最多可以传递 6 个整型 ( 例如整数和指针 ) 参数 .
6 个的部分就要通过栈来传递 .
(2) 返回地址入栈 : 将当前代码区调用指令的下一条指令地址压入栈
, 供函数返回时继续执行 .
(3) 代码区跳转 : 处理器从当前代码区跳转到被调用函数的入口处 .
(4) 栈帧调整 , 包括 :
1) 保存当前栈帧状态值,已备后面恢复本栈帧时使用 (rbp 入栈 )
2) 将当前栈帧切换到新栈帧 ( rsp 值装入 rbp ,更新栈帧底部 )
3) 给新栈帧分配空间 ( ESP 减去所需空间的大小,抬高栈顶 )
函数返回的步骤如下 :
(1) 保存返回值,通常将函数的返回值保存在寄存器 rax .
(2) 弹出当前帧,恢复上一个栈帧 , 具体包括:
1) 在堆栈平衡的基础上,给 rsp 加上栈帧的大小,降低栈顶,回
收当前栈帧的空间。
2) 将当前栈帧底部保存的前栈帧 rbp 值弹入 rbp 寄存器,恢复出
上一个栈帧。
3) 将函数返回地址弹给 rip 寄存器 .
(3) 跳转:按照函数返回地址跳回母函数中继续执行 .
程序中主要调用以下几个函数 :
1. main 函数 :
被系统启动函数 __libc_start_main 调用 .
3.3.7.1 _libc_start_main
可以看出第一个参数被存储在 edi , 而第二个参数被存储在 rsi
2. printf 函数 :
第一次调用 :
- 13 - 计算机系统基础课程报告
3.3.7.2 printf
被编译为 :
3.3.7.3 printf
第一个参数被压入了 rdi , 也就是字符串的首地址 .
第二次调用 :
3.3.7.4 printf
被编译为 :
3.3.7.5 printf
由于第二次 printf 中除了有字符串常量 , 还包括 argv[1] argv[2]
两个参数故依次将其存储在 %rsi %rdx , 再将字符串首地址传到 rdi
, 最后再 call printf
3. exit 函数 :
被编译为 :
3.3.7.6 exit(1)
edi 中传递参数 1, call exit
4. sleep 函数 :
被编译为 :
- 14 - 计算机系统基础课程报告
3.3.7.7 sleep()
argv[3] 作为 edi 传递给 atoi 作参数 , 再将其返回值传递给 edi ,执
sleep 函数。
5. getchar 函数 :
被编译为 :
3.3.7.8 getchar()
本身不需要参数 , 直接 call getchar 即可
3.4 本章小结
本章通过编译器对上一章生成的 .i 文件编译成 .s 文件 , 并对数据类型 , 赋值 , 类型
转换 , 算数操作 , 关系操作 , 位移控制 , 函数进行了分析 , 将编译过程进行了全面的分析 ,
并对一些常用的汇编指令进行了阐述。
编译器由 .i 文件处理成了 .s 文件的汇编代码 , 现在我们对汇编代码也有了初步
的了解可以基本看懂汇编语言并进行分析。
- 15 - 计算机系统基础课程报告
- 16 -
第 4 章 汇编
4.1 汇编的概念与作用
汇编器 (as) hello.s 翻译成机器语言指令 , 把这些指令打包成一种叫做可重定位
目标程序的格式 , 并将结果保存在目标文件 hello.o .
作用 : 将汇编代码转变为机器指令 , 生成目标文件 .
4.2 在 Ubuntu 下汇编的命令
4.2 汇编命令
4.3 可重定位目标 elf 格式
hello.o 实际上是以一种可重定位链接格式组织的,我们称之为 ELF
ELF 文件格式是用如图所示的方法设计的 :
名称
内容
.text
程序的机器代码
.rodata
只读数据,例如跳转表
.data
已初始化的全局和静态 C 变量
.bss
未初始化的全局和静态 C 变量,以及所有被初始化为 0 的全
局或静态变量
.symtab
一个符号表,存放一些在程序中定义和引用的函数和全局变
量的信息
.rel.text
.text 节中需要被修正的信息
.rel.data
被模块引用或定义的所有全局变量的重定位信息
.debug
调试符号表
.line
原始 C 源程序中的行号和 .text 节中机器指令之间的映射
.strtab
一个字符串表(包括 .symtab .debug 节中的符号表)
1. ELF :
ELF 头以一个 16 字节的序列开始,这个序列描述了生成该文件的系统的
字的大小和字节顺序、包含帮助链接器语法分析和解释目标文件的信息,其
中包括 ELF 头的大小、目标文件的类型、机器类型、节头部表的文件偏移, 计算机系统基础课程报告
节头部表中条目的大小和数量等。
4.3.1 ELF
2. 节头表
记录每个节的名称 , 类型 , 属性 ( 读写权限 ), ELF 文件中所占的长度对齐方
式和偏移量。
- 17 - 计算机系统基础课程报告
4.3.2 节头表
3. 重定位节
重定位条目告诉链接器在目标文件合并成可执行文件时如何修改这个引用 .
offset 是需要被修改的引用的节的偏移 , 符号标识被修改引用应该指向的符号 . type
告知连接器如何修改新的引用 , 加数是一个有符号常数 , 一些类型的重定位要用
它对被修改引用的值做偏移调整。
- 18 - 计算机系统基础课程报告
4.3.3 重定位节
重定位类型分为两种 :
1) R_X86_64_PC32: 重定位一个使用 32 PC 相对地址的引用。
2) R_X86_64_32: 重定位一个使用 32 PC 绝对地址的引用。直接使用在
指令中编码的 32 位值作为有效地址。
ELF 重定位条目为 :
4.3.4 ELF 重定位条目
4. 符号表
它存放程序中定义和引用的函数和全局变量的信息 , 符号表不包括局部变
量的条目 .
- 19 - 计算机系统基础课程报告
4.3.6 符号表
4.4 Hello.o 的结果解析
1. hello.s 相比较除了头尾部不同外 , 反汇编代码与 hello.s 差别不大。
2.hello.s 使用十进制 , 反汇编代码中使用 16 进制 .
3.hello.s 中使用到的 .L2, .L4, .L3 标识符都用了地址代替 , 从而在分支转移部分
全部都是 jmp 到相对 main 函数起始地址的偏移表示的跳转的地址 .
4.hello.s 中对函数调用均用的是函数名 ; 反汇编中函数调用的目标地址是当前
的下一条指令 . call 后的地址为全 0, 在重定位节中有对应的重定位条目 ,
接之后确定地址 .
5.hello.s 中使用段名称和 %rip 访问 , 反汇编代码中使用 0x0(%rip) 访问 . 机器码
中待访问的全局变量地址为全 0, 重定位节中有对应的重定位条目 , 链接之后
确定地址 .
- 20 - 计算机系统基础课程报告
4.3.7
4.5 本章小结
本章简单描述了从 hello.s hello.o 的过程,着重介绍了重定位,同时通过比
较反汇编代码和 hello.s 的不同,更加明确的阐释了汇编的作用,以及在汇编过程
中对代码所进行的操作 .
- 21 - 计算机系统基础课程报告
- 22 -
第 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.2 链接命令
5.3 可执行目标文件 hello 的格式
1. ELF 计算机系统基础课程报告
5.3.1 ELF
2. 节头表
- 23 - 计算机系统基础课程报告
5.3.2.1 节头表 1
- 24 - 计算机系统基础课程报告
5.3.2.2 节头表 2
节头表内容如图 , 包含了各个节的大小以及各个节的位置 .
其中 :
.text 节是保存了程序代码指令的代码节 . 由于 .text 节保存了程序代码,因
此节的类型为 SHT_PROGBITS.
.rodata 保存只读数据 . 类型 SHT_PROGBITS.
.plt 过程链接表,包含动态链接器调用从共享库导入的函数所必须的相关
代码 . 存在于 text 段中,类型 SHT_PROGBITS.
.bss 节保存未初始化全局数据,是 data 的一部分 . 程序加载时数据被初始化
0 ,在程序执行期间可以赋值,未保存实际数据,类型 SHT_NOBITS.
.got 节保存全局偏移表 . 它和 .plt 节一起提供了对导入的共享库函数访问的
入口 . 由动态链接器在运行时进行修改 . 类型 SHT_PROGBITS
.dynsym 节保存共享库导入的动态符号信息,该节在 text 段中,类型
SHT_DYNSYM.
.dynstr 保存动态符号字符串表,存放一系列字符串,代表了符号的名称,
以空字符作为终止符 .
.rel 节保存重定位信息,类型 SHT_REL.
.hash 节,也称为 .gnu.hash, 保存一个查找符号散列表 .
.symtab 节,保存了 ElfN_Sym 类型的符号信息,类型 SHT_SYMTAB.
strtab 节,保存符号字符串表,表中内容被 .symtab ElfN_Sym 结构中的
st_name 条目引用。类型 SHT_SYMTAB.
.shstrtab 节,保存节头字符串表,以空字符终止的字符串集合,保存了每
个节节名,如 .text .data 等。有个 e_shsrndx ELF 文件头条目会指向 .shstrtab
节, e_shstrndx 中保存了 .shstrtab 的偏移量 . 这节的类型是 SHT_SYMTAB.
.ctors .dtors 节,前者构造器,后者析构器,指向构造函数和析构函数的
函数指针,构造函数是在 main 函数执行前需要执行的代码,析构是 main 函数
之后需要执行的代码 .
- 25 - 计算机系统基础课程报告
3. 程序头表
5.3.3 程序头表
程序包含八个 type:
1) PTDR: 指定程序头表在文件及程序内存映像中的位置和大小 .
2) INTERP: 指定要作为解释程序调用的以空字符结尾的路径名的位置和大小 .
对于动态可执行文件,必须设置此类型。
3) LOAD: 指定可装入段,通过 p_filesz p_memsz 进行描述 . 文件中的字节会
映射到内存段的起始位置 .
4) DYNAMIC: 指定动态链接信息 .
5) NOTE: 指定辅助信息的位置和大小 .
6) GNU_STACK: 权限标志,标志栈是否是可执行的 .
7) GNU_RELRO: 指定在重定位结束之后那些内存区域是需要设置只读 .
5.4 hello 的虚拟地址空间
- 26 - 计算机系统基础课程报告
5.4.1 程序头表
(1) .PDHR: 起始位置为 0x400040, 大小为 0x2a0
- 27 - 计算机系统基础课程报告
5.4.1 PDHR
(2) .INTERP: 起始位置为 0x4002e0, 大小为 0x1c
5.4.3 .INTERP
5.5 链接的重定位过程分析
1. hello 中增加了许多节和被调用的函数,例如:
5.5.1 .init
- 28 - 计算机系统基础课程报告
2. 引用了 rodata 中的数据 , 而在 hello.o printf 参数的字符串用全 0 代替 .
3. hello 中使用的都是确定的地址 , 这是因为链接后全局变量的地址能够确定。
5.5.2 printf
4. hello.o 中的地址从 0 开始 , hello main 的地址不是 0 开始
hello.o:
5.5.3 hello.o
hello:
5.5.4 hello
5. hello 中有库函数的代码,而 hello.o 中没有
- 29 - 计算机系统基础课程报告
- 30 -
5.5.5 hello 库函数
5.6 hello 的执行流程
开始执行: _start _libc_start_main
执行 main _main _printf _exit _sleep _getchar
退出: exit
程序名称
程序地址
ld-2.31.so!_dl_start
0x00007f807a1180b3
ld-2.31.so!_dl_init
0x00007f5a70281c10
Hello!_start
0x000000000040111e
libc-2.31.so!__libc_start_main
0x00007fd1c3e7a550
Hello!main
0x00000000004010d0
Hello!printf@plt
0x00000000004010a0
hello!atoi@plt
0x00000000004010c0
Hello!sleep@plt
0x00000000004010e0 计算机系统基础课程报告
- 31 -
hello!getchar@plt
0x00000000004010b0
libc-2.31.so!exit
0x00007d1c3e6f460
5.7 Hello 的动态链接分析
Hello 的动态链接分析
程序调用一个由共享库定义的函数时,编译器没有办法预测这个函数的运行
时地址,因为定义它的共享模块在运行时可以加载到任意位置。 GNU 编译系统使
用延迟绑定的技术解决这个问题,将过程地址的延迟绑定推迟到第一次调用该过
程时。
延迟绑定要用到全局偏移量表( GOT )和过程链接表(
PLT )两个数据结构。
如果一个目标模块调用定义在共享库中的任何函数,那么它就有自己的 GOT
PLT
PLT PLT 是一个数组,其中每个条目是 16 字节代码。 PLT[0] 是一个特殊条
目,跳转到动态链接器中。每个条目都负责调用一个具体的函数。 PLT[1] 调用系统
启动函数
__libc_start_main )。从 PLT[2] 开始的条目调用用户代码调用的函数。
GOT GOT 是一个数组,其中每个条目是 8 字节地址。和 PLT 联合使用时,
GOT[0] GOT[1] 包含动态链接器在解析函数地址时会使用的信息。 GOT[2] 是动态
链接器在 ld-linux.so 模块中的入口点。其余的每个条目对应于一个被调用的函数,
其地址需要在运行时被解析。
分析 :
5.6.1 .got
节头表中找到 GOT 的起始地址为 601000, 偏移量为 0x1000
调用 _dl_start 之前有 :
5.6.2 _dl_start
调用之后有 : 计算机系统基础课程报告
5.6.3 _dl_start
GOT[2] 是动态链接器在 ld-linux.so 模块中的入口点 , 共享库模块 :
5.6.4 共享库模块
第一次调用 puts 前的跳转地址 :
5.6.5 puts 前的跳转地址
调用 puts 之后的跳转地址 :
- 32 - 计算机系统基础课程报告
5.6.6 puts 后的跳转地址
说明调用 printf 前未链接到动态库 , 调用后链接到了动态库 , 说明是动态
链接的 , 可以跳到确切的地址 .
5.8 本章小结
hello.o 经过一系列链接变成可执行文件 hello, 而本章中对于链接过程中的重
定位进行了详细的介绍 , 也展现了重定位在链接过程中的重要性。
- 33 - 计算机系统基础课程报告
第 6 章 hello 进程管理
6.1 进程的概念与作用
进程是一个执行中程序的实例。是系统进行资源分配和调度的基本单位,是
操作系统结构的基础。
作用:进程的概念为我们提供了两个关键的抽象:
(1) 一个独立的逻辑控制流:他提供一个假象,好像我们的程序在独占
地使用处理器。
(2) 一个私有的地址空间:他提供一个假象,好像我们的程序在独占地
使用内存系统。
处理器好像是无间断地一条接一条地执行我们程序中的指令,我们程序中的
代码和数据好像是系统内存中唯一的对象。
6.2 简述壳 Shell-bash 的作用与处理流程
Shell 俗称壳,是一个交互型的应用级程序,是指“为使用者提供操作界面”
的软件(命令解析器)。它接收用户命令,然后调用相应的应用程序。
1. 功能:命令解释;可执行文件都可以作为 Shell 命令来执行。
2. 处理流程:
1 )当用户提交了一个命令后, Shell 首先判断它是否为内置命令,如果是就通
Shell 内部的解释器将其解释为系统功能调用并转交给内核执行。
2 )若是外部命令或应用程序就试图在硬盘中查找该命令并将其调入内存,再
将其解释为系统功能调用并转交给内核执行。
6.3 Hello 的 fork 进程创建过程
shell 中输入 ./hello shell 判断不是内置命令,通过 fork 函数创建一个新的
运行的子进程来加载并运行可执行文件 hello 。新创建的子进程几乎但不完全与父
进程相同,子进程得到与父进程用户级虚拟地址空间相同的(但是独立的)一份
副本,包括代码和数据段、堆、用户库以及用户栈。子进程还可以获得与父进程
任何打开文件描述符相同的副本,这就意味着当父进程调用 fork 时,子进程可以
读写父进程中打开的任何文件,父进程和新创建的子进程之间最大的区别在于它
们有不同的 PID fork 被调用一次,返回两次。在父进程中 fork 返回子进程的 pid
- 34 - 计算机系统基础课程报告
- 35 -
在子进程中 fork 返回 0. 父进程与子进程是并发运行的独立进程。
6.4 Hello 的 execve 过程
execve 函数在新创建的子进程的上下文中加载并运行 hello 程序,且带参数列
argv 和环境变量列表 envp 。只有发生错误时 execve 才会返回到调用程序。
execve
调用一次且从不返回。在 execve 加载 hello 之后调用启动代码,启动代码设置栈并
将控制传递给新程序的主函数。
main 开始执行时,用户栈组织结构如图:
6.4.1 用户栈结构
6.5 Hello 的进程执行
逻辑控制流:一系列程序计数器 PC 的值的序列叫做逻辑控制流,进程是轮流
使用处理器的。
调度:在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个 计算机系统基础课程报告
先前被抢占了的进程的决定。
时间片:一个进程执行它的控制流的一部分的每一时间段叫做时间片。
用户模式和内核模式:处理器通常使用一个寄存器提供两种模式的区分,该寄
存器描述了进程当前享有的特权,当没有设置模式位时,进程就处于用户模式中,
用户模式的进程不允许执行特权指令,也不允许直接引用地址空间中内核区内的
代码和数据;设置模式位时,进程处于内核模式,该进程可以执行指令集中的任
何命令,并且可以访问系统中的任何内存位置。(总的来说就是权限的问题)
上下文信息:上下文就是内核重新启动一个被抢占的进程所需要的状态,它由
通用寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内
核数据结构等对象的值构成。
通过上下文切换,控制流从一个进程传递到另外一个进程。
执行 sleep 时:
hello
sleep
倒计时结束
sleep 返回
6.5.1 sleep 进程切换
hello 进程调用 sleep 之后陷入内核模式,内核处理休眠请求主动释放当前
进程,并将 hello 进程从运行队列中移出加入等待队列,定时器开始计时,内核进
行上下文切换将当前进程的控制权交给其他进程,当定时器到时时发送一个中断
信号,此时进入内核状态执行中断处理,将 hello 进程从等待队列中移出重新加入
到运行队列,成为就绪状态, hello 进程就可以继续进行自己的控制逻辑流了。
调用 getchar
内核可以执行上下文切换,将控制转移到其他进程。 getchar() 的数据传输结束
之后,引发一个中断信号,控制回到 hello 进程中。
- 36 - 计算机系统基础课程报告
hello
read
磁盘中断
read 返回
6.5.2 getchar 进程切换
6.6 hello 的异常与信号处理
异常的类别:
6.6.1 异常类别
信号:
- 37 - 计算机系统基础课程报告
6.6.3 信号类别
信号的处理:
当内核把进程 p 从内核模式切换到用户模式时,它会检查进程 p 的未被阻塞的
待处理信号的集合。如果集合非空,内核强制 p 接收信号 k 。收到这个信号会触发
进程采取某种行为,一旦完成行为,控制就传递回 p 的逻辑控制流中的下一条指
令,每个信号类型都有一种默认行为,可以通过设置 signal 函数改变和信号 signum
相关联的行为。
1. 正常退出:
- 38 - 计算机系统基础课程报告
6.6.4 正常退出
程序结束后,被正常回收
2. 随便乱按:
6.6.5 随便乱按
乱按会把输入的数留在输入缓冲区,作为接下来的命令行输入。同时程序可以
被正常回收。
3.ctrl+c
- 39 - 计算机系统基础课程报告
6.6.6. ctrl+c
这个操作向进程发送一个 SIGINT 信号,让进程直接结束,信号处理程序会回
收子进程。可以看到 hello 进程已被终止,后台没有 hello 进程挂起。
4.crtl+z
6.6.7. ctrl+z
这个操作可以向进程发送一个 SIGSTP 信号,让进程暂时挂起,输入 ps 可以
看到 hello 进程还没有被终止。此时我们可以用 kill 命令让该进程被终止。再次使
ps ,可以看到 hello 进程已经被终止。
也可以使用 fg 让后台挂起程序继续运行:
- 40 - 计算机系统基础课程报告
6.6.8 fg
可以看到在挂起前, hello 进程输出了 4 次, fg 后输出了 5 次,说明确实是继
续运行的。
5. jobs
6.6.9 jobs
可以查看当前的关键命令。
6.pstree
- 41 - 计算机系统基础课程报告
- 42 - 计算机系统基础课程报告
- 43 - 计算机系统基础课程报告
6.6.8 pstree
可以用进程树的方法把各个进程用树状图的方式连接起来。
6.7 本章小结
本章中主要介绍了 hello 在执行过程中的异常信号处理,以及 shell 的处理流程,
同时通过堆异常控制流的介绍,我们能够更加深入了解了系统对于异常的处理机
制。而 hello 也完成它的作用即 hello 的进程被执行。
结论
1. 程序员通过 IDE 或者其他的文本编辑器创建了 hello.c
2. 文件 hello.c 被预处理器经过预处理调用外部库展开为 hello.i
3. 文件 hello.i 被编译器编译成 hello.s ,变为一个汇编语言程序
4. 汇编器将 hello.s 汇编成机器码,把这些机器指令打包为 hello.o ,这个文件
叫做可重定位的目标文件。
5. 链接器将 hello.o 与动态链接库等链接成为可执行文件 hello
- 44 - 计算机系统基础课程报告
6. 用户在 shell 中输入 ./hello 1180300918 邹建
7. 在进程中 shell 调用 fork 函数创建子进程。
8. 在这个子进程中, shell 用函数 execve 来调用启动器加载器。
9. 程序 hello 在一个时间片里执行自己的控制流,同时访问内存以及申请动态
内存,并且可以接受 ctrl+z ctrl+c 的信号
10. hello 调用 sleep getchar exit 等系统函数后进程结束,被父进程 shell
收,内核删除为子进程创建的数据结构。
至此 hello 的一生结束了。
可以说程序员熟悉一个新的环境,最先尝试的一定是不同意义下的 hello 语句,
而计算机系统也不例外。只是没想到这次的 hello 如此凶险 ( 苦笑 ) 。通过亲手操作
并且一步一步阅读、理解每一个阶段,原本十分简单的 hello 语句变得复杂的不行,
我也第一次成功地调戏,或者阻碍了程序的运行,也是第一次如此详尽地了解了
hello 的一生,也最终帮助 hello 程序突破层层阻碍,从物理内存一步一步到达了我
的输出设备屏幕,和我的学号还有名字相见,可以说是意料之中的别致惊喜了。
就像前面所说的,尽管 hello 程序非常简单,但是为了让它实现运行,系统的
每个主要组成部分都需要协调工作。通过这次大作业,我了解了在系统上执行 hello
程序时,系统发生了什么以及为什么会这样。我也逐步了解到了在程序运行的每
一步中,计算机各个部分的功能,同时了解了在面对各种异常时的处理机制,这
将有助于我对计算机系统的深入理解。
- 45 - 计算机系统基础课程报告
- 46 -
附件
1. hello.i
hello.c 预处理之后的文件
2. hello.s
hello.i 编译成汇编语言后的文件
3. hello.o
hello.s 生成的二进制文件
4. hello
hello 的可执行文件
5. hello_1.txt
objdump hello.o 反汇编的结果
6. hello_2.txt
objdump hello 反汇编的结果
7. hello_3.txt
hello readelf 生成的 elf
8. hello_4.txt
hello.o readelf 生成的 elf 计算机系统基础课程报告
- 47 -
参考文献
[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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值