目 录
2.2在Ubuntu下预处理的命令............................................................................. - 5 -
5.3 可执行目标文件hello的格式....................................................................... - 8 -
6.2 简述壳Shell-bash的作用与处理流程........................................................ - 10 -
6.3 Hello的fork进程创建过程........................................................................ - 10 -
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 -
8.2 简述Unix IO接口及其函数.......................................................................... - 13 -
第1章 概述
(0.5分)
1.1 Hello简介
P2P:
用户编写hello.c程序。C预处理器处理得到中间文件hello.i;C编译器将hello.i其处理成汇编语言文件hello.s;再经由汇编器得到可重定位目标文件hello.o;最后通过链接器得到可执行目标文件hello。
O2O
进程角度:fork函数产生子进程,调用execve函数,进行虚拟内存映射,为运行的hello分配时间片等。
内存角度:以页表为中介,实现VA与PA之间的映射,并通过TLB、cache等加速,异常处理对陷阱/故障以及信号等;程序结束时,进程被回收。
1.2 环境与工具
列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开发与调试工具。
- 硬件环境:Intel core 13600k@5.1GHz; 32G RAM; 2T SSD;
- 软件环境:windows11 pro-64bit ;Vmware 17; Ubuntu 23.04LTS-64bit
- 开发工具:Microsoft Visual studio 2022;codeblocks;;vim+gcc; gdb;
1.3 中间结果
hello.i 对预处理指令做初步处理
hello.s 汇编代码文件
hello.o 可重定位目标程序
hello 可执行目标程序
1.4 本章小结
本章介绍了hello.c从源程序到可执行程序的过程,并简要介绍了hello在运行时的内存、进程管理。
第2章 预处理
(0.5分)
2.1 预处理的概念与作用
2.1.1概念
预处理是指编译器在编译开始之前,调用预处理器,根据以字符#开头的命令,修改原始的C程序,生成以.i结尾的文本文件。
作用
- 预处理#define,将替换宏。
- 预处理#include,展开头文件,把头文件合并进当前文件;
- 根据#if以及#endif、#ifdef、#ifndef来判断执行编译的条件。
2.2在Ubuntu下预处理的命令
正在上传…重新上传取消
图1:Ubuntu下预处理的命令
正在上传…重新上传取消 |
图2:预处理结果
结果解析:解引用printf等函数,将头文件中的函数定义写入hell.i中。
2.4 本章小结
本章简述了Ubantu下使用gcc进行预处理过程的指令及结果解析。
第3章 编译
(2分)
3.1 编译的概念与作用
编译是将C语言翻译成汇编语言的过程
编译器做了如下工作:
- C语言语法分析
- 优化后生成相应的汇编代码
3.2 在Ubuntu下编译的命令
正在上传…重新上传取消
图5:编译指令
3.3 Hello的编译结果解析
正在上传…重新上传取消
图4:部分编译结果
-
-
正在上传…重新上传取消
常量:被处理为立即数。
-
图5:常量
-
- 字符串:仍以字符串形式存储。
正在上传…重新上传取消
图6:字符串
-
- 变量:存储在内存中。
正在上传…重新上传取消
图7:变量
-
- 赋值:通过mov指令实现。
正在上传…重新上传取消
图8:赋值
-
- 算术操作:使用add/sub等指令实现。
正在上传…重新上传取消
图9:算术指令
-
- 关系操作:使用cmp指令
正在上传…重新上传取消
图10:关系操作
-
- 函数操作:不同指令间跳转,采用寄存器/内存传递参数。
正在上传…重新上传取消
图11:函数操作
3.4 本章小结
本章简述了hello.i编译为hello.s的过程,并对结果进行了简要分析
第4章 汇编
(2分)
4.1 汇编的概念与作用
汇编是把汇编语言转换成机器语言的过程。
4.2 在Ubuntu下汇编的命令
正在上传…重新上传取消
图12:汇编指令
4.3 可重定位目标elf格式
正在上传…重新上传取消
图13:可重定位目标elf格式(部分)
最开始是ELF头,它由一个16字节的序列开始,剩下的部分包含帮助链接器语法分析和解释目标文件的信息,其中包括大小、类型、机器类型、文件偏移,条目大小和数量等。随后是各节内容。
4.4 Hello.o的结果解析
正在上传…重新上传取消
图14:hello.o反汇编(左)hello.s(右)对比、
基本相同的,仅有各种常量及函数名被重定位标签取代。可以认为hello.o与hello.s是一对一翻译的结果。
4.5 本章小结
本章简述了汇编的过程,并通过对比得出了编译前后文件内容的差别。
第5章 链接
(1分)
5.1 链接的概念与作用
概念:指将各种代码和数据片段收集并组合称为一个单一文件的过程,这个文 件可以被加载到内存并执行。
正在上传…重新上传取消
作用:将不同功能的片段组合在一起,形成一个能执行完整功能的程序。
5.2 在Ubuntu下链接的命令
图15:在Ubuntu下链接的命令
5.3 可执行目标文件hello的格式
正在上传…重新上传取消
图16:hello的ELF头
可见Hello为可执行文件,由14节变成27节
正在上传…重新上传取消
图17:sectionheaders
正在上传…重新上传取消
图18:Symbol table
5.4 hello的虚拟地址空间
正在上传…重新上传取消
图19:0x4010f0附近内存
发现节头表中指向的0x4010f0处为程序起始地址
5.5 链接的重定位过程分析
正在上传…重新上传取消
图20:hello反链接(左)与hello.o(右)对比
发现:
- hello比hello.o多了许多文件节,如.init节和.plt节;
- hello文件中的地址是虚拟地址,而hello.o节中的是相对偏移地址;
- hello中增加了许多外部链接的共享库函数,如puts@plt,printf@plt等;
- hello中跳转和函数调用的地址是虚拟地址。
重定位过程:
合并相同的节,确定新节中所有定义符号在虚拟地址空间中的地址,对引用符号进行重定位(确定地址),修改.text节和.data节中对每个符号的引用(地址),在.rel_data和.rel_text节中保存重定位信息。
5.6 hello的执行流程
(以下格式自行编排,编辑时删除)
1.开始执行:start、libc_start_main
2.执行main:main、printf、exit、sleep、_getchar
3.退出:exit
程序名及地址:
_start 0x4010f0
_libc_start_main 0x2f12271d
main 0x401125
_printf 0x4010a0
_exit 0x4010d0
_sleep 0x4010e0
_getchar 0x4010b0
5.7 Hello的动态链接分析
动态链接采用延迟加载,在调用函数时才进行符号的映射。使用偏移量表got+过程链接表plt实现函数的动态链接。got中存放函数目标地址,为每个全局函数创建一个副本函数,并将对函数的调用转换成对副本函数调用。.got全局偏移表 .plt程序链接表
正在上传…重新上传取消
图21:执行dl_init前的.got(0x403ff0)及.plt(0x40400)
正在上传…重新上传取消
图22:执行dl_init后的.got(0x403ff0)及.plt(0x40400)
发现执行dl_init后,发生了内存变化。
5.8 本章小结
本章简要介绍了链接的概念及作用,结合实验中的hello可执行程序,阐述了hello的重定位链接和动态链接过程。
第6章 hello进程管理
(1分)
6.1 进程的概念与作用
进程:正在运行的程序的实例。
作用
进程为用户提供了以下假象:程序独占地使用处理器和内存,处理器无间断地执行指令。
6.2 简述壳Shell-bash的作用与处理流程
Bash:linux的默认shell。
shell的作用:用户与操作系统之间完成交互式操作的一个接口程序
shell的处理流程:
- 从终端读入输入的命令。
- 切分输入字符串获得参数。
- 检查第一个参数是否是一个内置的shell命令,如果是则立即执行。
- 如果不是内部命令,调用fork( )创建新进程/子进程执行指定程序。
6.3 Hello的fork进程创建过程
终端程序调用fork函数,首先输入./hello指令,shell读入命令。父进程通过fork函数创建新的运行子进程hello。
6.4 Hello的execve过程
execve函数在当前进程的上下文中加载并运行一个新程序,原型为:
int execve(const char *filename, const char *argv[], const char *envp[])
程序会带着参数列表argv和环境变量列表envp加载运行可执行文件filename,在错误的时候会返回。
6.5 Hello的进程执行
开始运行时在用户模式,当调用sleep函数后进入内核模式,内核处理休眠,将其移入等待序列中,当休眠时间结束后会发送给内核一个中断信号,内核进行中断处理,将hello从等待序列中移除,之后继续执行hello。
6.6 hello的异常与信号处理
(以下格式自行编排,编辑时删除)
中断:收到i/o信号,读取异常信号,调用中断处理程序,处理完毕后控制权返回下一条指令。
陷阱:fork一个hello,中断处理程序完成fork工作,处理完毕后控制权返回下一条指令。
故障:缺页异常,从磁盘中加载适当页,处理完毕后控制权返回本条指令。
终止:出现DRAM或SRAM位损坏的奇偶错误,终止程序。
正在上传…重新上传取消
图23:乱按及回车
乱按键盘及回车不会影响程序正常运行。
正在上传…重新上传取消
图24:Ctrl+C及PS
发现输入Ctrl+C后,程序退出,且没有后台留存,故已终止。
正在上传…重新上传取消
图25:ctrl+z、ps、jobs及pstree
按下ctrl+z后,程序仍在后台,故为挂起。
正在上传…重新上传取消
图26:kill
输入kill后,进程被终止。
6.7本章小结
本章简要介绍了进程的概念和作用,Shell-bash的作用与处理流程。
第7章 hello的存储管理
( 2分)
7.1 hello的存储器地址空间
逻辑地址:由程序产生的段偏移地址。它由一个段标识符和一个段偏移量组成。
线性地址:一个有序的非负整数地址集。
虚拟地址:在保护模式下,程序在虚拟内存中运行。虚拟内存被组织成一个存储在磁盘上的N个连续的字节大小的单元阵列。每个字节都有一个唯一的虚拟地址,作为数组的索引。虚拟地址由VPO(虚拟页偏移)、VPN(虚拟页号)、TLBI(TLB索引)和TLBT(TLB标签)组成。
物理地址:计算机系统的主存储器被组织成一个由M个连续的字节大小的单元组成的阵列,每个字节有一个唯一的物理地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
以段描述符为标志,在GDT、LDT查表得到段地址,段地址与偏移地址组合得到线性地址。
7.3 Hello的线性地址到物理地址的变换-页式管理
CPU请求地址时,将地址(虚拟地址)发送给MMU,MMU根据虚拟地址计算出物理地址,送往主存。
7.4 TLB与四级页表支持下的VA到PA的变换
使用 TLB快表来改善MMU查阅PTE的速度。TLB通过虚拟地址VPN部分进行检索,分为索引TLBI和标记TLBT两个部分。VA中的VPN变换为PPN后与PPO(VPO)结合,形成PA。
7.5 三级Cache支持下的物理内存访问
获得物理地址之后,先在L1中寻找对应组。如果存在,检查有效位是否为1.满足则命中,取出值传给CPU,否则按顺序对L2cache、L3cache、内存进行相同操作,直到命中。然后再一级一级向复制
7.6 hello进程fork时的内存映射
fork创建进程时,内核为新进程创建各种数据结构并分配PID,然后创建虚拟内存,复制当前进程的mm_struct、vm_area_struct和页表。然后所有页面标记为只读,区域结构都标记为私有的写时复制。
7.7 hello进程execve时的内存映射
execve函数加载并运行hello时,首先删除当前的用户区域,然后为新程序的代码、数据、bss和栈区域创建新的区域结构,接着将hello程序与标准C库链接,这些对象动态链接到这个程序,然后再映射到用户虚拟地址空间中的共享区域内。
最后设置指向入口的程序计数器。
7.8 缺页故障与缺页中断处理
发生缺页故障时,内核会调用缺页处理程序。程序首先会检查虚拟地址是否合法,如果不合法则触发一个错误,终止程序。然后检查进程是否有读、写或执行该区域页的权限,如果不具有则终止程序。随后,内核将换入新的页并更新页表,然后将控制转移给发缺页故障的指令。
7.9本章小结
本章简要介绍了hello的存储,以及虚拟地址空间与物理地址空间的关系。
结论
(0分,必要项,如缺失扣1分,根据内容酌情加分)
Hello.c经过编译器对预处理指令做初步处理生成hello.i,Hello.i经过编译生成汇编代码文件hello.s,hello.s经过汇编生成可重定位目标程序hello.o,hello.o经过链接生成可执行目标程序hello。
执行 ./hello时shell会为hello程序fork一个子进程,调用execve加载hello并运行,此时的hello是一个子进程。运行过程中涉及复杂的内存管理,包括虚拟内存与物理内存的转换,TLB和Cache、主存的配合,缺页等异常处理,实现用户与hello的交互。
感悟:计算机科学虽然只有不到百年历史,但已然是一个高度发达的学科。这教导我们,不能因诞生的早晚、发展的长短来区分事物的成熟与否。同时,我们也应当从中看出,只有所有的从业人员心往一处想,劲往一处使,一个行业才能产生高速的发展。