2022春 计算机系统大作业 程序人生-Hello’s P2P

这篇博客详细分析了计算机系统中从源代码到可执行程序的完整过程,包括预处理、编译、汇编和链接等阶段。通过实例程序hello,探讨了gcc、edb、readelf等工具的使用,展示了预处理器如何处理宏和头文件,编译器如何生成汇编代码,汇编器如何形成可重定位目标文件,以及链接器如何生成可执行文件。此外,还讨论了进程管理,包括fork、execve、内存映射和异常处理。最后,文章阐述了存储管理,如地址空间、段式管理、页式管理和三级缓存。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

计算机系统

大作业

题 目 程序人生-Hello’s P2P
专 业 计算学部
学   号
班   级
学 生    
指 导 教 师

计算机科学与技术学院
2022年5月

摘 要
为深入理解计算机系统,本文以hello程序整个生命周期为例,利用gcc、edb、readelf等工具进行了实验与分析,伴随着每个阶段,对涉及到的知识点进行了较细致的介绍,为更进一步学习奠定基础。

关键词:CSAPP;Hello’s P2P;计算机系统

(摘要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简介
P2P:即From Program to Process。Hello.c文件先经过cpp预处理生成hello.i,然后cll进行编译将hello.i翻译成hello.s,接着as将hello.s翻译成机器语言指令得到hello.o文件,最后由连接器ld得到可执行目标文件hello。在shell中输入./hello运行程序,shell为其fork产生子进程来执行hello。
020:子进程调用execve,为hello映射内存,设置当前进程上下文中的程序计数器,使之指向代码区域的入口点。进入程序入口后通过存储管理机制将指令和数据载入内存,CPU以流水线形式读取并执行指令,为hello分配时间片执行逻辑控制流。程序运行结束后,父进程回收子进程,内核删除相关内容,hello从开始的未被内存映射到运行再到回收后不再存在,就是020的过程。
1.2 环境与工具
硬件环境:X64 CPU(AMD R7 1.80GHZ);16G RAM;475GB SSD
软件环境:Windows10 64位;Vmware;Ubuntu 20.04 LTS 64位
工具:codeblocks;gdb;Objdump;HexEdito
1.3 中间结果
hello.i 预处理阶段产生的文件
hello.s 编译阶段产生的文件
hello.o 汇编阶段产生的文件
hello 链接阶段产生可执行目标文件
hello.elf hello.o的elf文件
hello.objdump hello反汇编生成文件
1.4 本章小结
介绍hello程序的生命周期,同时交代此次作业用到的工具以及运行环境。

(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用
概念:预处理一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。
作用:预处理中会展开以#起始的行,试图解释为预处理指令,预处理指令一般被用来使源代码在不同的执行环境中被方便的修改或者编译。
1.条件编译(#if/#ifdef/#ifndef/#else/#elif/#endif);
2.删除#define并展开所定义的宏;
3.源文件包含(#include);
4.行控制(#line);
5.错误指令(#error);
6.和实现相关的杂注(#pragma);
7.空指令(#)
8.删除注释(//)
2.2在Ubuntu下预处理的命令

预处理命令:gcc -E -o hello.i hello.c
在这里插入图片描述

图 2-1 预处理
结果:
在这里插入图片描述

图 2-2 预处理结果
2.3 Hello的预处理结果解析
在这里插入图片描述

图 2-3 预处理结果
对比原来的hello.c文件,main函数部分并没有改变,而以#开头的语句都进行了展开,而注释也被删去。原来的#include语句被替换成了头文件的内容。
2.4 本章小结
本章介绍了预处理的概念、作用以及在Ubuntu下预处理的结果
(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序
概念:利用编译程序从源语言编写的源程序产生目标程序的过程。编译器(ccl)将文本文件hello.i翻译成hello.s,它包含一个汇编语言程序。
作用:将高级语言转化为汇编语言,便于汇编器进行下一阶段
3.2 在Ubuntu下编译的命令
编译命令:gcc -S hello.i -o hello.s
在这里插入图片描述

图 3-1 编译
3.3 Hello的编译结果解析
编译结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

图 3-2 编译结果
文件开头部分:
节名称 作用

.file 声明源文件

.text 代码节

.section.rodata 只读数据段

.globl 声明全局变量

.type 声明一个符号是函数类型还是数据类型

.size 声明大小

.string 声明一个字符串

.align 声明对指令或者数据的存放地址进行对齐的方式
分析:
3.3.1 数据
1)字符串
在这里插入图片描述

图 3-3 字符串
在hello.c中两个printf的参数是字符串常量,字符串被保存在rodata 段中。从编译结果可以看出,第一个字符串有汉字,每个汉字被编码为三 个字节。LC0对应的是argc不为4的输出,LC1对应的是argc为4的输 出。
2)局部变量
在这里插入图片描述

图 3-4 局部变量
main函数声明了一个局部变量i,编译时先将i放入堆栈,位置如图3-4,然后跳转至L3进行循环累加操作。
3)整型数
main函数的参数argc是整型变量,也同样放入堆栈中,位置在-20(%rbp)。
4)立即数
exit(1) 1为立即数,用立即数寻址

在这里插入图片描述

图 3-5 立即数
3.3.2 数组操作
Hello.c中main函数的第二个参数就是个字符类型的数组,首地址先存储在-32(%rbp)(图3-6第一行),然后通过首地址+偏移量的方式获得数组中两个数据的地址(加8、加16)。

在这里插入图片描述

图 3-6 数组操作

3.3.3 赋值操作
程序中对变量i没有赋初值,而是在循环开始时赋为0。通过movl把0赋给位于-4(%rbp)的i。
3.3.4 关系操作&控制转移

  1. 在if条件中有!=操作,这里主要用了cmpl+je两个指令。如图所示,先比较4与位于-20(%rbp)的第一个参数的值,如果不等则不跳转,执行if中的操作;若相等则跳转至L2。
    在这里插入图片描述

图 3-7 关系操作
2)for循环处的跳转也类似,不过此处为jle不相等跳转。
3.3.4 算术操作

for循环中有累加操作,通过指令addl $1, -4(%rbp)实现,亦即在每遍循环结束时对位于-4(%rbp)的i进行累加1。

3.3.5 函数操作
1)函数调用:函数调用都用了指令call,在程序中一共出现了6次函数调用call puts@PLT、call exit@PLT、call printf@PLT、call atoi@PLT、call sleep@PLT、call getchar@PLT。
2)参数传递:参数传递主要通过6个寄存器实现,分别为%rdi、%rsi、%rdx、%rcx、%r8、%r9,多余的参数通过栈来传递。
3)函数返回
通过指令ret。
3.4 本章小结
本章介绍了编译的概念和作用,并通过hello.s文件分析了编译器如何处理C语言的数据与操作。
(第3章2分)

第4章 汇编

4.1 汇编的概念与作用

注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。
汇编概念:指汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件hello.o中。Hello.o是一个二进制文件。
作用:将汇编语言翻译成机器语言,使其在链接后能够被机器识别并执行。
4.2 在Ubuntu下汇编的命令
汇编命令:gcc -c -o hello.o hello.s

在这里插入图片描述

图 4-1 汇编
4.3 可重定位目标elf格式
用readelf -a hello.o可获得hello.o文件的ELF格式
在这里插入图片描述

图 4-2 ELF头
为了便于查看分析,用readelf -a hello.o > helloelf.txt将结果重定向到helloelf.txt。
在这里插入图片描述

图 4-3 重定向及其产物
分析:
1)ELF头:头部以一个16字节的序列开始,描述生成该文件的系统的字的大小和字节顺序。剩下的部分包含帮助链接器分析语法和解释目标文件的信息,其中包含ELF头大小、目标文件的类型、及其类型、节头部表的文件偏移,以及节头部表中条目的大小和数量。
2)节头:描述不同节的位置和大小,目标文件中的每个节都有一个固定大小的节头部表条目。描述包括包括节的名称、类型、地址和偏移量等。ELF 节头表是一个节头数组。每一个节头都描述了其所对应的节的信息,如节名、节大小、在文件中的偏移、读写权限等。编译器、链接器、装载器都是通过节头表来定位和访问各个节的属性的。
在这里插入图片描述

