计算机系统

大作业

题 目 程序人生-Hello’s P2P
专业计算机科学与技术
学   号2021112656
班   级2103102
学生    郑小博
指 导 教 师   刘宏伟

计算机科学与技术学院
2022年5月 
摘 要
摘要是论文内容的高度概括,应具有独立性和自含性,即不阅读论文的全文,就能获得必要的信息。摘要应包括本论文的目的、主要内容、方法、成果及其理论与实际意义。摘要中不宜使用公式、结构式、图表和非公知公用的符号与术语,不标注引用文献编号,同时避免将摘要写成目录式的内容介绍。

关键词:关键词1;关键词2;……;

(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)

目 录

第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简介
我是hello.c,在编译时经历预处理、编译、汇编、链接后成为了一个可执行目标文件,在被打开时,操作系统为我开辟新进程,将我加载到内存并执行,执行结束后,bash为我收尸,操作系统将我抹去。
1.2 环境与工具
硬件:intel core-i7 10700
软件:windows 11 ,Ubuntu 18.04,VMware
工具:gcc,vim,bash
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
Hello.i hello预处理结果
Hello.s hello汇编代码
Hello.o hello可重定位目标文件
Hello.out hello可执行文件
1.4 本章小结

(第1章0.5分)


第2章 预处理
2.1 预处理的概念与作用
概念:预处理程序负责完成的一个处理部分。系统自动调用预处理程序对程序的‘#’开头的预处理部分进行处理,处理完毕后进入编译阶段。
作用:将‘#’开头的预处理部分进行处理,包括:
预定义符号,如__FILE__\__FUNCTION__\__DATE__\__TIME__,将这些符号转为预定义中的结果
宏定义,如 #definePI3.14159265,将源文件中的PI替换为3.14159265
文件包含, 如 #include,将该行替换为stdio.h中的内容

2.2在Ubuntu下预处理的命令

2.3 Hello的预处理结果解析
结果文件为hello.i ,仍为文本文件,在此文件中,#include后的头文件被替换为文件内容,源文件内函数本身未被更改。

2.4 本章小结
Hello预处理经预处理程序处理,将包括的头文件均加入到源文件中,成为hello.i,。
(第2章0.5分) 
第3章 编译
3.1 编译的概念与作用
概念: 编译过程是整个程序构建的核心部分,编译成功,会将源代码由文本形式转换成机器语言
作用:把预处理完的文件进行一系列词法分析、语法分析、语义分析以及优化后生成相应的汇编代码文件。

3.2 在Ubuntu下编译的命令

3.3 Hello的编译结果解析
3.3.1 综述

Hello.s作为汇编语言代码,仍为文本文件。
主函数首先将rbp入栈并赋值为rsp,指明栈帧头。然后sub 32 rsp,为可能的局部变量开辟空间。然后将edi和rsi入栈,比较edi(argc)与4若不相同则将LC0置入edi,然后调用puts打印字符串然后退出1。若相同,则跳转至L2,将0置入(rbp-4)作为i。每当i小于等于8的时候进入循环,首先同理打印LC1,然后sleep x秒,其中LC1中的%s为argv2,x为argv3.当i大于等于9的时候,跳出循环,调用getchar,之后返回0.

3.3.2 数据
常量:主函数前定义了字符串LC0,LC1.作为只读数据,不可更改。
变量:1)主函数参数——argc和argv,分别存在edi与rsi中。
2)局部变量:循环变量i:存在栈rbp-4中,以相对寻址赋值读取。
3)数组:argv。这里%rax记录偏移量,%rbp-32是指针数组argv的首地址。通过M[%rbp-32+%rax]的寻址方式来找到对应的字符串的首地址。
3.3.3 控制转移
1.比较在if条件语句中,要进行判断 argc!=4。对应的汇编代码如下图所示。

这里通过cmpl指令实现。同时设置条件码,便于下一步跳转指令的实现。

