哈工大csapp 大作业 hello的一生

计算机系统 大作业

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

计算机科学与技术学院
2019年12月

摘 要

本文通过对hello预处理、编译、汇编、链接的过程以及在此过程中生成的文件等进行深入研究,了解到了一个程序是如何从开始到结束的,这便是hello的一生。

关键词:预处理、编译、汇编、链接

目 录

第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的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。
Linux下,hello.c经过c预处理器进行预处理、编译器编译为汇编代码、汇编器汇编转化为二进制目标代码文件、链接器进行链接产生最终的可执行代码。在bash输入命令后,bash调用fork函数为可执行代码创建子进程(process)这就是hello的P2P过程;
然后shell为其execve映射虚拟内存。进入程序入口后,程序开始加载物理内存,然后进入main函数执行目标代码。 CPU为正在运行的hello分配时间片以执行逻辑控制流。程序完成后,shell父进程负责恢复hello进程,内核删除相关的数据结构这就是hello的020过程;

1.2 环境与工具
硬件环境:X64 CPU 8G Hz 8G RAM 1T HDD
软件环境:Windows 10 & VirtualBox + Ubuntu 18.04 64位;
开发与调试工具:visual studio 2019 & objdump,gdb,edb,hexedit;
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
hello.c 用C语言编写的hello程序源代码
hello.i 经预处理处理得到的文本文件
hello.s 编译后得到的汇编代码
hello.o 汇编后得到的可重定位目标执行文件
hello.oasm hello.o的反汇编文件
hello链接后得到的二进制文件
1.4 本章小结

(第1章0.5分)

第2章 预处理
2.1 预处理的概念与作用
预处理概念:
一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程,即在编译之前进行的处理,C语言的预处理主要有三个方面的内容: 1.宏定义; 2.文件包含; 3.条件编译。 预处理命令以符号“#”开头。
预处理作用:C预处理器扩展源代码,插入所有用#include命令指定的文件,并扩展所有用#define声明指定的宏。
2.2在Ubuntu下预处理的命令
Linux>gcc -E hello.c -o hello.i(如图1)
在这里插入图片描述
图1:ubuntu下预处理的命令及目标文件展示

2.3 Hello的预处理结果解析
可以看到代码从20多行扩展到了3000+行,文件前部分加入了许多头文件里的东西,原先的代码放在了文件末尾。
在这里插入图片描述
在这里插入图片描述
图2:预处理文件hello.i展示

2.4 本章小结
本章对预处理概念作了略微了解,并根据对hello.c进行预处理得到的hello.i的解析,了解到预处理实际上是在编译之前将头文件中的宏定义、变量定义等插入到文本中。

(第2章0.5分)

第3章 编译
3.1 编译的概念与作用
编译的概念:就是把代码转化为汇编指令的过程,汇编指令只是CPU相关的,也就是说C代码和python代码,代码逻辑如果相同,编译完的结果其实是一样的。
编译的作用:将源代码转换为汇编代码
注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序
3.2 在Ubuntu下编译的命令
Linux>gcc -S hello.i -o hello.s
在这里插入图片描述
图3:编译hello.i生成hello.s

3.3 Hello的编译结果解析
3.3.0 开头
.file:源文件名
text:代码段
.globl:全局变量
.data:数据段
.align:对齐方式
.type:指定是对象类型或是函数类型
.size:大小
.long:长整型
.section .rodata:下面是.rodata节
.string:字符串

在这里插入图片描述
图4:hello.s头

3.3.1数据
1)常量

如图5所示:Hello.s中常量均以立即数的形式出现
在这里插入图片描述

图5:hello.s部分汇编代码
2)变量
如图4、5可以看到,hello.s 中的变量有全局变量、局部变量、指针变量
整形变量:sleepsecs、argc、i
① 图5中显示:sleepsecs被声明 为globl类型,并且为对象,占据4个字节被保存在.rodata段;
② 图4中显示,main函数参数argc被%edi保存,并储存在-20(%rbp)。
③图6中显示,变量i以0为初始值, 其地址被保存在-4(%rbp),每循环一次就加1

指针变量:argv作为第二个参数%rsi被保存在-32(%rbp)中。

