补充知识(1)

📅 2026/7/18 12:41:25 👁️ 阅读次数 📝 编程学习
补充知识(1)

我们认识了文件系统,学习操作系统如何管理未打开的文件。操作系统中除了未打开的文件外,还存在许多打开的文件。打开一个文件,实际上就是将文件从磁盘加载到内存中。此外,当内存中的数据长时间未被访问时,操作系统也会将该数据踢出内存。本章涉及的第一个话题便是磁盘和内存间的数据交互。

磁盘和内存间的数据交互

我们可以将内存看做一块对数据临时存取的空间,它的传输速度比硬盘快得多。为了更好地利用CPU,计算机会利用内存和CPU进行数据交换。但内存能存放的数据是有限的,很多时候操作系统需要将数据从磁盘加载到内存中,然后再将数据传递给CPU。同时操作系统也需要将内存中无用的数据放回磁盘。问题来了:操作系统怎么将数据从磁盘加载到内存中呢?

以打开文件为例,假设此刻操作系统要打开一个文件,这个文件的大小是1KB,或者是4KB,1MB,难道在打开文件时,操作系统会直接使用内存的1KB,4KB或者是1MB空间,然后直接将文件放进内存中吗?操作系统是不会这么干的。

在磁盘与内存交换数据时,逻辑上一般以4KB为单位,物理内存被分为多部分。这4KB也是交换数据时基本的交互单位。我们一般将内存上这一个4KB的空间叫做页框。为了能够更好地和内存进行交换数据,在逻辑上,磁盘(卷)也以4KB为单位分为多部分,这时的4KB的空间就被称作页帧。因此磁盘与内存之间交换数据,就是一个或者多个页框和页帧之间的数据转移。

当我们只打开小文件(几个或几百个字节)时,这种4KB的数据交换显得有些死板,但以4KB为单位进行的数据交换比随意大小的数据交换(需要多少字节就交换多少字节)更有效一点。首先,固定单位的内存空间要更容易管理;此外当大小固定时,读取和写入的效率会变高。我们可以想象一下:我们要往内存中的一片区域写1KB数据,如果4KB为单位,操作系统只需找到一块空闲的空间,然后写入数据即可。如果单位不固定,代表内存中存放的数据大小也是不固定的,空闲空间的大小也不固定,我们在找到空闲空间后,还需要判断该空间是否足够大。这时候,光找到合适的空间就比较花费时间了;最后,这种直接以4KB为单位的交换是一种基于程序局部性原理的预加载机制,这样可能会减少缺页中断的次数。

如何管理内存

一个4GB的内存,操作系统该如何管理内存呢?

前面说过,在逻辑上物理内存被分为多个页框。所以操作系统只要管理这些页框,便能管理好内存。那么该怎么管理呢?——先描述,再组织。

Linux中有一个数据结构名为"struct page",这个结构体中所存的便是一个页框必要的属性信息,例如引用计数,空闲标志等等。一个内存有多个页框,也就有多个page页框,因为内存是连续的,所以我们可以使用数组管理这多个page,所以一个内存就被抽象成一个数组。数组天然具有下标,因此每个页框自然具有自己的编号。访问内存时,只要从内存地址上找到对应的页框编号,然后去page数组中找对应的元素即可。申请内存则是在数组中寻找空闲的page,然后分配即可。

page数组是内存管理的核心结构,当我们访问和申请内存时,并不会直接去访问该数组,操作系统中有对应的算法,不会让我们直接接触数组的。

打开文件

我们已经知道当我们打开文件时,系统会通过文件路径和文件名找到文件对应的inode和内容。现在一个进程已经打开了文件,它如何根据这个被打开的文件找到文件属性和文件内容呢?

在描述打开文件的结构体中,有类似的属性名为"struct inode",通过这一属性便能找到打开文件对应的inode,得到该文件的属性。此外,在struct file中还存在着描述文件页缓冲区的这一数据结构,Linux是通过字典树描述文件的页缓冲区的。其中文件内容也以4KB为单位进行划分,在这种划分方式下,文件的数据块存在对应的编号,然后根据文件数据块编号对应的32为二进制进行划分,构造出对应的字典树,从而找到对应的页。这也就是打开文件的页缓冲区。