2.跳转:如
判断i与8的关系,小于等于则跳转至循环。
Jmp作用是将PC指向.L4
2.函数调用:
使用call指令进行函数调用,通过寄存器%rdi,%rsi,%rdx,%rcx,%r8以及%r9传递参数,若参数超过6个,则其余参数被分配到栈中。汇编代码如下图所示。

call指令将首地址压入栈中,为局部变量与函数参数建立栈帧,转移到对应函数的地址。最后使用ret指令返回。

3.3.4功能操作
1.赋值:
关于赋值操作,此程序中包括对局部变量i赋值为1的操作。使用了movl指令, 对i赋初值为0。
2.算术运算
程序中的算术仅有i++。对比汇编代码,这里通过addl指令实现循环体变量i的增加。

3.4 本章小结
编译时,编译器把预处理完的文件进行一系列词法分析、语法分析、语义分析以及优化后生成相应的汇编代码文件。

(以下格式自行编排,编辑时删除)
(第3章2分) 
第4章 汇编
4.1 汇编的概念与作用

概念:汇编器(as)将.s汇编程序翻译成机器语言指令,把这些指令打包成可重定位目标程序的格式,并将结果保存在.o目标文件(包含程序的指令编码)中。
作用:解释和翻译汇编代码中的指令和数据,判断数据类型,将数据存入应被存入的节,如data,rodata。并将需要重定位的信息cunrurel.data中。将代码翻译后变为机器指令,存入text,对于需要重定位的函数,存入rel.text.
4.2 在Ubuntu下汇编的命令
4.3 可重定位目标elf格式

其中,头十六个字节是magic,前四个字节是魔数,可以确认文件类型,接下来的一个字节,是用来区分32位还是64位,再下一个字节,是区分小端法还是大端法的,然后是版重号,接下来用0来填充。接下来的部分,是链接器的语法分析和解释目标文件分析,ELF头的大小、目标文件的类型、机器类型、节头部表的文件偏移、节头部表的条目的大小和数量。

节头表描述了每一个节的名称、在文件中的偏移量以及对齐方式等信息。

重定位节:在该节中保存了符号重定位相关信息,如.rodata节中的模式串,puts函数,exit函数,printf函数,sleep函数,getchar函数以及atoi函数。等。
.symtab节:在该节中保存了符号表,包括符号值,大小,作用域,类型,名字等信息。

4.4 Hello.o的结果解析
(以下格式自行编排,编辑时删除)
objdump -d -r hello.o 分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。

说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。

与原始的hello.s相比,在反汇编代码的结果中主要有三点不同:
1. 函数调用以及全局变量的名字被改为自主函数起的偏移
2. 函数调用以及全局变量的地址为00 00 00 00 ,并标有R_X86_64_(PC)32。Rel部分的内容,这将为将来的重定位提供指导。
3. 无数据,全局变量的数据在其他节中。
4.5 本章小结
汇编器(as)将.s汇编程序翻译成机器语言指令,把这些指令打包成可重定位目标程序的格式,并将结果保存在.o目标文件(包含程序的指令编码)中。

(第4章1分) 
第5章 链接
5.1 链接的概念与作用
链接是将各种代码和数据片段收集并组合成一个单一文件的过程,这个文件可被加载到内存并执行。
5.2 在Ubuntu下链接的命令
5.3 可执行目标文件hello的格式
使用终端命令:readelf -a hello > hello_out.elf
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。
ELF头的信息如下图所示。其中,类型是EXEC(可执行文件),入口点地址也已经确定为0x4010f0,重定位已经完成,有29个节。

节头部表的信息如下图所示。这里列出了各节的名称、类型、地址以及偏移量等信息。可见,这些节的地址都已经确定,而不是0,可通过偏移量计算得到最终的地址。

重定位节的内容如下图所示。

符号表的内容如下图所示。

5.4 hello的虚拟地址空间

由5.3中所分析的ELF节的内容可知,.text节地址为0x4010f0,在edb中查看该处信息,如下图所示。

利用同样的方法,.rodata节的地址为0x402000,在edb中找到信息如下图所示。

.rodata中包含了printf的格式串内容。
.init节的地址为0x401000,虚拟地址空间的信息如下。

