目录
【第九周学习总结】
【第八周课上测试】
第九周学习总结
课堂内容总结
(一)学习目标
- 理解虚拟存储器的概念和作用
- 理解地址翻译的概念
- 理解存储器映射
- 掌握动态存储器分配的方法
- 理解垃圾收集的概念
- 了解C语言中与存储器有关的错误
(二)学习内容
(1)虚拟存储器
- 概念:虚拟存储器,又称虚拟内存(VM),是对主存的抽象概念
- 能力:①高效使用主存 ②简化内存管理 ③ 保护每个进程的地址空间不被破坏
- 理解虚拟内存:①核心的 ②强大的 ③ 危险的
- 学习方向:①虚拟内存的工作流程 ②如何在程序中使用和管理虚拟内存
(2)物理寻址和虚拟寻址
虚拟内存主要是一种地址扩展技术,主要是建立和管理两套地址系统:物理地址和虚拟地址。由虚拟地址空间(硬盘上)装入进程,其实际执行是在物理地址空间(内存上)承载进程的执行。虚拟地址空间比物理地址空间要大的多,操作系统同时承担着管理者两套地址空间的转换。
- 物理寻址:主存的每个地址都是唯一的,第一个字节地址为0,接下来为2,以此类推。
- 虚拟寻址:CPU生成一个虚拟地址,先通过地址翻译转换成适当的物理地址,再被送到内存中
(3)地址翻译
- 概念: 将一个虚拟地址转换为物理地址的任务。 地址翻译需要CPU硬件和操作系统之间的紧密合作。CPU芯片上叫做内存管理单元的专用硬件,利用存放在主存中的查询表来动态翻译虚拟地址,该表的内容由操作系统管理。
- CPU中有一个专门的页表基址寄存器(PTBR)指向当前页表,使用页表进行翻译的时候方法如下:
每个虚拟地址由两部分组成:虚拟页号(VPN)+虚拟页偏移量(VPO),当CPU生成一个虚拟地址并传递给MMU开始翻译的时候,MMU利用虚拟地址的VPN来选择相应的PTE,同时将页表中的物理页号(PPN)+虚拟地址的VPO就生成了相应的物理地址(物理地址 = 页表中的物理页号 + 虚拟地址中的偏移量)。
(4)地址空间
- 虚拟页的三个状态
- 未分配的:VM系统还没分配/创建的页,不占磁盘空间
- 缓存的:当前已经缓存在物理内存中的已分配页
- 未缓存的:未缓存在物理内存中的已分配页
- 线性地址空间:地址空间中的整数是连续的。
- 虚拟地址空间:CPU从一个有 N=2^n 个地址的地址空间中生成虚拟地址,这个地址空间成为称为虚拟地址空间。
- 地址空间的大小:由表示最大地址所需要的位数来描述。
- 物理地址空间:与系统中的物理存储器的M个字节相对应。
- 虚拟存储器的基本思想:主存中的每个字节都有一个选自虚拟地址空间的虚拟地址和一个选自物理地址空间的物理地址。
虚拟存储器的虚拟页VP:每个虚拟页大小为P=2^平字节。物理存储器——物理页PP,也叫页帧,大小也为P字节。
存储器映射概念: 通过赋予每个任务不同的虚拟–物理地址转换映射,支持不同任务之间的保护。从形式上来说就是建立一个虚拟地址空间到物理地址空间的映射关系。简单来说就是把一个虚拟地址连接到一个物理地址。
(5)动态存储器分配的方法
- 动态存储器分配
除了使用低级的mmap和munmap函数来创建和删除虚拟存储器的区域,还可以使用动态存储器分配器。动态存储器分配器维护着一个进程的虚拟存储器区域,称为堆。堆是一个请求二进制零的区域(参见浅谈Linux存储器映射),它紧接在未初始化的bss区域后开始,并向上生长(向更高的地址)。对于每个进程,内核维护着一个变量brk(即break),它指向堆顶。分配器将堆视为一组不同大小的块的集合来维护。每个块就是一个连续的虚拟存储器片(chunk),要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可用来分配。分配器有两种,即显示分配器和隐式分配器。
- 两种分配器
- 显示分配器:要求应用显示的释放已经分配的块,像C,C++语言采用的机制。
- 隐式分配器:分配器检测一个已经分配的块何时不再被程序所使用时,自动free掉。这个就是垃圾收集。
- 显示分配器调用函数
//malloc函数
#include <stdlib.h>
// 返回:若成功则为指针,若出错则为NULL
void *malloc(size_t size);
//free函数
#include<stdlib.h>
// 返回: 无
void free(void*ptr)
- 使用动态存储器分配的原因:坚持到程序实际运行时我们才能够知道某些数据结构的大小。
(6)垃圾收集
垃圾收集器是一种动态存储器分配器,它自动释放程序不再需要的已分配块。这些块称为垃圾。自动回收堆存储的过程称为垃圾收集。垃圾收集器将存储器视为一张有向可达图,如下图所示。根节点包括堆中的指针,寄存器,栈里的变量,或者是虚拟存储器中读写数据区域内的全局变量。
C和C++的收集器不能维持可达图的精确表示,因此被称为保守的垃圾收集器。
- 标记清除垃圾收集器 = 标记阶段 + 清除阶段。标记阶段标记处根节点的所有可达的和已分配的后继,而清除阶段释放每个未被标记的已分配块。
C语言的标记收集器是保守的,其根本原因是C语言不会用类型信息来标记存储器位置。因此像int或float这样的标量可以伪装成指针。假设某个可达的已分配块在它的有效载荷中包含一个int,其值碰巧对应于某个其他已分配块b的有效载荷中的一个地址。对收集器而言是没有办法推断出这个数据实际上是int而不是指针的。因此,分配器必须保守地将b标记为可达,尽管事实上它可能是不可达的。
第八周课上测试
(一)求命令行传入整数参数的和
(1)测试题目
- 参考命令行参数在Linux下完成“求命令行传入整数参数的和”
- 测试代码传入自己的8位学号
- 上方提交代码,附件提交运行测试截图
(2)测试结果与截图
码云代码链接:commandline.c
编译运行
gcc commandline.c -o commandline
./commandline 2 0 1 6 5 2 2 3
- 测试截图
(3)测试中遇到的难题
>>> 运行时并没能输出命令行传入参数的和,原因是 ?
- 要像命令行参数中一样,将字符依次空格输入才行
(二)练习Y86-64模拟器汇编
(1)测试题目
- 把练习一中的代码在X86-64(Ubuntu)中反汇编,给出汇编代码和机器码的截图
- 把X86-64汇编翻译成Y86-64汇编,给出相应机器码的截图(使用Y86-64模拟器)
(2)测试结果与截图
- 码云代码链接
- 源代码:commandline.c
- 汇编代码:commandline.s
- 机器码:commandline.o
- Linux编译命令
gcc -E xxx.c -o xxx.i //预编译
gcc -S xxx.i -o xxx.s //编译 源代码转换为汇编代码
gcc -c xxx.s -o xxx.o //汇编 汇编代码转换为二进制机器语言
gcc -o xxx.o -o xxx.out //链接 得到可执行程序
- 1.在X86-64下 (Ubuntu) 将源代码 ( commandline.c ) 转换成汇编代码 ( commandline.s )
gcc -E commandline.c -o commandline.i
gcc -S commandline.i -o commandline.s
- 2.查看X86-64下的汇编代码
cat commandline.s
- 3.查看X86-64下的机器码
gcc -c commandline.s -o commandline.o
objdump -d commandline.o
- 4.使用Y86-64模拟器,将X86-64汇编翻译成Y86-64汇编
cd pipe
./psim -t -g ../y86-code/xxxxx.yo
例如:
- 5.查看Y86-64下的机器码
(3)测试中遇到的难题
>>> 如何安装Y86-64模拟器 ?
- 1.下载Y86-64模拟器
Y86-64模拟器资源下载:Y86-64模拟器
- 2.安装词法分析工具
sudo apt-get install bison flex
- 3.安装 Tcl/Tk 支持图形界面
sudo apt-get install tcl8.5-dev tk8.5-dev tcl8.5 tk8.5
- 4.解压sim.tar压缩包
unzip Y86-64模拟器.zip
tar -xvf sim.tar
- 5.修改makefile文件(sim文件夹下)
GUIMODE=-DHAS_GUI
TKLIBS=-L/usr/lib/ -ltk8.5 -ltcl8.5
TKINC=-I/usr/include/tcl8.5
- 6.编译
cd /sim
make clean;make
>>> 在安装词法分析工具时提示无法获得锁 ?
- 强制解锁
sudo rm /var/lib/dpkg/lock
>>> 在安装 Tcl/Tk 支持图形界面时出现如下依赖包问题 ?
一般出现这种情况的原因是:要装A,A依赖B,但是已经安装的软件C也依赖B,但是A依赖的B的版本和C依赖的B的版本不一致,导致了如果要安装B,那么C便不能正常运行了,所以系统报错,组织安装B。
解决方法是安装指定版本号的B。
- 1.先查看依赖关系
sudo apt-get install libxss-dev
- 2.制定 libxss1 的安装版本
根据报错的信息“下列软件包有未满足的依赖关系:libxss-dev : 依赖: libxss1 (= 1:1.2.1-2) 但是 1:1.2.2-1 正要被安装”
那么只需要sudo apt-get install xxx=yyy (xxx为提示信息的 libxss1, yyy为提示错误的1:1.2.2-1)
sudo apt-get install libxss1=1:1.2.2-1
- 3.同理解决掉另一个libxft-dev依赖包问题,然后再重新安装Tcl/Tk 支持图形界面即可
sudo apt-get install tcl8.5-dev tk8.5-dev tcl8.5 tk8.5
>>> 在 make clean;make 的过程中出现对'matherr'未定义引用问题 ?
- 1.知在进入pipe目录并编译psim.c和pipe-std.c时出现了对'matherr'未定义引用的错误
- 2.进入psim.c,将包含matherr的语句注释掉,总共有两行
- 3.继续使用 make clean;make 编译,成功
(三)基于socket实现daytime(13)服务器和客户端
(1)测试题目
- 基于socket使用教材的csapp.h和csapp.c实现daytime(13)服务器和客户端
- 服务器的端口使用13+自己的后三位学号,服务器响应消息格式是 :
客户端IP:XXXX
服务器实现者学号:XXXXXXXX
当前时间: XX:XX:XX
- 提交代码
- 提交一个客户端至少查询三次时间的截图测试截图
- 提交至少两个客户端查询时间的截图测试截图
(2)测试结果与截图
【一个客户端查询三次时间】
【两个客户端多线程同时查询时间】
(3)测试中遇到的难题
>>> 如何使用教材的配套代码 csapp.h和csapp.c ?
- 1.下载《深入理解计算机系统》配套代码 csapp
csapp.h其实就是一堆头文件的打包,在 csapp 中可以下载
- 2.以 root 身份登录下载并解压后得到一个code文件夹
- 3.在code文件夹的子文件夹include和src中分别找到csapp.h和csapp.c,把这两个文件拷贝到文件夹 /usr/include 里
- 4.修改csapp.h文件,在最低端的 #endif 之前加上一句 #include<csapp.h>
- 5.由于csapp.c中有关于线程的头文件,在用gcc的时候最后要加上 -lpthread
如:gcc xxx.c -o xxx -lpthread