图 4-4 节头部表
3)重定位节:
重定位就是将符号定义和符号引用进行连接的过程,重定位表是进行重定位的重要依据。汇编器遇到对最终位置未知的目标引用,会产生一个重定位条目,告诉链接器在将目标文件合并成可执行文件时如何修改这个引用。代码的重定位信息就放在重定位节.rela.text中,已初始化数据的重定位条目放在.rela.data中。
ELF定义了32种不同的重定位类型,在重定位节中出现了R_X86_64_PC32(重定位使用32位PC相对地址的引用)和R_X86_64_PLT32,其实这两种为同一种寻址方式。
在hello程序中,重定位节.rela.text一共描述了8个重定位条目。重定位节.rela.eh_frame描述了1个重定位条目。
在这里插入图片描述

图 4-5 重定位节
4)符号表
符号表.symtab保存着程序定义和引用的符号(全局变量和函数)信息。一个符号表的索引是数组的下标。第0项既指定这个表的起始项也作为未定义符号的索引。数组是条目的数组,每个条目包括value:距定义目标的节的起始位置的偏移;size:目标的大小;type:指明数据还是函数;bind:表示符号是本地的还是全局的等等。
Hello.s的符号表一共描述了18个符号。比如main函数,Ndx=1表明它在.text节,value=0表明它在.text节中偏移量为0的地方,size=125表明大小为125字节,bind=GLOBAL表明它是全局符号,type=FUNC:表明它是函数。而对于puts、exit等外部的库函数,需要等到链接后才能确定。
在这里插入图片描述

图 4-6 符号表
4.4 Hello.o的结果解析

用objdump -d -r hello.o进行反汇编,结果如下:
在这里插入图片描述
在这里插入图片描述

