计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 网络空间安全
学 号 2022111167
班 级 2203901
学 生 毕浩翔
指 导 教 师 史先俊
计算机科学与技术学院
2024年5月
本文通过对Hello.c文件的预处理,编译,汇编,链接的单步执行与对反汇编代码,中间文件,I/O结果的查看,跟踪一个典型c语言程序的“一生”,分析该过程中软件与硬件的协同配合。
关键词:预处理;汇编;链接;反汇编;I/O
目 录
6.2 简述壳Shell-bash的作用与处理流程... - 25 -
6.3 Hello的fork进程创建过程... - 26 -
7.2 Intel逻辑地址到线性地址的变换-段式管理... - 30 -
7.3 Hello的线性地址到物理地址的变换-页式管理... - 30 -
7.4 TLB与四级页表支持下的VA到PA的变换... - 30 -
7.5 三级Cache支持下的物理内存访问... - 31 -
7.6 hello进程fork时的内存映射... - 31 -
7.7 hello进程execve时的内存映射... - 31 -
第1章 概述
1.1 Hello简介
根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。
P2P: hello.c-(cpp)->hello.i-(ccl)->hello.s-(as)->hello.o-(ld)->hello.out
020: 程序未运行时没有分配内存空间(0),在shell上输入./hello后OS为程序分配虚拟地址及逻辑控制流,让程序拥有独立的逻辑控制流和内存系统,在程序结束运行后再回收分配到的空间并由其父进程回收子进程,回到一无所有的状态(0)
1.2 环境与工具
环境:Ubuntu 64 位
工具:gedit/gdb
1.3 中间结果
hello.c 源文件
hello.i 预编译结果
hello.s 编译结果
hello.o 汇编结果
hello.exe 链接结果
hello_asm hello.exe的反汇编文件
1.4 本章小结
本章概括性介绍了本文的运行环境,中间结果及整体思路,便于读者复现。
第2章 预处理
2.1 预处理的概念与作用
概念
预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。结果就得到了另一个C程序,通常是以.i作为文件扩展名。
作用
预处理阶段根据已放置在文件中的预处理指令来修改源文件的内容。比如#include就是一个预处理指令,它把头文件的内容添加到.cpp文件中。这种预处理的机制提高了源文件的灵活性,能适应不同的计算机和操作系统;而且通过预处理指令,可以使用已经封装好的库函数,极大地提高了编程效率。
2.2在Ubuntu下预处理的命令
2.3 Hello的预处理结果解析
我发现hello.i比hello.c多出了许多外部代码,这些外部代码应当是hello.c中include进来的库文件
2.4 本章小结
Hello.c到hello.i经历了预处理的过程,该过程处理了hello.c中的库引用(include)与宏定义(define),将引用的库代码拼接到源代码文件中。
第3章 编译
3.1 编译的概念与作用
注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序
概念
编译器ccl将文件hello.i中的c语言代码编译为汇编语言存入hello.s
作用
在编译阶段中,gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。在编译阶段,编译器还能起到优化的作用,优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关,而且同机器的硬件环境也有很大的关系。
3.2 在Ubuntu下编译的命令
3.3 Hello的编译结果解析
3.3.1 汇编头部
.file 源文件名
.text 源代码
.rodata 只读数据
.string 对应hello.c文件中”用法: Hello 学号 姓名 手机号 秒数!\n”
.globl 全局变量main
.type 全局变量main是一个函数
3.3.2main函数
逻辑结构:
- 分支跳转(je .L2)对应源文件if语句,24行cmpl对应if语句的判断条件
- 26-30行对应源文件调用printf函数打印字符串并退出exit(0)
- .L3和.L4对应源文件for循环,.L4是循环体,.L3是循环终止条件
- .L4中执行打印字符串操作并通过寄存器传参调用sleep函数
3.4 本章小结
本章通过分析hello.c的汇编代码与源代码的语句对应关系剖析了汇编语言的头部,传参,函数调用,分支结构。
第4章 汇编
4.1 汇编的概念与作用
汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件hello.o中。hello.o是一个二进制文件,它包含的17个字节是函数main的指令编码。
注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。
4.2 在Ubuntu下汇编的命令
4.3 可重定位目标elf格式
分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。
4.3.1 elf头部
可以看到许多程序有关信息,如该文件使用小端法编码的等等。
-
-
- elf各section
-
上图展示了elf的整体section结构,包括各section的大小,名称等
-
-
- 重定向(rela.text)
-
该section中包含了需要被重定位的变量的偏置值,在链接时会根据偏置值将局部变量重定向到对应已声明变量,将已声明变量对应到对应内存地址
4.4 Hello.o的结果解析
(以下格式自行编排,编辑时删除)
objdump -d -r hello.o 分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。
说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。
- 机器语言指令部分与汇编基本相同,但在每行指令的开头都多了一串十六进制数,表示该指令的内存地址;汇编语言每行指令没有分配地址
- 控制结构表示不同,汇编语言中jmp指令跟的.L1.LL3等在机器语言中被使用对应指令首地址表示,因此指令中的.L1.LL3也不存在了
- 函数调用表示不同,call后面跟的函数名变成了对应地址
4.5 本章小结
本章通过分析hello.o的elf结构和反汇编代码剖析了一个可执行程序的基本组成结构,对比分析了机器语言与汇编代码的异同。
第5章 链接
5.1 链接的概念与作用
链接(linking)是将各种代码和数据部分收集并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。
链接由链接器程序自动执行。链接器在软件开发中扮演着一个关键的角色,因为它们使得分离编译成为可能。我们不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其他文件。
注意:这儿的链接是指从 hello.o 到hello生成过程。
5.2 在Ubuntu下链接的命令
使用ld的链接命令,应截图,展示汇编过程! 注意不只连接hello.o文件
5.3 可执行目标文件hello的格式
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。
5.4 hello的虚拟地址空间
使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。
通过edb调试可以看出该程序地址从0x401000开始,我们可以通过elf的每个section的地址在Data Dump中找到相应数据。
5.5 链接的重定位过程分析
(以下格式自行编排,编辑时删除)
objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。
结合hello.o的重定位项目,分析hello中对其怎么重定位的。
可以看出,重定向后的hello.out比hello.o的反汇编左侧多了一列虚拟地址序号VPN,因为经过重定向每条指令的VA-PA映射关系已经确定。
同时经过链接该文件比hello.o多了许多库函数的代码如exit()等,因为许多调用的外部函数以可重定向文件的格式存在,在链接这步加入了这些文件。
同时在链接过程中,各个可重定向文件的相同elf section会被聚合在一起形成聚合结,例如所有的.rodata,.text,.bss,.data等等。
5.6 hello的执行流程
使用gdb/edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程(主要函数)。请列出其调用与跳转的各个子程序名或程序地址。
在_start打断点并单步执行
函数 | 地址 |
main | 0x4011e2 |
init | 0x401000 |
_start | 0x7ffff7fe3290 |
call_init | 0x7ffff7c29e68 |
执行顺序
_start->init->call_init->main
5.7 Hello的动态链接分析
分析hello程序的动态链接项目,通过edb/gdb调试,分析在动态链接前后,这些项目的内容变化。要截图标识说明。
PLT和GOT是与动态链接最相关的两个节,观察他们所在地址段的变化即可反应动态链接的内容变化。
通过readelf找到他们的地址
在动态链接处打断点
发现程序没有正确停下,于是我在_start处与main处分别打断点,观察观察前后两变量变化,如下:
5.8 本章小结
本章通过gdb调试剖析了链接这一过程进行了怎么样的工作,包括elf节的聚合,虚拟地址的映射,库函数代码的引入等等。
第6章 hello进程管理
6.1 进程的概念与作用
进程,是一个执行中程序的实例。
进程提供给应用程序的关键抽象:一个独立的逻辑控制流;一个私有的地址空间。
通过以上两个抽象,进程提供给用户一种假象:就好像我们的程序是系统中当前运行的唯一的程序一样。我们的程序好像是独占地使用处理器和内存。处理器就好像是无间断地一条接着一条地执行我们程序中的指令。最后,我们程序中的代码和数据好像是系统内存中唯一的对象。
6.2 简述壳Shell-bash的作用与处理流程
bash
(Bourne Again Shell)是Linux和许多Unix系统上广泛使用的shell之一。作为用户与操作系统之间的接口,bash
(以及其他shell)允许用户通过命令行与系统进行交互,并执行各种命令和脚本。以下是bash
的作用和处理流程的简要概述:
bash的作用
- 命令解释器:
bash
读取用户输入的命令,解析它们,并传递给相应的程序或函数执行。 - 脚本解释器:
bash
还可以执行存储在文件中的命令序列,这些文件通常被称为shell脚本或bash脚本。 - 自动化:通过脚本,
bash
可以实现自动化任务,如系统维护、文件操作、数据处理等。 - 环境控制:
bash
管理环境变量,这些变量定义了shell和程序的行为。 - 任务调度:结合其他工具(如
cron
),bash
可以用于在特定时间或条件下自动执行任务。 - 交互式使用:用户可以直接在命令行界面与
bash
交互,执行命令和查看输出。
bash的处理流程
- 启动:当用户启动一个新的bash shell(例如,通过打开一个终端窗口或登录到系统)时,bash会读取其初始化文件(如
.bashrc
或.bash_profile
),这些文件定义了shell的环境和行为。 - 读取命令:bash等待用户输入命令。当用户键入一个命令并按下回车键时,bash会读取该命令。
- 解析命令:bash将输入的命令分解成各个部分,如命令名、选项和参数。它还会检查命令是否存在以及用户是否有权限执行该命令。
- 执行命令:bash会查找并执行与命令名对应的程序。这可能是一个内置的bash命令(如
cd
或echo
),一个外部程序(如/bin/ls
),或者一个shell函数。 - 捕获输出:bash会捕获命令的输出(包括标准输出和标准错误输出),并可以选择性地将其显示给用户或重定向到文件。
- 循环等待:执行完命令后,bash会返回到等待状态,继续读取用户输入的下一个命令。这个过程会一直持续下去,直到用户退出shell(通过键入
exit
命令或关闭终端窗口)。
6.3 Hello的fork进程创建过程
我们在shell中输入./hello,OS就会使用我们给的上下文(参数,状态信息等等)fork一个子进程来运行hello
6.4 Hello的execve过程
OS创建了子进程后,就会调用execve在当前上下文加载并运行hello可执行程序
6.5 Hello的进程执行
进程是程序的一个在特定上下文中的实例,进程的许多行为由上下文决定(如hello程序接收五个参数,接收参数不同打印的提示信息也不同),上下文包括程序接受的参数,运行状态等很多与程序运行相关的属性。
进程会在用户态与内核态不断切换,因为CPU采用非阻塞模式,当一个进程阻塞(如执行大规模磁盘读写),CPU会先执行其他进程,这一过程称为调度,由内核管理。进程在用户态下访问权限有限,如果要调用系统函数或磁盘信息等等,需要构造特定的异常进入异常处理子程序,从而进入内核态执行高权限操作,执行完毕再回到内核态。
6.6 hello的异常与信号处理
hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。
6.6.1 Ctrl-C
6.6.2 Crtl-Z
6.6.3 正常运行
6.7本章小结
本章通过运行hello程序剖析了OS与创建进程的交互,并探究了不同信号对进程的影响。
第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址是程序直接使用的地址,一般用段偏移量索引;
线性地址和虚拟地址是虚拟内存空间中的地址,对应磁盘的相应位置;
物理地址是主存中的地址值,可以直接用来访问主存数据。
7.2 Intel逻辑地址到线性地址的变换-段式管理
逻辑地址由段基址和偏移量构成。
首先检查TI字段,决定是使用GDT(TI=0)还是LDT(TI=1),再计算段描述符的地址,最后将逻辑地址中的偏移量与段描述符中的base相加得到线性地址。
7.3 Hello的线性地址到物理地址的变换-页式管理
7.4 TLB与四级页表支持下的VA到PA的变换
7.5 三级Cache支持下的物理内存访问
7.6 hello进程fork时的内存映射
当shell使用fork创建子进程时,内核为新的子进程创建各种数据结构,并分配给子进程一个唯一的PID,为了给它创建虚拟内存空间,内核创建了当前进程的mm_struct、区域结构和页表的原样副本,将两个进程的页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。这样,在新进程里,最开始的时候它的虚拟内存和原进程的虚拟内存映射相同,但当这两个进程中的任意一个进行写操作时,写时复制机制就会创建新页面,这样两个进程的地址空间就在逻辑上私有了。
7.7 hello进程execve时的内存映射
7.8 缺页故障与缺页中断处理
7.9动态存储分配管理
Printf会调用malloc,请简述动态内存管理的基本方法与策略。
动态内存管理分为显式内存管理和隐式内存管理,显式内存管理常用的由malloc,calloc等等;隐式的内存管理是垃圾回收,即找出堆中不可达(不能从堆外部访问)的节点并释放对应内存。
7.10本章小结
本章通过hello这一实例剖析了计算机系统是如何进行内存管理的,捋清了VA->PA->Data的转化流程,回顾了常见的动态内存管理机制。
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:文件
设备管理:unix io接口
Linux将IO设备视为文件的一种,将Input视为读文件,将Output视为写文件
8.2 简述Unix IO接口及其函数
常见函数:
open 打开文件
close 关闭文件
read 将文件读到内存
write 将内存数据写入文件
8.3 printf的实现分析
源代码
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; } |
printf会接收一个格式串并根据格式串决定接收参数类型及个数。
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall等.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
源代码
int getchar(void) { static char buf[BUFSIZ]; static char* bb=buf; static int n=0; if(n==0) { n=read(0,buf,BUFSIZ); bb=buf; } return(--n>=0)?(unsigned char)*bb++:EOF; } |
getchar是stdio.h中的库函数,它的作用是从stdin流中读入一个字。
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
本章通过分析c语言中的文件处理函数剖析了linux系统的I/O处理模式。
结论
- 程序员写出hello.c这一高级程序语言脚本
- hello.c-(cpp)->hello.i-(ccl)->hello.s-(as)->hello.o-(ld)->hello.out
- 在shell上运行./hello
- OS为hello程序fork一个子进程,在子进程中exceve可执行程序hello
- hello在运行时拥有“看起来”私有的地址空间与逻辑控制流
- hello在运行时可能接收各种信号,如键盘发出的crtl-C
- hello结束运行后,OS回收分配给hello的子进程及地址空间
附件
hello.c 源文件
hello.i 预编译结果
hello.s 编译结果
hello.o 汇编结果
hello.exe 链接结果
hello_asm hello.exe的反汇编文件
参考文献
[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] Randal E.Bryant / David O'Hallaron Computer Systems: A Programmer's Perspective, 2016
[8] Linux readelf命令教程:深入理解ELF文件结构(附实例详解和注意事项)-Linux入门自学网 (bashcommandnotfound.cn)