在这里插入图片描述
图6:for循环

3)类型:如图5所示,sleepsecs被定义为long类型

3.3.2 赋值
Movx (x为q或l)
两个赋值语句:图5中.long 2表示sleepsecs被赋值为long类型的2;
图6中第36行movl $0, -4(%rbp)表示i被赋值为0;

3.3.3 类型转换
int sleepsecs=2.5; 为隐式类型转换,2.5为double型,在图5中sleepsecs实际上被赋值为long类型的2,符合浮点数转换为整型遵循的向零舍入的原则

3.3.4 算术操作
i++:如图6所示,第52行addl $1, -4(%rbp)即为i++的操作,addl表示操作数大小为4字节
leaq .LC1(%rip), %rdi:使用了加载有效地址指令leaq计算LC1的段地址%rip+.LC1并传递给%rdi

3.3.5 关系操作
表达式:argc !=3; i < 10;
①如图5所示,第29行进行了argc !=3表达式的转换
②如图6所示,第54行进行了 i < 10的表达式转换

3.3.6 控制转移
if(): 如图5所示29行、30行即为if语句
for(;😉: 如图6所示 35行.L2:内为i = 0;.L4内为for执行代码,52行为i++;.L3内为i < 10循环条件语句

3.3.7 函数操作

1.main函数:
参数传递: argc和argv,分别保存在%rdi和%rsi。
函数调用:被系统启动。
函数返回:将%eax设置为0并且返回,对应于return 0 。
2.printf函数:
参数传递:call puts时只传入了字符串参数首地址;for循环中call printf时传 入了 argv[1]和argc[2]的地址。
函数调用:for循环中被调用
3.exit函数:
参数传递:传入的参数为1,再执行退出命令
函数调用:if判断条件满足后被调用
4.sleep函数:
参数传递:传入参数sleepsecs,
传递控制:call sleep
函数调用:for循环下被调用
5.getchar函数
传递控制:call getchar
函数调用:在main中被调用
3.4 本章小结
本章主要对汇编代码hello.s从变量、操作、控制、函数等进行解析,对汇编代码有了进一步的了解。
汇编代码已经与高级语言编写的代码有了很大的不同,阅读起来也相对麻烦,但是它是转换为机器代码所必须经历的阶段,越到底层就越明白为何我们要学习高级语言。
(第3章2分)

第4章 汇编
4.1 汇编的概念与作用
汇编的概念:
汇编器(as)将汇编语言书写的程序翻译成与之等价的机器指令的过程,它把这些指令打包成可重定位目标程序的格式,并将结果保存在目标文件hello.o中。hello.o文件是一个二进制文件,它包含的是程序的指令编码。

注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。
4.2 在Ubuntu下汇编的命令
Linux>gcc -c hello.s -o hello.o
在这里插入图片描述
图7:hello.o文件展示

4.3 可重定位目标elf格式
输入Linux> readelf -h hello.o查看ELF头信息
在这里插入图片描述
图8:hello.o的ELF头信息
由图8可知,hello.o是可重定位文件,采用补码、小端序。

输入Linux>readelf -s hello.o查看hello.o节头信息
在这里插入图片描述
图9:hello.o节头信息

输入Linux>readelf -s hello.o查看符号表
在这里插入图片描述
图10:hello.o符号表

输入Linux>readelf -r hello.o查看重定位信息
在这里插入图片描述
图11:hello.o重定位信息

由图8至图11可知Hello.o的elf格式由elf头、节头、重定位信息、符号表等组成,

4.4 Hello.o的结果解析
objdump -d -r hello.o 分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。

输入命令 Linux>objdump -d -r hello.o > hello.oasm得到hello.s的反汇编文本
在这里插入图片描述
图12:hello.o的反汇编代码

为了便于比对,这里我们再放上hello.s的main函数部分
在这里插入图片描述
图13:hello.s的main部分
机器语言实际上就是对汇编语言的编码,每一条汇编代码都有唯一的机器级编码

由图12、图13可以发现
1.进制:在hello.oasm中,立即数以十六进制出现,在hello.s中,立即数以10进制出现,
2.分支转移:在hello.oasm中,没有像hello.s中.L1, .L2, .L3等那样的跳转目标名称,而是以指令的地址为跳转。
3.函数调用:在hello.oasm中,函数调用的目标地址是当前指令的下一条指令的地址,在hello.s中,函数调用直接使用函数名称
4.全局变量:在hello.oasm中,使用0x0(%rip)进行访问,在hello.s中,使用<段名称>(%rip)进行访问。

4.5 本章小结
本章对elf文件格式进行了研究,介绍了汇编代码的概念、结构、作用,对汇编与反汇编代码进行了比较。
(第4章1分)

第5章 链接
5.1 链接的概念与作用
链接的概念:
链接时将各种代码和数据片段收集并组合成为一个单一的文件的过程,这个文件可被加载(复制)
注意:这儿的链接是指从 hello.o 到hello生成过程。
5.2 在Ubuntu下链接的命令
输入命令Linux>ld -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 /usr/lib/gcc/x86_64-linux-gnu/7/crtbegin.o /usr/lib/gcc/x86_64-linux-gnu/7/crtend.o /usr/lib/x86_64-linux-gnu/crtn.o hello.o -lc -z relro -o hello
在这里插入图片描述
图14:ld链接hello.o

使用ld的链接命令,应截图,展示汇编过程! 注意不只连接hello.o文件
5.3 可执行目标文件hello的格式
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。

在这里插入图片描述

图15:hello的elf头

在这里插入图片描述
图16:hello的节头信息
在这里插入图片描述
图17:hello的程序头

其余格式与内容皆与4.3相当,在此不一一赘述。
5.4 hello的虚拟地址空间
使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。
5.5 链接的重定位过程分析
(以下格式自行编排,编辑时删除)
objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。
结合hello.o的重定位项目,分析hello中对其怎么重定位的。
5.6 hello的执行流程
(以下格式自行编排,编辑时删除)
使用edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程。请列出其调用与跳转的各个子程序名或程序地址。
5.7 Hello的动态链接分析
(以下格式自行编排,编辑时删除)
分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的内容变化。要截图标识说明。
5.8 本章小结
链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行于编译时, 也就是在源代码被翻译成机器代码时;也可以执行于加载时, 也就是在程序被加载器加载到内存并执行时;甚至执行于运行时, 也就是由应用程序来执行。在早期的计算机系统中,链接是手动执行的。在现代系统中,
链接是由叫做链接器的程序自动执行的。
链接器在软件开发中扮演着一个关键的角色,因为它们使得分离编译成为可能。我们不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,不必重新编译其他文件
(第5章1分)

第6章 hello进程管理
6.1 进程的概念与作用
进程的概念:
进程的经典定义就是一个执行中程序的实例。进程是系统进行资源分配和调度的基本单位,是操作系统结构的基础。系统中的每个程序都运行在某个进程的上下文中,上线爱温室由程序正确运行的状态组成的,包括存放在内存中的程序的代码和数据,它的栈,通用目的寄存器的内存,程序计数器,环境变量以及打开文件描述符的集合。
进程的作用:
进程提供给应用程序两个关键的抽象。一个独立的逻辑控制流,他提供一个假象,好像我们的程序独占的使用处理器。一个私有的地址空间,它提供一个假象,好像我们的程序独占的使用内存系统。
6.2 简述壳Shell-bash的作用与处理流程
Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。
Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
bash,全称为Bourne-Again Shell。它是一个为GNU项目编写的Unix shell。bash脚本功能非常强大,尤其是在处理自动循环或大的任务方面可节省大量的时间。bash是许多Linux平台的内定Shell。
处理流程:
1 新建文件test.sh
$ touch test.sh
2 添加可执行权限
$ chmod +x test.sh
3 编辑test.sh,test.sh内容如下:
#!/bin/bash
echo “hello bash”
exit 0
6.3 Hello的fork进程创建过程
Shell 调用fork函数为hello创建子进程
6.4 Hello的execve过程
Shell调用execve函数加载hello进程,映射虚拟内存。
6.5 Hello的进程执行
结合进程上下文信息、进程时间片,阐述进程调度的过程,用户态与核心态转换等等。
一开始,Hello运行在用户模式,当程序收到一个信号时,进入内核模式,运行信号处理程序,之后再返回用户模式。在Hello运行的过程中,cpu不断切换上下文,使Hello程序运行过程被切分成时间片,与其他进程交替占用cpu,实现进程的调度
6.6 hello的异常与信号处理
hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
hello 执行过程中可能出现四类异常:中断、陷阱、故障和终止。

  1. 中断是来自 I/O 设备的信号,异步发生,中断处理程序对其进行处理,返
    回后继续执行调用前待执行的下一条代码,就像没有发生过中断。
  2. 陷阱是有意的异常,是执行一条指令的结果,调用后也会返回到下一条指
    令,用来调用内核的服务进行操作。帮助程序从用户模式切换到内核模式。
  3. 故障是由错误情况引起的,它可能能够被故障处理程序修正。如果修正成
    功,则将控制返回到引起故障的指令,否则将终止程序。
  4. 终止是不可恢复的致命错误造成的结果,通常是一些硬件的错误,处理程
    序会将控制返回给一个 abort 例程,该例程会终止这个应用程序。

程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行截屏,说明异常与信号的处理。

接下来分析 hello 对各种信号的处理:

在这里插入图片描述

图18:运行hello按下ctrl+c

在这里插入图片描述

图19:运行hello按下ctrl+z

在这里插入图片描述

图20:运行hello按下ctrl+z后按下ps

在这里插入图片描述

图21:运行hello按下ctrl+z后输入kill

在这里插入图片描述

图22:运行hello按下ctrl+z后输入jobs

在这里插入图片描述

图23:运行hello按下ctrl+z后输入pstree

在这里插入图片描述

图24:运行hello按下ctrl+z后输入fg

6.7本章小结
Linux shell bash 是一款十分强大的应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。使用shell运行hello程序可以向其发送各种命令,可以通过信号处理各类异常,并且进程调度十分合理,这样使得系统运行稳定且高效。

(第6章1分)

第7章 hello的存储管理
7.1 hello的存储器地址空间
结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。
逻辑地址(Logical Address) 是指由程式产生的和段相关的偏移地址部分,如hello.oasm中可见的指令地址。不过有些资料是直接把逻辑地址当成虚拟地址,两者并没有明确的界限。
线性地址(Linear Address) 是逻辑地址到物理地址变换之间的中间层。
物理地址(Physical Address) 是指出目前CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果地址。
逻辑地址与物理地址的“差距”是0xC0000000,是由于虚拟地址->线性地址->物理地址映射正好差这个值。这个值是由操作系统指定的。
7.2 Intel逻辑地址到线性地址的变换-段式管理
段式内存管理方式就是直接将逻辑地址转换成物理地址,也就是CPU不支持分页机制。其地址的基本组成方式是 段号+段内偏移地址。
在x86保护模式下,段的信息(段基线性地址、长度、权限等)即段描述符占8个字节,而段寄存器只有2字节,段信息无法直接存放在段寄存器中。Intel的设计是段描述符集中存放在GDT或LDT中,而段寄存器存放的是段描述符在GDT或LDT内的索引值(index)。
首先给定一个完整的逻辑地址[段选择符:段内偏移地址],接下来:
1.看段选择描述符中的T1字段是0还是1,可以知道当前要转换的是GDT中的段,还是LDT中的段,再根据指定的相应的寄存器,得到其地址和大小,我们就有了一个数组了。
2.拿出段选择符中的前13位,可以在这个数组中查找到对应的段描述符,这样就有了Base,即基地址就知道了。
3.把基地址Base+Offset,就是要转换的下一个阶段的地址。

7.3 Hello的线性地址到物理地址的变换-页式管理
分页的基本原理是把内存划分成大小固定的若干单元,每个单元称为一页(page),每页包含4k字节的地址空间(为简化分析,我们不考虑扩展分页的情况)。这样每一页的起始地址都是4k字节对齐的。为了能转换成物理地址,我们需要给CPU提供当前任务的线性地址转物理地址的查找表,即页表(page table)。
为了节约页表占用的内存空间,x86将线性地址通过页目录表和页表两级查找转换成物理地址。32位的线性地址被分成3个部分:最高10位 Directory 页目录表偏移量,中间10位 Table是页表偏移量,最低12位Offset是物理页内的字节偏移量。页目录表的大小为4k(刚好是一个页的大小),包含1024项,每个项4字节(32位),项目里存储的内容就是页表的物理地址。如果页目录表中的页表尚未分配,则物理地址填0。页表的大小也是4k,同样包含1024项,每个项4字节,内容为最终物理页的物理内存起始地址。
每个活动的任务,必须要先分配给它一个页目录表,并把页目录表的物理地址存入cr3寄存器。页表可以提前分配好,也可以在用到的时候再分配。
7.4 TLB与四级页表支持下的VA到PA的变换
在Intel Core i7环境下研究VA到PA的地址翻译问题。前提如下:
虚拟地址空间48位,物理地址空间52位,页表大小4KB,4级页表。TLB 4路16组相联。CR3指向第一级页表的起始位置(上下文一部分)。
解析前提条件:由一个页表大小4KB,一个PTE条目8B,共512个条目,使用9位二进制索引,一共4个页表共使用36位二进制索引,所以VPN共36位,因为VA 48位,所以VPO 12位;因为TLB共16组,所以TLBI需4位,因为VPN 36位,所以TLBT 32位。
在这里插入图片描述
图25:使用k级页表的地址翻译

在这里插入图片描述
图26:core i7的内存系统
如图 ,CPU产生虚拟地址VA,VA传送给MMU,MMU使用前36位VPN作为TLBT(前32位)+TLBI(后4位)向TLB中匹配,如果命中,则得到PPN(40bit)与VPO(12bit)组合成PA(52bit)。
如果TLB中没有命中,MMU向页表中查询,CR3确定第一级页表的起始地址,VPN1(9bit)确定在第一级页表中的偏移量,查询出PTE,如果在物理内存中且权限符合,确定第二级页表的起始地址,以此类推,最终在第四级页表中查询到PPN,与VPO组合成PA,并且向TLB中添加条目。
如果查询PTE的时候发现不在物理内存中,则引发缺页故障。如果发现权限不够,则引发段错误。

7.5 三级Cache支持下的物理内存访问
在这里插入图片描述
图27:三级Cache支持下的物理内存访问
得到VA后,使用CI为组索引,匹配CT。若成功且有效则命中,根据偏移取出数据返回。
若未命中,向下一级缓存查询。查询到后,若有空闲,则直接放置,否则使用最少使用策略LFU进行替换。
7.6 hello进程fork时的内存映射
Fork的子进程完全与父进程一致,有相同的虚拟内存空间。每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有,写时复制。

7.7 hello进程execve时的内存映射
使用execve
1.删除已存在的用户区域
2.创建新的私有区域(.malloc,.data,.bss,.text)
3.创建新的共享区域(libc.so.data,libc.so.text)
4.设置PC,指向代码的入口点
7.8 缺页故障与缺页中断处理
缺页故障是一种常见的故障,当指令引用一个虚拟地址,在MMU中查找页表时发现与该地址相对应的物理地址不在内存中,就会触发。处理流程如下:
情况1:段错误:首先,先判断这个缺页的虚拟地址是否合法,那么遍历所有的合法区域结构,如果这个虚拟地址对所有的区域结构都无法匹配,那么就返回一个段错误;
情况2:非法访问:接着查看这个地址的权限,判断一下进程是否有读写改这个地址的权限;
情况3:如果不是上面两种情况那就是正常缺页,那就选择一个页面牺牲然后换入新的页面并更新到页表。
7.9动态存储分配管理
Printf会调用malloc,请简述动态内存管理的基本方法与策略。
动态内存分配器维护着进程的堆区,在此处为程序分配内存空间。
分配器分为两种基本风格:显式分配器、隐式分配器。
显式分配器:要求应用显式地释放任何已分配的块。
隐式分配器:要求分配器检测一个已分配块何时不再使用,那么就释放这个块,自动释放未使用的已经分配的块的过程叫做垃圾收集。
链表维护策略:
后进先出(LIFO),释放的块在链表的头部,使用LIFO的顺序和首次适配的放置策略,分配器会最先检查最近使用过的块,在这种情况下,释放一个块可以在线性的时间内完成,如果使用了边界标记,那么合并也可以在常数时间内完成。
按照地址顺序来维护链表,其中链表中的每个块的地址都小于它的后继的地址,在这种情况下,释放一个块需要线性时间的搜索来定位合适的前驱。平衡点在于,按照地址排序首次适配比LIFO排序的首次适配有着更高的内存利用率,接近最佳适配的利用率。
7.10本章小结
本章对hello的存储地址空间进行了简要概括。简单研究了逻辑地址、虚拟地址、物理地址与线性地址的概念与转换方法,且介绍了fork、execve函数,介绍了动态内存分配管理的方法。
(第7章 2分)

第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:文件
设备管理:unix io接口
所有的I/O设备都被模型化为文件,而所有的输人和输出都被当作对相应文件的读和写来执行。这种将设备优雅地映射为文件的方式,允许Linux内核引出一个简单、低级的应用接口,称为Unix I/O,这使得所有的输人和输出都能以一种统一且一致的方式来执行。
8.2 简述Unix IO接口及其函数
打开和关闭文件:open()and close();
读写文件:read() and write();
改变当前的文件位置 lseek();
8.3 printf的实现分析
看printf函数原型

在这里插入图片描述

图28:printf函数原型

看这句va_list arg = (va_list)((char*)(&fmt) + 4);
va_list的定义: typedef char va_list
这说明它是一个字符指针。 其中的: (char
)(&fmt) + 4) 表示的是…中的第一个参数。
看看vsprintf(buf, fmt, arg)是什么函数。