图 4-7 反汇编结果
通过与hello.s的对比可以发现,hello.s中的汇编指令被映射到二进制的机器语言,机器语言都能找到其所对应汇编指令。但是在转换的过程中一些操作数会出现与汇编语言不一致的现象:
1)分支转移:在hello.s中有助记符.L1、.L2等,但是汇编成机器语言后就不存在了,因此反汇编后跳转指令处看到的是相对偏移的地址。
2)立即数:hello.s立即数为十进制,而hello.o反汇编后立即数为十六进制。
3)函数调用:在hello.s中函数调用直接在call后面加上需要调用的函数名,而机器语言反汇编后,call指令后是被调函数的相对地址。由于调用的函数都是库函数,需要在动态链接后才能确定被调函数的确切位置,因此call指令后的二进制码为全0,同时需要在重定位节中添加重定位条目,在链接时确定最终的相对地址。

4.5 本章小结
本章介绍了汇编的概念和作用,分析了可重定位目标文件的ELF格式,另外对比了hello.s和hello.o反汇编结果的不同。
(第4章1分)

第5章 链接

5.1 链接的概念与作用
注意:这儿的链接是指从 hello.o 到hello生成过程。
概念:链接是将各种代码和数据片段收集并合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行,为可执行目标文件。
作用:以hello程序调用printf函数为例,它是每个C编译器都提供的C标准库中的一个函数。printf函数存在于一个名为printf.o的单独的预编译好了的目标文件中,而这个文件必须以某种方式合并到我们的hello.o程序中。链接器(ld)就负责处理这种合并。结果就得到hello文件
链接器在软件开发过程中扮演着一个关键的角色,因为它们使得分离编译成为可能。我们不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其它文件。
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-1 链接

5.3 可执行目标文件hello的格式
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。
用readelf -a hello > hello.elf 命令生成 hello 程序的 ELF 格式文件。
在这里插入图片描述

图 5-2 readelf命令
分析:
1)ELF头:
在这里插入图片描述

图 5-3 ELF头
同第四章一样,ELF头以一个16字节的目标序列开始,如图中Magic所示,这个序列描述了生成该文件的系统的字的大小和字节顺序。ELF头剩下的部分包括ELF头的大小、目标文件的类型、机器类型、节头部表的文件偏移,以及节头部表中条目的大小和数量。
2)与hello.o相比,hello的节头部表一共描述了27个不同节的位置、大小等信息,数量更多。同时每一节都有了实际地址,而不是像在hello.o中那样地址值全为0。这说明重定位工作已完成。同时多出的节是为了能够实现动态链接。

在这里插入图片描述

图 5-4 节头部表
在这里插入图片描述

图 5-5 节头部表
3)程序头
hello比hello.o多了程序头,一共有八个段,描述了可执行文件的连续的片映射到连续的内存段的映射关系。包括目标文件的偏移(Offset)、段的读写/执行权限(Flags)、内存的开始地址(VirtAddr)、对齐要求(Align)、段的大小(FileSiz)、内存中的段大小(MemSiz)等。
在这里插入图片描述

图 5-6 程序头
4)重定位节
在hello中,原来的.rela.text节已经没有了,说明链接的过程已经完成了对.rela.text的重定位操作。Hello中出现了新的重定位条目。这些重定位条目都和共享库中的函数有关,因为此时还没有进行动态链接,共享库中函数的确切地址仍是未知的,因此仍然需要重定位节,在动态链接后才能确定地址。
在这里插入图片描述

图 5-7 重定位节
5)段节:
在这里插入图片描述

图 5-8 段节
6)符号表
符号表存放程序中定义和引用的函数和全局变量的信息,每个符号表是一个条目的数组,每个条目包括value:距定义目标的节的起始位置的偏移;size:目标的大小;type:指明数据还是函数;bind:表示符号是本地的还是全局的等等。
在这里插入图片描述

图 5-9 符号表
在这里插入图片描述

图 5-10 符号表
hello中还多出了一个动态符号表,表中都是外部共享库中的函数,需要动态链接。
在这里插入图片描述

图 5-11 动态符号表
5.4 hello的虚拟地址空间
使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。
在这里插入图片描述

图 5-12 edb运行命令
打开edb后加载hello,在data dump中可以查看加载到虚拟地址的情况
在0x401000~0x402000段中,程序被载入,对应5.3程序头中LOAD段的第二个load,标记为R E,是只读可执行的代码段。同样利用各个节的偏移量可以找到每个节对应的虚拟地址,如.plt.sec 的地址为0000000000401090。
在这里插入图片描述

图 5-13 data dump

可以看出,段的虚拟空间从0x400000开始,这与5.3程序头中LOAD段的起始地址是相合的。
在这里插入图片描述

图 5-14 data dump
5.5 链接的重定位过程分析
objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。
结合hello.o的重定位项目,分析hello中对其怎么重定位的。
在这里插入图片描述