.interp节保存了共享库的信息,地址是0x4002e0,它的虚拟地址空间的信息如下

5.5 链接的重定位过程分析
用指令objdump -d -r hello > objdump.txt进行反汇编,下面分析二者不同之处。

1. 反汇编结果中所使用的函数已经完成了符号解析与重定位,如printf/getchar/sleep/exit等。Hello.o由于为链接所以没有。
2. Hello中,jmp所值地址由于完成了重定位而有了具体的内存地址。
3. Hello新增了.init/.plt节。

有上述差别可知,链接的过程就是将编译好的把编译好的目标文件和其他的一些目标文件和库链接在一起,形成最终的可执行文件的过程。此过程包含符号解析和重定位。

5.6 hello的执行流程
子程序名 地址
hello!_start 0x4010f0
hello!_init 0x401000
hello!main 0x401125
hello!puts@plt 0x401030
hello!printf@plt 0x401040
hello!getchar@plt 0x401050
hello!atoi@plt 0x401060
hello!exit@plt 0x401070
hello!sleep@plt 0x401080
hello! fini 0x4011c0
libc.so.6!nanosleep 0x7fa0290fb619
libc.so.6!printf 0x7f4bc4244770
libc.so.6!exit 0x7fa02903ad92

5.7 Hello的动态链接分析

调用dl_init函数前后,内容发生了明显变化,这是因为动态链接采用了延时绑定的策略,将过程地址的绑定推迟到第一次调用该过程时。延迟绑定通过全局偏移量表(GOT)和过程链接表(PLT)的协同工作实现函数的动态链接,其中GOT中存放函数目标地址,PLT使用GOT中地址跳转到目标函数。
5.8 本章小结
链接的过程就是将编译好的把编译好的目标文件和其他的一些目标文件和库链接在一起,形成最终的可执行文件的过程。此过程包含符号解析和重定位。
链接器在完成符号解析以后,就把代码中的每个符号引用和正好一个符号定义关联起来。在重定位的过程中,链接器将所有链接文件中相同的节合并,链接器将运行时地址赋给新的聚合节,依次进行赋予定义的每个节、每个符号。然后修改hello.o中的代码节和数据节中符号的引用,使得它们指向正确的运行地址。
(第5章1分)

第6章 hello进程管理
6.1 进程的概念与作用
6.1.1进程的概念
进程的经典定义就是一个执行中程序的实例。进程的广义定义是指一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。每一个进程都有它自己的地址空间,一般情况下,包括文本区域、数据区域和堆栈。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。
6.1.2进程的作用
进程提供了一种假象,好像我们的程序是系统中当前运行的唯一的程序一样。我们的程序好像是独占地使用处理器和内存,处理器就好像是无间断地一条接一条地执行我们程序中的指令。我们程序中的代码和数据好像是系统内存中唯一的对象。