在这里插入图片描述

图29:vsprintf函数原型

vsprintf的作用就是格式化。它接受确定输出格式的格式字符串fmt。用格式字符串对个数变化的参数进行格式化,产生格式化输出。
write系统函数:

在这里插入图片描述

图30:write函数汇编
再找到INT_VECTOR_SYS_CALL的实现:
init_idt_desc(INT_VECTOR_SYS_CALL, DA_386IGate, sys_call, PRIVILEGE_USER);
int INT_VECTOR_SYS_CALL表示要通过系统来调用sys_call这个函数

在这里插入图片描述

图31:sys_call汇编
于是我们可知直到printf函数执行流程如下:
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。

8.4 getchar的实现分析

在这里插入图片描述

图32:getchar函数原型
1.getchar由宏实现:#define getchar() getc(stdin)。
2.getchar有一个int型的返回值。当程序调用getchar时.程序就等着用户按键。用户输入的字符被存放在键盘缓冲区中。直到用户按回车为止(回车字符也放在缓冲区中)。当用户键入回车之后,getchar才开始从stdio流中每次读入一个字符。getchar函数的返回值是用户输入的字符的ASCII码,若文件结尾(End-Of-File)则返回-1(EOF),且将用户输入的字符回显到屏幕。如用户在按回车之前输入了不止一个字符,其他字符会保留在键盘缓存区中,等待后续getchar调用读取。也就是说,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完后,才等待用户按键。
3.异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
4.getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
本章主要介绍了Linux下IO设备的管理,和Linux的接口的简述,又介绍了printf和getchar这两个输出函数的实现,这些为hello的O2O过程中最后的输出部分。
(第8章1分)
结论
计算机系统看似复杂,实则也确实复杂,其中的思想也远超一般人能够想到的,比如从逻辑地址的概念建起,到hello编译四部曲(预处理、编译、汇编、链接),再到进程的概念、内核的调用、内存的分配原则。。。这一切都是计算机科学家们智慧的结晶啊!
(结论0分,缺失 -1分,根据内容酌情加分)

附件
列出所有的中间产物的文件名,并予以说明起作用。
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
hello.c 用C语言编写的hello程序源代码
hello.i 经预处理处理得到的文本文件
hello.s 编译后得到的汇编代码
hello.o 汇编后得到的可重定位目标执行文件
hello.oasm hello.o的反汇编文件
hello 链接后得到的二进制文件

(附件0分,缺失 -1分)

参考文献
为完成本次大作业你翻阅的书籍与网站等
[1] https://www.cnblogs.com/pianist/p/3315801.html

[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分)

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值