图 5-15 反汇编
1)对比hello.o可以发现,hello的反汇编不止有.text节,还有.init、.plt、.plt.sec、.fini且每个节中还有很多函数。说明各个库函数的代码已经链接到程序中。
2)hello的汇编代码使用了虚拟地址,而hello.o都是从0开始。
3)涉及调用外部库函数的代码发生改变,在之前汇编的过程中,汇编器遇到对最终位置未知的目标引用,会产生一个重定位条目,告诉链接器在将目标文件合并成可执行文件时如何修改这个引用,hello直接用了虚拟地址。
从这些不同中我们就能大抵看出重定位是如何进行的,下面以第一个重定位条目.rodata为例。
首先在hello.o的反汇编中找到相应位置:
在这里插入图片描述

图 5-16
对比源程序,这条指令应该是向printf函数传入参数,对应字符串常量,但是它的最终位置未知,因此产生重定位条目,地址还是0。重定位就需要将地址修改为它的最终地址,传给rdi。由第四章可知,该条目相对.text的偏移量为00000000001c。
注释已经说明,这条指令的段地址其实就是main函数的段地址加上20,而main 函数地址重定位后为401125,加上20应该是40113e。而字符串常量的地址为0xec3,重定位后指令的偏移地址就变为48 8d 3d c3 0e(小端序)。在hello的反汇编中找到相应位置,发现确实如此。
在这里插入图片描述

图 5-17
5.6 hello的执行流程
使用edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程。请列出其调用与跳转的各个子程序名或程序地址。
加载hello后依次执行如下子程序:
ld-2.23.so!_dl_start 0x7f26382bddf0
__libc_csu_init 0x4011c0
ld-2.23.so! dl_init 0x7f26382cdc10
_start 0x4010f0
__libc_start_main@GLIBC_2.2.5 0x403ff0
main 0x401125
puts@plt 0x401090
exit@plt 0x4010d0
printf@plt 0x4010a0
atoi@plt 0x4010c0
sleep@plt 0x4010e0
getchar@plt 0x4010b0
__libc_csu_fini 0x401230
_fini 0x401238

5.7 Hello的动态链接分析

分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的内容变化。要截图标识说明。
动态链接涉及到两个数组:PLT和GOT
PLT:PLT是一个数组,其中每个条目是16字节代码。PLT[0]是一个特殊条目,它跳转到动态链接器中。每个被可执行程序调用的库函数都有它自己的PLT条目。每个条目都负责调用一个具体的函数。
GOT:GOT是一个数组,其中每个条目是8字节地址。和PLT联合使用时,GOT[O]和GOT[1]包含动态链接器在解析函数地址时会使用的信息。GOT[2]是动态链接器在1d-linux.so模块中的入口点。其余的每个条目对应于一个被调用的函数,其地址需要在运行时被解析。每个条目都有一个相匹配的PLT条目。
在这里插入图片描述

图 5-16
由图可知,.got.plt节开始位置是0x404000,GOT[1]和GOT[2]这两个条目初始时均为0。

使用edb调试,当dl_start函数返回后,发现这两个条目被修改为正确的值。可可见,在函数第一次被调用时,动态链接器会修改相应的GOT条目。

5.8 本章小结
本章介绍了链接的概念与作用,分析了可执行目标文件hello的ELF格式、hello的虚拟地址空间、链接的重定位过程以及hello执行过程。
(第5章1分)

第6章 hello进程管理

6.1 进程的概念与作用
概念:一个正在运行的程序的实例。
作用:系统中的每个程序都运行在某个进程的上下文中。每次用户通过向shell输入一个可执行目标文件的名字,运行程序时,shell就会创建一个新的进程,然后在这个新进程的上下文中运行这个可执行目标文件。
6.2 简述壳Shell-bash的作用与处理流程
作用:shell是一个交互型应用程序,代表用户运行其他程序;同时它也是一种程序设计语言。shell应用程序提供了一个界面,用户通过访问这个界面访问操作系统内核的服务。
处理流程:
1)从终端读入输入的命令。
2)将输入字符串切分获得所有的参数
3)如果是内置命令则立即执行
4)否则调用相应的程序执行
5)shell 应该接受键盘输入信号,并对这些信号进行相应处理
6.3 Hello的fork进程创建过程
用户在shell输入./hello后,由于hello不是内置命令,shell会将其视为可执行文件,于是在路径中寻找到hello文件后就执行之。shell通过fork函数创建出一个新的子进程,子进程得到与父进程用户级虚拟地址空间相同的(但是独立的)一份副本,包括代码和数据段、堆、共享库以及用户栈.子进程进程还获得与父进程任何打开文件描述符相同的副本,但子进程有不同于父进程的PID。

在这里插入图片描述