6.2 简述壳Shell-bash的作用与处理流程
Shell是一个交互型的应用级程序,它执行一系列的读、求值步骤,解析命令行,并代表用户运行其他程序。
Shell的处理流程如下:
(1)终端进程读取用户由键盘输入的命令行。
(2)分析命令行字符串,获取命令行参数,并构造传递给execve的argv向量。
(3)检查第一个命令行参数是否是一个内置的shell命令。
(4)如果不是内置命令,调用fork函数创建子进程。
(5)在子进程中,用步骤2获取的参数,调用execve函数执行指定程序。
(6)如果用户没要求后台运行,即命令末尾没有&号,否则shell使用waitpid函数等待作业终止后返回。
(7)如果用户要求后台运行,即命令末尾有&号,则shell返回。
6.3 Hello的fork进程创建过程
当在命令行中输入./hello时,命令行判断该命令不是内置命令,于是shell将hello视为可执行目标文件。使用fork函数创建子进程。新创建的子进程得到与父进程虚拟地址空间相同但独立的一份副本,包括代码和数据段、堆、共享库以及用户栈。fork函数调用一次,返回两次,一次返回到父进程,一次返回到新创建的子进程。父进程与子进程能够并发运行。
6.4 Hello的execve过程
execve函数的作用是在当前进程的上下文中加载并运行程序。execve函数加载并运行可执行目标文件hello,且带参数列表argv和环境变量envp,只用当出现错误时,execve函数才会返回,调用成功则不会返回。execve调用启动加载器来执行hello程序。加载器首先删除子进程现有的虚拟内存段,并创建新的代码段、数据段、堆和栈段。代码和数据段被映射为hello文件的.txt和.data区,堆和栈被置空。最后加载器将PC指向hello程序的起始位置。
6.5 Hello的进程执行
上下文信息:上下文就是内核重新启动一个被抢占的进程所需的状态。它由一些对象的值组成,包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构。
进程时间片:一个进程执行它的控制流的一部分的每一时间段叫做时间片。
内核态:处理器通过设置某个控制寄存器的模式位,限制应用可以执行的指令以及它可以访问的地址空间范围。当设置了模式位时,进程就运行在内核模式中。一个运行在内核模式的进程可以执行指令集中的任何指令,并且可以访问系统中的任何内存位置。
用户态:没有设置模式位时,进程运行在用户态中。用户模式的进程不允许执行特权指令,也不允许直接引用地址空间中内核区的代码和数据。
进程调度:当内核选择一个新的进程运行时,内核调度了这个进程。在内核调度了一个新的进程运行时,它就抢占当前的进程,并使用上下文切换的机制将控制转移到新的进程。上下文切换的实现包括以下三个步骤:
(1) 保存当前进程的上下文;
(2) 恢复某个先前被抢占的进程被保存的上下文;
(3) 将控制偿传递给这个新恢复的进程。[2]
当内核代表用户执行系统调用时,可能会发生上下文切换。当hello程序调用sleep函数时,内核可以选择进行上下文切换,进程由用户态转变为内核态,运行另外一个进程并切换为用户态。此外,当分配给进程的时间片用尽,内核也会执行上下文切换。
6.6 hello的异常与信号处理
下表给出了异常的四种类别。Hello在执行过程中以下几种异常都可能会发生。
类别 原因 异步/同步 返回行为
中断 来自I/O设备的信号 异步 总是返回到下一条指令
陷阱 有意的异常 同步 总是返回到下一条指令
故障 潜在的可恢复的错误 同步 可能返回到当前指令
终止 不可恢复的错误 同步 不会返回
出现异常时,首先将控制传递给异常处理程序,运行异常处理程序。当异常处理程序执行完毕后,类型为中断或陷阱的异常会返回到下一条指令;类型为故障的异常,如果故障被成功解决,那么返回到当前指令,如果故障未能解决,则程序终止;类型为终止的异常,不会返回。
乱按键盘:
1) 不按,或者按正常的按键:程序会正常地每隔一秒输出一次字符串,正常被 按下的按键被存入缓冲区中,循环结束后getchar时,在收到回车后读取,结束程序。第一个字符之后的数据仍在缓冲区内,被放到下一条指令中。
2) Ctrl+C: shell向该前台程序发送一个SIGINT信号,hello接收SIGINT后进入信号处理程序,默认终止。
3) Ctrl+Z:sehll向该前台程序发送一个SIGTSTP信号,hello接收SIGTSTP后进入信号处理程序,默认停止。但进程未被回收,jobs中仍有该任务,输入fg命令,该进程继续运行。
6.7本章小结
Shell在工作时,循环处理读取-分析-检查内置命令执行-fork创建进程-执行的循环。在新进程中使用execve执行指定程序,程序运行时会敏锐地接受信号与异常并跳入对应的处理程序并可能进行用户模式切换,根据情况返回程序或终止。
(第6章1分) 
第7章 hello的存储管理
7.1 hello的存储器地址空间
(以下格式自行编排,编辑时删除)
结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。
7.2 Intel逻辑地址到线性地址的变换-段式管理
(以下格式自行编排,编辑时删除)
7.3 Hello的线性地址到物理地址的变换-页式管理
(以下格式自行编排,编辑时删除)
7.4 TLB与四级页表支持下的VA到PA的变换
(以下格式自行编排,编辑时删除)
7.5 三级Cache支持下的物理内存访问
(以下格式自行编排,编辑时删除)
7.6 hello进程fork时的内存映射
(以下格式自行编排,编辑时删除)
7.7 hello进程execve时的内存映射
(以下格式自行编排,编辑时删除)
7.8 缺页故障与缺页中断处理
(以下格式自行编排,编辑时删除)
7.9动态存储分配管理
(以下格式自行编排,编辑时删除)
Printf会调用malloc,请简述动态内存管理的基本方法与策略。
7.10本章小结
(以下格式自行编排,编辑时删除)
(第7章 2分) 
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
(以下格式自行编排,编辑时删除)
设备的模型化:文件
设备管理:unix io接口
8.2 简述Unix IO接口及其函数
(以下格式自行编排,编辑时删除)
8.3 printf的实现分析
(以下格式自行编排,编辑时删除)
https://www.cnblogs.com/pianist/p/3315801.html
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall等.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
(以下格式自行编排,编辑时删除)
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
(以下格式自行编排,编辑时删除)
(第8章1分)
结论
hello程序的一生包括以下阶段。
1. 预处理,预处理器将hello.c转化为hello.i文件。
2. 编译,编译器将hello.i转化为hello.s汇编语言文件。
3. 汇编,汇编器将hello.s文件转化为hello.o可重定位的目标文件。
4. 链接,链接器将hello.o与其他目标文件进行链接,得到可执行文件hello。
5. 加载,在shell中输入./hello.out 20211112656 zxb 1,shell就会通过fork函数创建子进程,调用execve函数加载并执行hello程序。
6. 执行,调用execve函数时,execve 调用启动加载器,内核为hello进程映射虚拟内存。进入程序入口处开始载入物理内存,进入main函数执行相应代码。
7. 异常处理:程序使用陷阱(系统调用)时,会进入内核态,控制转交给os,进行上下文切换, 程序阻塞,直到等待的事件发生,重新切换回该程序。
8. 信号处理:进程通过设置handler,对不同的信号进行处理或终止程序。
9. 终止:子进程执行完成后,发送信号SIGCHLD给shell,shell回收当前进程,并且删除hello的所有数据结构。
本学期通过对课程的认真学习,和四个实验的实践,我更深入地理解了现代计算机系统。这对我的计算机思维及其体系的构建起到了极大的作用。首先通过对计算机系统的漫游对系统架构进行大致了解,之后深入每一个部分,包括对信息的表示,程序的机器级表示,计算机处理器体系结构,程序的优化,存储器体系结构,连接,信号等,最终将大量的细节归一化,一切回归最原始的程序,凝练成一个程序的一生。这对我计算机系统的认知实现了了融会贯通。
同时也在这门课程中我也感受到,要成为一名真正的计算机科学与技术领域的成员,在仅仅编写程序之后,还要深入理解计算机系统,从底层挖掘计算机运行的根本,才能在更高层次的应用中得心应手,游刃有余。

(结论0分,缺失 -1分,根据内容酌情加分) 
附件
1 Hello.c 源程序
2 Hello.i 源文件预处理结果
3 Hello.s 预处理后编译结果
4 Hello.o 编译后汇编结果
5 Hello.out 可重定位目标文件链接结果
6 Objdump.txt Hello.out的反汇编文件
7 objdumpo.txt Hello.o的反汇编文件
8 o.elf Hello.o通过readelf读取的节头表
9 Out.elf Hello通过readelf读取的节头表
(附件0分,缺失 -1 分) 
参考文献

[1] laolitou_ping. C语言——预处理[DB/OL]. CSDN,2021.05.21. http://t.csdn.cn/dja6H.
[2] 兰德尔 E. 布莱恩特,大卫 R. 奥哈拉伦. 深入理解计算机系统(第3版)[M]. 北京:机械工业出版社. 2016.7.
[3] 叫我小者呀. 进程的概念[DB/OL].CSDN, 2021-05-06.http://t.csdn.cn/ficbV.
(参考文献0分,缺失 -1分)