计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 人工智能
学 号 2021111772
班 级 2103601
学 生 门春廷
指 导 教 师 郑贵滨
计算机科学与技术学院
2023年5月
详细阐述hello.c从预处理到最终生成可执行文件的全过程,介绍了hello.c需要经历预处理、编译、汇编、链接等步骤。根据进程管理,虚拟内存两方面,结合课本知识,更加详细的了解了计算机系统。
关键词:hello程序、虚拟内存、Linux
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
目 录
6.2 简述壳Shell-bash的作用与处理流程... - 26 -
6.3 Hello的fork进程创建过程... - 26 -
7.2 Intel逻辑地址到线性地址的变换-段式管理... - 31 -
7.3 Hello的线性地址到物理地址的变换-页式管理... - 32 -
7.4 TLB与四级页表支持下的VA到PA的变换... - 32 -
7.5 三级Cache支持下的物理内存访问... - 33 -
7.6 hello进程fork时的内存映射... - 34 -
7.7 hello进程execve时的内存映射... - 34 -
第1章 概述
(0.5分)
1.1 Hello简介
P2P过程:P2P即From Program to Process。Linux下,hello.c经过c预处理器进行预处理(hello.i)、编译器编译为汇编代码(hello.s)、汇编器汇编转化为二进制目标代码文件(hello.o)、链接器进行链接产生最终的可执行代码(hello.out)。在bash输入命令后,bash调用fork和execve函数为可执行代码创建子进程(process)。
然后shell为其execve映射虚拟内存。进入程序入口后,程序开始加载物理内存,然后进入main函数执行目标代码。 CPU为正在运行的hello分配时间片以执行逻辑控制流。程序完成后,shell父进程负责恢复hello进程,内核删除相关的数据结构。
1.2 环境与工具
硬件环境:Intel Core i7;NVIDIA GeForce GTX 1650 Ti 4GB;8GB(Micron 2933MHz)。
软件环境;Windows 10; Ubuntu-20.04.4。
开发与调试工具:edb;gbd;gcc;objdump等
1.3 中间结果
文件名称 | 文件作用 |
Hello.c | Hello程序源代码 |
Hello.i | 经预处理得到的文本文件 |
Hello.s | 编译后得到的汇编代码 |
Hello.o | 汇编后得到的可重定位目标执行文件 |
Hello | 链接后得到的可执行文件 |
1.4 本章小结
本章作为文章的开始,粗略的介绍了hello.c文件到可执行文件所经历的过程和中间结果。
第2章 预处理
(0.5分)
2.1 预处理的概念与作用
预处理概念:
一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程,即在编译之前进行的处理,C语言的预处理主要有三个方面的内容: 1.宏定义; 2.文件包含; 3.条件编译。 预处理命令以符号“#”开头。
预处理作用:C预处理器扩展源代码,插入所有用#include命令指定的文件,并扩展所有用#define声明指定的宏。
2.2在Ubuntu下预处理的命令
图1 预处理指令
图2 hello.i
2.3 Hello的预处理结果解析
图3 hello.c
图4 hello.i
预处理器将stdio.h、unistd.h和stdlib.h内容加入到hello.i中,并将替换所有宏定义的值,将原代码放到文件最后。
2.4 本章小结
本章对预处理有了详细了解,了解到预处理代码,和文件的变化。
第3章 编译
(2分)
3.1 编译的概念与作用
编译的概念:就是把预处理后代码转化为汇编指令的过程。
编译的作用:为不同的源程序语言提供了统一的汇编语言输出,便于机器以相同的标准执行。
3.2 在Ubuntu下编译的命令
图5 编译指令
图6 hello.s文件
3.3 Hello的编译结果解析
图7 hello.s头
3.3.1hello.s头
分别为.file:源文件名;text:代码段;.globl:全局变量;.data:数据段;.align:对齐方式;.type:指定是对象类型或是函数类型;.size:大小;.long:长整型;.section .rodata:.rodata节;.string:字符串
.LC0和.LC1分别对应这两个printf函数字符串:“用法:Hello 学号 姓名 秒数!\n”,“Hello %s %s\n”
图8 main部分反汇编代码
3.3.2 环境变量
Main的环境参数argc和argv分别保存在%edi和%rsi寄存器中,之后压入栈。
3.3.3 关系操作
判断语句 cmpl $4,-20(%rbp)
判断argc是否如立即数大小,改变标志位。
3.3.4判断语句
图9 判断语句
判断比较argc是不是等于4。若等于4则跳至.L2执行循环,否则调用printf函数打印、exit(1)退出
3.3.5循环
图10 循环部分
循环变量i保存在栈(%rbp-4)的位置,先在.L2:对循环变量i进行赋值,然后跳转至.L3 执行判断操作,判断比较i是不是小于等于7,满足条件跳转至.L4执行循环操作,否则退出循环。
3.3.6数组/指针/结构操作
循环内:首先,程序计算-32(%rbp)即argv的地址,保存在%rax中。argv为char*[]型,%rax + 16相当于计算了argv[2],将argv[2]的值保存在%rdx中,这里argv[2]是一个指针,所以需要8个字节的寄存器保存。同理,程序计算argv[1],并将其保存在%rsi中。在执行完printf后,程序计算argv[3],将其保存至%rdi中。
3.3.7计算
i++(加法操作) addl $1, -4(%rbp)
3.3.8函数
Printf函数: leaq .LC0(%rio),%rdi
call printf@PLT
利用PC相对寻址,.LC0是字符串
Exit函数: movl $1,%edi
call exit@PLT
将1传递给exit
Atoi函数:
将argv[3]的值取出保存到寄存器%rdi中,然后调用atoi函数将值传入。
Sleep函数: 程序将atoi的返回值保存到%edi寄存器中,然后调用sleep函数将参数传入
3.4 本章小结
本章主要是,编译器将一个预处理文件(.i文件)经过编译器转换为汇编代码(.s文件)。除此之外,对汇编代码有了较为详细的了解,如数据,变量赋值,分支结构,循环结构等等。
第4章 汇编
(2分)
4.1 汇编的概念与作用
汇编的概念:
汇编器(as)将汇编语言书写的程序翻译成与之等价的机器指令的过程。
汇编的作用:
将汇编语言转化为机器能懂的机器语言(由0 1构成)
(以下格式自行编排,编辑时删除)
4.2 在Ubuntu下汇编的命令
图11 汇编命令
图12 hello.o文件
4.3 可重定位目标elf格式
图13 ELF头
ELF头的开始是一个16字节的魔数,其中前4个字节7f 45 4c 46描述了该文件是否满足ELF格式,第5个字节02描述了该系统架构为x86-64,第6个字节01表示使用小端法,第7个字节01表示ELF的主版本号。下面是有关该ELF文件的一些其他信息。
图14 节头表
节头部表描述了每一个节的名称、大小、类型、地址、偏移量、旗标、链接、信息、对齐等要素。
图15 重定位节
重定位节描述了需要重定位的符号名称,偏移量,信息,类型。偏移类型包括相对引用和绝对引用。
图16 符号表
符号表中存放了所有引用的函数和全局变量的信息。
4.4 Hello.o的结果解析
图17 反汇编
条件分支变化:
跳转指令由标签转变为相对pc的偏移。
函数调用变化:
在.s文件中,当使用call指令调用函数时,其后紧跟着函数名。然而,在反汇编文件中,call指令的后面紧跟着下一条指令的地址。这是因为该程序调用的函数属于共享库中的函数,其最终的运行时执行地址需要通过动态链接器来确定,call指令中的相对偏移量被暂时编码为全0,在.rela.text节中添加了重定位条目,以便在链接过程中进一步确定函数的执行地址。
数据访问变化:
对于同一全局变量需要通过链接时的重定位确定地址,而在.s文件中仍以.LC0占位符表示。printf的字符串存在.rodata端 +0x22为偏移量。
4.5 本章小结
本章介绍了汇编过程,通过查看ELF文件了解了ELF各个节内容和作用。并通过查看反汇编与hello.i比较,加深了对汇编过程的了解。
第5章 链接
(1分)
5.1 链接的概念与作用
链接的概念:链接(Linking)分为静态链接和动态链接,是指将多个目标文件或库文件组合成一个可执行文件或共享库的过程。
链接的作用:链接的作用是将多个目标文件或库文件合并成一个可执行文件或共享库。通过链接,可以使得程序具有可执行性,并且可以使得不同程序间共享同一份库文件,提高系统资源利用率。链接还可以进行符号解析、地址重定向等操作,以确保程序在运行时能够正常访问所需的函数和变量。
5.2 在Ubuntu下链接的命令
图18 链接代码
5.3 可执行目标文件hello的格式
图19 ELF头
图20 节头部表
各段起始地址、大小等信息如上图所示
5.4 hello的虚拟地址空间
图21 edb显示hello程序
图22 ELF头
由前16个字节可知,这部分为ELF头
图23 指令装载地址
这部分是指令装载区,区域为可读可执行。
图24 字符串常量数据
图25 堆
这部分是运行时堆,由malloc函数管理内存分配,区域为可读可写。
图26 栈
5.5 链接的重定位过程分析
图27 hello反汇编
如hello.o反汇编文件对比可知:
图28 _init函数
图29 外部库函数
图30 _start函数
图31 call指令
- 含有汇编代码的段增多,hello.o反汇编只有.text段由代码,hello反汇编.init段等等都增加代码
- 增添了main入口函数_start,外部库如printf,exit等
- 进行重定位,hello.o中call指令后面偏移地址为0,此时已经进行了重定位,为准确地址。
5.6 hello的执行流程
观察edb右侧寄存器%rip可知程序调用和地址
图32 寄存器
子程序名 | 程序地址 |
_init | 401000 |
.plt | 401020 |
puts@plt | 401090 |
printf@plt | 4010a0 |
getchar@plt | 4010b0 |
atoi@plt | 4010c0 |
_start | 4010f0 |
main | 401125 |
__libc_csu_init | 4011c0 |
__libc_csu_fini | 401230 |
_fini | 401238 |
5.7 Hello的动态链接分析
图33 节头部表
在节头部表中我们发现.got.plt的地址为0x404000,我们在edb中找到它。
执行init后
图34 数据
如图0x7f50bec12190为动态链接后的地址。
5.8 本章小结
反汇编hello文件了解了两个ELF格式文件的差异。
第6章 hello进程管理
(1分)
6.1 进程的概念与作用
概念:进程的经典定义就是一个执行中程序的实例。系统中的每个程序都运行在某个进程的上下文(context)中。上下文是由程序正确运行所需的状态组成的。
作用:进程 提供给了应用程序两个关键抽象
一个独立的逻辑控制流,它提供一个假象,好像我们的程序独占地使用处理器。
一个私有的地址空间,它提供一个假象,好像我们的程序独占地使用内存系统。
6.2 简述壳Shell-bash的作用与处理流程
Shell-bash的作用;Shell对读入的字符进行分割,判别是不是系统命令,以及利用fork,execve运行程序
处理流程:
1. shell在用户输入命令行后,对这个命令行分割。
2. Shell解析第一个命令行参数是否为一个内置的Shell命令。如果是,它就立即执行这个命令。
3. 如果不是内置命令,那么shell建立一个子进程,并在子进程中执行所请求的程序。
4.如果是前台程序,shell等待程序停止或终止。如果是后台,则继续等待用户输入命令。
6.3 Hello的fork进程创建过程
在Linux系统中,用户可以通过./指令来执行一个可执行目标文件。Shell就会利用fork()创建一个新的进程,fork()函数拥有一个pid_t型的返回值。子进程中fork返回0,在父进程中fork返回子进程的Pid。子进程与父进程几乎相同但有细微的差别。子进程得到与父进程虚拟地址空间相同的(但是独立的)一份副本(代码、数据段、堆、共享库以及用户栈),并且子进程拥有与父进程不同的Pid。
6.4 Hello的execve过程
Execve函数调⽤loader加载器函数,loader删除子进程现有的虚拟内存段和映射表,清空和物理内存,硬盘上可执行文件的映射,栈、堆初始化0,通过将虚拟地址空间中的页映射到可执行文件的页大小的片,新的代码和数据段被初始化为可执行文件中的内容。最后加载器设置PC指向_start地址,_start最终调用hello中的main函数。并将argv和envp作为参数传递给进程。
6.5 Hello的进程执行
上下文信息:内核为每个进程维护一个上下文(context)。上下文就是内核重新启动一个被抢占的进程所需要的状态。它由一些对象的值组成,这些对象包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈、各种内核数据结构。
进程时间片:进程执行它的控制流的一部分的每一个时间段叫做时间片。
进程调度过程:当进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占的进程,这种决策叫做调度。在内核调度一个新进程运行后,他会抢占当前进程,并利用一种称为上下文切换的机制将控制转移到新进程。
用户模式与内核模式:处理器通过某个控制寄存器中的一个模式位来提供限制一个应用可以执行的指令以及它可以访问的地址空间范围的功能。该寄存器描述了当前进程运行的权限。当设置了模式位时,进程就运行在内核模式中。没有设置模式位时,进程就运行在用户模式中。一个运行在内核模式的进程可以执行指令集中的任何指令,并且可以访问系统中的任何内存;而用户模式的进程不允许和执行特权指令、也不允许用户模式中的进程直接引用地址空间中内核区内的代码和数据。
6.6 hello的异常与信号处理
四类异常:
- 中断:处理器外I/O设备信号的结果,异步异常,总是返回到下一条指令
- 陷阱:一种有意的异常,使指令执行的结果,其最重要的用途是在用户程序和内核之间提供一个像过程一样的接口,称为系统调用。同步异常,总是返回下一条指令。
- 故障:潜在可恢复的错误,它可能被故障处理程序修复。如果处理程序能修正,则重新执行;若不能被修正,则终止程序。同步异常,可能返回当前指令。
- 终止:不可恢复的错误,终止处理程序,不会将控制返回给应用程序。
图35 常用信号及处理
Hello对各种信号的处理:
1. ctrl+c:终止
图36 hello按ctrl+c
2. ctrl+z:停止
图37 hello按ctrl+z
3.回车:空行
图37 hello按回车
4.ps
图38 ps
5.jobs:进程为停止
图39 jobs
6.pstree:以树状显示进程
图40 pstree
7.fg:将后台程序再次变为前台进程
图41 fg
8.kill:杀死进程
图42 kill
6.7本章小结
本章介绍了进程的概念与作用,壳shell处理进程的过程以及执行hello程序时如何调用fork函数创建进程和调用execve执行hello程序。同时也分析了hello执行过程中可能引发的异常和信号处理。
第7章 hello的存储管理
( 2分)
7.1 hello的存储器地址空间
逻辑地址:逻辑地址是指由程序产生的与段相关的偏移地址部分。就是hello.o里面的相对偏移地址。
线性地址:逻辑地址经过段机制后转化为线性地址,其地址空间是一个非负整数地址的有序集合,如果地址空间中的整数是连续的,那么我们说它是一个线性地址空间。即hello里面的虚拟内存地址。
虚拟地址:虚拟内存被组织为一个由存放在磁盘上的N个连续的字节大小的单元组成的数组。仍为hello里面的虚拟内存地址。
物理地址:CPU通过地址总线的寻址,找到真实的物理内存对应地址。就是hello在运行时虚拟内存地址对应的物理地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
段式管理是一种将程序分割成多个段进行存储的管理方法。每个段都是一个独立的逻辑实体,程序员需要知道并使用这些段。段式管理与程序的模块化直接相关。
段式管理通过使用段表来实现。段表包括段号或段名、段起点、装入位和段长度等信息。此外,还需要主存占用区域表和主存可用区域表来支持段式管理。
逻辑地址 = 段标识符 + 段内偏移量。
段内偏移量是一个固定的常量,直接传递给最后的加法器,用于计算线性地址。
段标识符由索引号、表指示器和请求权级组成。
索引号是段描述符表的索引。段描述符表可以细分为全局段描述符表和局部段描述符表,根据表指示器确定要使用哪个表。
在逻辑地址到线性地址的转换过程中,我们将逻辑地址分为段选择符和偏移量。根据段选择符的TI(Table Indicator)位,如果是0,则切换到全局段描述符表(GDT),如果是1,则切换到局部段描述符表(LDT)。然后使用段选择符中的索引号在相应的表中取出段描述符,并从段描述符中取出基地址(Base),然后计算线性地址为Base + Offset。
7.3 Hello的线性地址到物理地址的变换-页式管理
图43 页式管理
分页机制是一种将线性地址映射到物理地址的内存管理机制。。
分页的基本原理是将内存划分为固定大小的页面(通常为4KB),每个页面包含一定范围的线性地址空间。每个页面都具有与之对应的物理地址。为了实现地址转换,操作系统需要提供当前任务的页表,即页目录表和页表。
通过分页机制,操作系统可以实现虚拟内存管理,将线性地址空间映射到物理内存空间,并实现了内存的隔离和保护。分页机制还可以实现内存的共享和换入换出等高级功能,提高了系统的灵活性和性能。
7.4 TLB与四级页表支持下的VA到PA的变换
整个过程从CPU出发。CPU内部将逻辑地址转化为虚拟地址以后,传送给MMU进行地址翻译,MMU首先将VPN取出,构建TLB的标记位和组索引,然后在翻译后备缓冲器TLB中寻找对应的PTE条目。
这里的TLB有16个组,每组4个条目(四路相联),所以构建时将VPN的低4位取出作为组索引,剩余位作为标记位。通过组索引和行匹配寻找对应的PPN。如果命中,就将其取出,构建下一步使用的PPN。
如果TLB不命中,MMU就通过4级页表从高速缓存中取出相应的PTE,作为PPN构建物理地址,同时在TLB中更新。
多级页表的虚拟地址翻译与单级页表的主要区别在于页表的保存形式。
多级页表从两个角度减少了内存要求。第一,如果一级页表中的一个PTE是空的,那么相应的所有低级页表都不存在,这意味着巨大的潜在节约。第二,只有一级页表才需要总是缓存在主存中,虚拟内存系统可以在需要时创建、页面调入或调出二级页表,这就减少了主存的压力;只有最经常使用的二级页表才需要缓存在主存中。
图44 多级页表
图45 VA到PA地址翻译
7.5 三级Cache支持下的物理内存访问
i7处理器的内存管理单元(MMU)使用四级页表将虚拟地址翻译为物理地址,并得到了物理地址PA。在分析支持三级缓存的物理内存访问时,假设L1缓存是8路相联,有64个组。每个缓存块的大小为64字节,因此块内偏移(CO)和组索引(CI)都是6位,标记(CT)是40位。
根据物理地址(PA),首先使用CI进行组索引,每个组有8路,然后分别匹配标记CT。如果匹配成功且块的有效位为1,则表示命中。此时可以根据块内偏移CO返回数据。如果未匹配成功或者匹配成功但是标志位为1,则表示未命中。此时需要从下一级缓存中获取所请求的块,并将新的块存储在组索引指示的组中的某个高速缓存行中。
如果映射到的组内有空闲块,则将新块直接放置在该空闲块中。否则,必须驱逐一个已存在的块。一般使用最近最少使用(LRU)策略进行替换,选择最久未被使用的块进行替换。
通过三级缓存的物理内存访问过程涉及组索引、标记匹配、命中判断、块的读取和替换等步骤,以实现高效的数据访问和缓存管理。
7.6 hello进程fork时的内存映射
在调用fork创建一个新进程时,会为新进程的hello程序创建虚拟内存。这个过程涉及创建当前进程的mm_struct(内存管理结构)、vm_area_struct(区域结构)和页表的副本。
而每个区域结构(vm_area_struct)被标记为私有的写时复制(Copy-on-Write,COW)。这意味着在新进程中,对于这些区域的写操作不会立即修改原始页面,而是过写时复制机制创建新的页面,目的是为了实现进程间的数据隔离。
7.7 hello进程execve时的内存映射
1.删除已存在的用户区域。删除当前进程虚拟地址的用户部分中已存在的区域结构。
2.映射私有区域。为Hello的代码、数据、bss和栈区域创建新的区域结构,所有这些区域都是私有的、写时复制的。
3.映射共享区域。比如Hello程序与标准C库libc.so链接,这些对象都是动态链接到Hello的,然后再用户虚拟地址空间中的共享区域内。
4.设置程序计数器PC。execve 做的最后一件事情就是设置当前进程上下 文的程序计数器,使之指向代码区域的入口点。
7.8 缺页故障与缺页中断处理
缺页故障:当指令引用一个虚拟地址,在MMU中查找页表时发现与该地址相对应的物理地址不在内存中发生了故障,需要调用异常处理子程序从磁盘中取出。
缺页中断处理:缺页处理程序是系统内核中的代码,选择一个牺牲页面,如果这个牺牲页面被修改过,那么就将它交换出去,换入新的页面并更新页表。当缺页处理程序返回时,CPU重新启动引起缺页的指令,这条指令再次发送VA到MMU,这次MMU就能正常翻译VA了。
7.9本章小结
本章主要介绍了段式管理,页式管理,虚拟地址转换,多级cache和从虚拟内存了解fork,execve函数
结论
(0分,必要项,如缺失扣1分,根据内容酌情加分)
- 预处理器处理hello.c生成hello.i文件
- 编译器处理hello.i生成汇编文件hello.s文件
- 汇编器处理hello.s生乘可重定位目标文件hello.o
- 链接器将hello.o与其他可重定位的目标文件和动态链接库链接乘可执行程序hello
- 在shell输入./hello 2021111772 menchunting。运行hello
- Shell调用fork创建子进程
- Shell调用execve函数,启动程序
- 载入物理内存,运行代码
- CPU为其分配时间片,执行自己的控制逻辑流
- MMU将虚拟地址映射为物理地址
- 在运行对不同的信号,做出不同的反应
- 结束,shell回收子进程。
感悟:每个程序都要经过完整的步骤才能执行,所运用的知识更是多,就算hello短短几十行代码也五脏俱全。
附件
hello.c:源程序
hello.i:预处理后的文本文件
hello.s:编译后汇编程序文本文件
hello.o:汇编后的可重定位目标程序(二进制文件)
hello:链接后的可执行目标文件
hello.o.txt: hello.o反汇编
hello.txt: hello反汇编
参考文献
为完成本次大作业你翻阅的书籍与网站等
[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分,缺失 -1分)