6.4 Hello的execve过程
execve 函数在当前进程的上下文中加载并运行一个新程序。该函数加载并运行可执行目标文件filename(hello), 且带参数列表argv 和环境变量列表envp 。只有当出现错误时,如指定的文件不存在, execve才会返回到调用程序。所以execve 调用一次并从不返回,与fork一次调用返回两次不同。
在新创建的子进程中,execve加载了hello之后,它调用启动代码,启动代码设置栈,并将控制传递给新程序的主函数。随后main开始执行。
6.5 Hello的进程执行
结合进程上下文信息、进程时间片,阐述进程调度的过程,用户态与核心态转换等等。
先阐述几个概念:
上下文信息:上下文就是内核重新启动一个被抢占的进程所需要的状态,它由通用寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构等对象的值构成。
进程时间片:一个进程执行它的控制流的一部分的每一时间段叫做时间片。
用户态与核心态:处理器通常使用一个寄存器提供两种模式的区分,该寄存器描述了进程当前享有的特权,当没有设置模式位时,进程就处于用户模式中,用户模式的进程不允许执行特权指令,也不允许直接引用地址空间中内核区内的代码和数据;设置模式位时,进程处于内核模式,该进程可以执行指令集中的任何命令,并且可以访问系统中的任何内存位置。

初始时,没有设置模式位,hello进程运行在用户模式中。hello运行的时候还有许多进程并发运行,hello调用sleep,引发系统调用,进程从用户模式转换为内核模式,内核处理休眠请求主动释放当前进程,并将hello进程从运行队列中移出加入等待队列,定时器开始计时,内核进行上下文切换将当前进程的控制权交给其他进程,当前进程(hello)进入休眠。当定时器到时时发送一个中断信号,此时进入内核状态执行中断处理,将hello进程从等待队列中移出重新加入到运行队列,成为就绪状态,hello进程继续进行自己的控制逻辑流。

6.6 hello的异常与信号处理
hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。
1)异常
异常名 原因 类别 处理结果
中断 处理器外部I/O设备引起 异步 返回到下一条指令
陷阱 有意的,执行指令的结果 同步 返回到下一条指令
故障 非有意的可被修复的错误 同步 修复并重新执行或终止指令
终止 非故意,不可恢复的致命错误 同步 中止当前程序
2)信号
可能产生的信号:SIGINT,SIGTSTP,SIGCHLD,SIGKILL,SIGALRM等等,接受到每个信号都有对应的预定义的默认行为
3)实例
1.按回车:getchar()会读入回车并把第一个回车之前的字符当作输入shell的命令,其余回车作为新的输入shell的命令,对hello的执行没有影响。
在这里插入图片描述

图 6-1
2.Ctrl-Z:发送SIGTSTP信号给前台进程组的每个进程,导致停止前台作业,也就是停止hello进程。
在这里插入图片描述

图 6-2

使用ps命令可以查看当前所有进程以及它们的PID
在这里插入图片描述

图 6-3
在这里插入图片描述

图 6-4
在这里插入图片描述

图 6-5
使用fg命令可以使停止的hello进程继续在前台运行
在这里插入图片描述

图 6-6
在这里插入图片描述

图 6-7

3.Ctrl-C:终止进程
在这里插入图片描述

图 6-8
6.7本章小结
本章介绍了进程的概念和作用,shell的工作过程,同时分析了使用fork以及execve函数加载运行hello,执行hello进程以及hello进程运行时的异常/信号处理过程。
(第6章1分)

第7章 hello的存储管理

7.1 hello的存储器地址空间
逻辑地址:程序经过编译后出现在汇编代码中的地址(在hello反汇编结果可以看到)。逻辑地址用来指定一个操作数或者是一条指令的地址。是由一个段标识符加上一个指定段内相对地址的偏移量。
线性地址/虚拟地址:逻辑地址经过段机制后转化为线性地址,是逻辑地址到物理地址变换之间的中间层。在分段部件中逻辑地址是段中的偏移地址,然后加上基地址就是线性地址。
物理地址:计算机系统的主存被组织成一个由M个连续的字节大小的单元组成的数组。每个字节都有唯一的物理地址。虚拟地址通过页表的方式通过MMU映射到物理地址中。
7.2 Intel逻辑地址到线性地址的变换-段式管理
分段系统的逻辑地址结构由段号(段名)和段内地址(段内偏移量)所组成。在段式存储管理中,将程序的地址空间划分为若干个段,这样每个进程有一个二维的地址空间。段号是一个16位长的字段组成,称为段选择符,其中前13位是一个索引号。后面三位包含一些硬件细节。Base(基地址)字段,表示的是包含段的首字节的线性地址,也就是一个段的开始位置的线性地址。
1)给定一个完整的逻辑地址[段选择符:段内偏移地址];
2)看段选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地址和大小;
3)取出段选择符中前13位,在这个数组中,查找到对应的段描述符,这样,基地址就知道了。把Base + offset,就是要转换的线性地址了。
7.3 Hello的线性地址到物理地址的变换-页式管理
虚拟内存必须有某种方法来判定一个虚拟页是否缓存在DRAM中的某个地方,还必须确定虚拟页在哪个物理页中。CPU通过将逻辑地址转换为虚拟地址来访问主存,这个虚拟地址在访问主存前必须先转换成适当的物理地址。CPU芯片上叫做内存管理单元(MMU)的专用硬件,利用存放在主存中的查询表来动态翻译虚拟地址。然后CPU会通过这个物理地址来访问物理内存。
MMU利用VPN(虚拟页号)来在虚拟页表中选择合适的PTE,将页表中的物理页号(PPN)和虚拟地址中的VPO串联起来,就得到相应的物理地址。因为物理和虚拟页面都是P字节的,所以物理页面偏移和VPO是相同的。
若页命中:
1)处理器生成一个虚拟地址,并把它传给MMU。
2)MMU生成PTE地址,并从高速缓存/主存请求得到它。
3)高速缓存/主存向MMU返回PTE。
4)MMU构造物理地址,并把它传送给高速缓存/主存。
5)高速缓存/主存返回所请求的数据字给处理器。
若缺页:
1)-3)与命中相同。
4)PTE中的有效位是零,MMU触发了一次异常,传递CPU中的控制到操作系统内核中的缺页异常处理程序。
5)缺页处理程序确定出物理内存中的牺牲页,如果这个页面已经被修改了,则把它换出到磁盘。
6)缺页处理程序页面调入新的页面,并更新内存中的PTE。
7)缺页处理程序返回到原来的进程,再次执行导致缺页的指令。CPU将引起缺页的虚拟地址重新发送给MMU。因为虚拟内存现在缓存在物理内存中,所以就会命中,在MMU执行了相应步骤之后,主存就会将所请求字返回给处理器。
7.4 TLB与四级页表支持下的VA到PA的变换
TLB:翻译后背缓冲器。TLB是一个小的、虚拟寻址的缓存,其中每一行都保存着一个由单个PTE组成的块。
多级页表:多级页表可以用来压缩页表,对于k级页表层次结构,虚拟地址的VPN被分为k个。
Intel Core i7,使用了4级页表,36位VPN被分成四个9位的片,每个片被用作到一个页表的偏移量。CR3寄存器包含L1页表的物理地址。VPN1提供一个L1PET的偏移量,这个PTE包含L2页表的基地址。VPN2提供一个到L2PTE的偏移量,以此类推。
7.5 三级Cache支持下的物理内存访问
当MMU完成了从虚拟地址到物理地址的转换后,就可以使用物理地址进行内存访问了。Intel Core i7使用了三级cache来加速物理内存访问,L1级cache作为L2级cache的缓存,L2级cache作为L3级cache的缓存,而L3级cache作为内存(DRAM)的缓存。获得了物理地址VA之后,使用CI(倒数7-12位)进行组索引,每组8路,对8路的块分别匹配CT(前40位)如果匹配成功且块的valid标志位为1,则命中,根据数据偏移量CO(后6位)取出数据返回.
如果没有匹配成功或者匹配成功但是标志位是1,则不命中,向下一级缓存中查询数据,查询到数据之后,用局部性原理作为i简单的放置策略。值得注意的是,三级cache不仅仅支持数据指令的访问,也支持页表条目的访问,在MMU进行虚拟地址到物理地址的翻译过程中,三级cache也会起作用。
7.6 hello进程fork时的内存映射
当fork函数被当前进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的PID。为了给这个新进程创建虚拟内存,它创建了当前进程的mm_struct、区域结构和页表的原样副本。它将两个进程中的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。
当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork 时存在的虚拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面,为每个进程保持了私有地址空间的抽象概念。
7.7 hello进程execve时的内存映射
execve函数在当前进程中加载并运行包含在hello中的程序,加载并运行hello.out需要以下几个步骤:
1)删除已存在的用户区域。
2)映射私有区域。
3)映射共享区域。Hello.out与共享对象链接,这些对象都是动态链接到这个程序的,然后再映射到用户虚拟地址空间中的共享区域内。
4)设置程序计数器(PC)。execve做的最后一件事就是设置当前进程上下文中的程序计数器,使之指向代码区域的入口点。
下一次调度这个进程时,它将从这个入口点开始执行。Linux将根据需要换入代码和数据页面。
7.8 缺页故障与缺页中断处理
缺页故障:用户在写内存地址时该地址对应的物理页不在内存中,在磁盘中,说明虚拟地址对应的内容还没有缓存在内存中,这时MMU会触发缺页故障。
处理:
1)缺页处理程序搜索区域结构的链表,判断虚拟地址是否合法。
2)判断试图进行的内存访问是否合法。
3)选择一个牺牲页面,如果这个牺牲页面被修改过,那么就将它交换出去,换入新的页面并更新页表。当缺页处理程序返回时,CPU重新启动引起缺页的指令,这条指令将再次发送虚拟地址到MMU。

7.9动态存储分配管理
Printf会调用malloc,请简述动态内存管理的基本方法与策略。
基本原理:动态内存分配器维护着一个进程的虚拟内存区域,称为堆。不失通用性,假设堆是一个请求二进制零的区域,它紧接在未初始化的数据区域后开始,并向上生长。对每个进程,内核维护着一个变量brk,它指向堆的顶部。分配器将堆视为一组不同大小的块的集合来维护。每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显示地保留为供应用程序使用。空闲块可用来分配。空闲块保持空闲,直到它显示地被应用所分配。一个已分配的块保持已分配状态,直到它被释放。
放置策略:
1)首次适配。从头开始搜索空闲链表,选择第一个合适的空闲块。
2)下一次适配。从上一次查询结束的地方开始。
3)最佳适配。检查每个空闲块,选择适合所需请求大小的最小空闲块。
7.10本章小结
本章主要介绍了hello的存储器地址空间、段式管理、页式管理;TLB与四级页表支持下的VA到PA的变换、三级cache支持下物理内存访问, hello进程fork时的内存映射、execve时的内存映射、缺页故障与缺页中断处理、动态存储分配管理等内容。
(第7章 2分)

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:所有的IO设备都被模型化为文件,而所有的输入输出都被当作对相应文件的读和写来执行。
设备管理:将设备优雅地映射为文件的方式,允许Linux 内核引出一个简单、低级的应用接口,称为Unix I/O,这使得所有的输入和输出都能以一种统一且一致的方式来执行,这就是Unix I/O接口。
8.2 简述Unix IO接口及其函数
Unix IO接口:
打开文件,内核返回一个非负整数的文件描述符,通过对此文件描述符对文件进行所有操作。
Linux shell创建的每个进程开始时都有三个打开的文件:标准输入(文件描述符0)、标准输出(描述符为1),标准出错(描述符为2)。头文件<unistd.h>定义了常量STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO,他们可用来代替显式的描述符值。
改变当前的文件位置,文件开始位置为文件偏移量,应用程序通过seek操作,可设置文件的当前位置为k。
读写文件,读操作:从文件复制n个字节到内存,从当前文件位置k开始,然后将k增加到k+n;写操作:从内存复制n个字节到文件,当前文件位置为k,然后更新k
关闭文件。当应用完成对文件的访问后,通知内核关闭这个文件。内核会释放文件打开时创建的数据结构,将描述符恢复到描述符池中。
Unix I/O函数:

open 函数:打开一个已存在的文件或者创建一个新文件的
函数原型:int open(char *filename, int flags, mode_t mode);
open 函数将filename 转换为一个文件描述符,并且返回描述符数字.返回的描述符总是在进程中当前没有打开的最小描述符.flags 参数指明了进程打算如何访问这个文件,mode 参数指定了新文件的访问权限位.返回:若成功则为新文件描述符,若出错为-1.
close 函数:关闭一个打开的文件.
函数原型:int close(int fd);
返回:若成功则为0, 若出错则为-1.
3.read 和write 函数:执行输入和输出的.
ssize_t read(int fd, void *buf, size_t n);
read 函数从描述符为fd 的当前文件位置复制最多n 个字节到内存位置buf .返回值-1表示一个错误,而返回值0 表示EOF.否则,返回值表示的是实际传送的字节数量.
返回:若成功则为读的字节数,若EOF 则为0, 若出错为-1.
ssize_t write(int fd, const void *buf, size_t n);
write 函数从内存位置buf 复制至多n 个字节到描述符fd 的当前文件位置.图10-3 展示了一个程序使用read 和write 调用一次一个字节地从标准输入复制到标准输出.返回:若成功则为写的字节数,若出错则为-1。
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码,直到接受到回车键才返回。
结论

我们由I/O设备编写hello.c程序,经过预处理、编译、汇编、链接等阶段先后产生hello.i、hello.s、hello.o、hello文件,可执行目标文件hello的诞生了。然后我们在shell中输入./hello指令,bash将从父进程中fork一个子进程,清空当前进程数据并加载hello,从函数入口进入,若出现计时器中断等情况,进程将暂时停止,当前进度会被保留并切换上下文,内核处理别的进程。我们还可以键入信号来终止或挂起hello程序。当执行到exit函数后父进程回收子进程,内核删除所有的数据。
计算机系统的设计与实现蕴含着极大的智慧,学习计算机系统对提高一个程序员的水平有着巨大的意义。
(结论0分,缺失 -1分,根据内容酌情加分)

附件

列出所有的中间产物的文件名,并予以说明起作用。
在这里插入图片描述

hello.i:hello.c经预处理得到的ASCII码的中间文件
hello.s:hello.i编译之后得到的一个ASCII汇编语言文件
hello.o:hello.s汇编之后得到的一个可重定位目标文件
hello:hello.s和标准的C库进行链接得到的可执行目标文件
helloelf.txt:hello.o的elf格式文件
hello.elf:hello的elf格式文件
(附件0分,缺失 -1分)

参考文献

[1] 辛希孟. 信息技术与信息服务国际研讨会论文集:A集[C]. 北京:中国科学出版社,1999.
[2]https://www.cnblogs.com/Sna1lGo/p/15786602.html
[3]https://www.csdn.net/tags/MtTacgzsMTc3NTgtYmxvZwO0O0OO0O0O.html
[4]https://zhuanlan.zhihu.com/p/419683114
[5]Randal E.Brtant,David R.O’Hallaron 深入理解计算机系统. 机械工业出版社,2016.

(参考文献0分,缺失 -1分)

《计算机应用基础》形成性考核参考答案 模块1 " Windows 7 操作系统" 得参考答案(客观题) 1、以微处理器为核心组成得微型计算机属于集成电路计算机。 2、第一台电子计算机诞生于1946。 3、第一台电子计算机使用得主要逻辑元件就是电子管。 4、一个完整得计算机系统应当包括硬件系统与软件系统。 5、操作系统就是一种系统软件. 6、某单位得人事档案管理程序属于应用软件。  7、操作系统得作用就是控制与管理系统资源得使用。 8、计算机硬件构成计算机得物理实体. 9、微型计算机中运算器得主要功能就是进行算术与逻辑运算。 10、下列设备中,显示器属于输出设备。 11、微机得核心部件就是微处理器.  12、MPU就是微型计算机得运算器、控制器及内存储器统称。 13、CD—ROM就是只读光盘。 14、下列不属于计算机应用范畴得就是水产捕捞。 15、激光式打印机得特点就是速度快、印字质量好,噪音低. 16、目前使用得防杀病毒软件得作用就是检查计算机就是否感染病毒,消除部已感染病 毒.  17、1MB= 1024KB 18、下列中最小得就是(75)8 19、云计算就是将网络资源集中管理与调度,并以虚拟化方式为用户提供服务得。 20、简单得说,物联网就是通过信息传感设备将物品与互联网相连接,以实现对物品进行 智能化管理得网络. 21、世界上第一台计算机名为ENIAC 22、世界上第一台电子计算机采用得主要逻辑元件就是电子管 。 23、简单地说,计算机就是一种不许人为干预,由能自动完成各种算术与逻辑运算得工具 。 24、计算机辅助教学得英文缩写得就是CAI、 25、运算器与控制器得集合体称CPU 、 26、能接收与保存据及程序得装置称为存储器 。 27、所有指令得有序集合叫程序 . 28、将2个以上得CPU整合在一起称为多核处理器 29、内存储器相对于外存储器得特点就是容量小、速度快 。 302009年,Tilera公司发不了全球第一款100核微处理器  31、计算机最基本得输入设备就是键盘 。 32、微机系统最基本得输出设备就是显示器 . 33、在系统软件中,必须首先配置操作程序 。 34、多媒体技术,就是指使用计算机综合处理多种媒体信息,使多种信息建立逻辑连接 ,集成为一个系统并具有交互性得技术。 35、对网络系统而言,信息安全主要包括信息得存储安全与信息得传输安全。  36、计算机病毒一般包括引导、传染部分与表现3个部分。 37、在汉字系统中,由键盘输入得汉字拼音码属于音码 38、基本ASCII码就是7位字符编码 、 39、字节与字长得关系就是字长为字节得整倍 . 40、二进制得计方式就是逢二进一.  41、Windows7就是一种单用户/多任务得操作系统. A 单任务  B 单用户 C网络 D  42、在Windows7中,若要运行一个指定程序,应使用开始菜单中得"运行"命令。 43、Windows7提供了一种剪贴板技术,以方便进行应用程序间信息得复制或移动等信 息交换。 44、关闭Windows7相当于关闭计算机. 45、按住鼠标器左钮同时移动鼠标器得作用称为拖拽。 46、在Windows7中,显示在应用程序窗口最顶部得称为标题栏。 47、在Windows7中,应用程序窗口标题栏颜色得变化可区分窗口与非活动窗口。  48、右击任务栏中得任务按钮,可以弹出相应得快捷菜单。 49、在Windows7中,控制面板就是操作系统得控制管理中心。 50、在资源管理器中,当删除一个或一组文件夹时,该文件或该文件夹组下得所有子 文件夹及其所有文件将被删除. 51、在资源管理器中,单击第一个文件名后,按住Shift键,再单击最后一个文件,可 选定一组连续得文件. 52、在资源管理器中,若想格式化一个磁盘分区,应右击该盘符并选定"格式化"命令。 53、在Windows7中,启动中文输入法或切换中文输入方式到英文方式,应同时按下 Ctrl+空格。 54、在资源管理器中,工具菜单项提供了文件夹设置功能。 55、在资源管理器中,选定文件或文件夹后,按住Ctrl键,再拖拽到指定位置,可完成 复制文件或文件夹得操作. 56、在Windows7资源管理器中,如果工具栏未显示,可以单击组织按钮进行设置。 57、在Windows7中,任务栏可以显示在屏幕任一边 58、使用屏幕保护程序,就是为了延长显示器使用寿命. 59、磁盘清理得主要作用就是进行文件清理并释放磁盘空间。 60、死机就是指出现鼠标停滞/键盘无法输入等现象。 61、Windows7操作系统允许同时运行 多 个应用程序。 62、关闭Windows7相当于关闭 计算机   63、Windows7得Aero桌面,具有透明得玻璃图案效果。 64、在Windows7中,利用剪贴板,可以方便地在应用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值