文章目录
- 一、操作系统对物理内存的管理
- 1.1 物理内存与磁盘的数据交换
- 1.2 操作系统对物理内存的管理
- 二、再来看文件打开和写入
- 2.1 文件页缓冲区的引入
- 2.2 向文件中写入的过程
- 三、结语
一、操作系统对物理内存的管理
1.1 物理内存与磁盘的数据交换
物理内存与磁盘之间的数据交换一般是以 4KB(大小可改) 为单位进行的,其中物理内存中一个 4KB 大小的空间叫做页框,填入 4KB 内存中的内容叫做页帧。样做的好处有以下几点:
-
减少 I/O 的次数,即减少 CPU 访问外设的次数以提高效率。一次访问 4KB 和分四次访问 1KB 相比,前者效率更高,因为前者 CPU 只和磁盘只打了一次交道,磁头和盘面只需要进行一次定位就可以读取出 4KB 的内容,而后者 CPU 和磁盘要打四次交到,并且这四次可能并不连续,意味着磁头和盘面要定位四次,大量的机械运动就伴随着效率的下降。
-
基于局部性原理的预加载机制。即使当前 CPU 只需要访问100字节的内容,但操作系统和磁盘之间还是以 4KB 为单位将数据加载进来,一般认为,CPU 在访问当前磁盘中的代码和数据的时候,接下来有较大的可能性去访问附近空间的代码和数据。
小Tips:4KB 是科学家经过大量实现确定出来的一个较为合理的值。一个文件如果占用了多个数据块,那么只有最后一个数据块可能存在没用完浪费的情况。一个数据块只能被一个文件使用。加入有两个只有几个比特的小文件,那么会为他们俩一人分配一个数据块,他们两个不可能共享同一个数据块。
1.2 操作系统对物理内存的管理
首先操作系统一定是可以看到物理内存的。对物理内存的管理还是逃不脱先描述再组织,在内核中有一个 struct page
的结构体就是用来描述物理内存的,一个 struct page
对象就对应一个 4KB 的内存空间,该结构体里面记录了当前 4KB 的一些属性信息,例如:当前页框的状态,当前页框的引用计数等。操作系统将物理内存划分成一个个的 struct page
对象,然后用数组的形式组织起来,数组的下标就是对应的页号。随便给一个物理地址(一般一个物理地址对应一个字节)求它所在的页号,就是用该物理地址除以 (4KB*1024 = 4096字节)即可,也就是用该地址按位与上 0XFFFFF000
,将低12位清零,就是该地址所在的页号。所有申请内存的动作都是在访问内存 page 数组,查看 struct page
里面的 flags 属性,通过 flags 属性判断当前页框的状态,得知该页框是否被使用,没被使用就去修改 flags,表示该页框已被申请。flags c除了可以表示当前页框是否被使用外,还可以用来表示该页框是只读的还是读写等状态。这里所说的操作系统对物理内存的管理只是一个雏形,真正的内存管理系统是非常复杂的,像伙伴系统算法、slab 分派器等。
二、再来看文件打开和写入
2.1 文件页缓冲区的引入
前面的文章中说到过 struct file
是内核中的一个数据结构,用来描述一个打开的文件。而 inode 是在介绍磁盘时引入的,磁盘上的一个文件对应一个 inode,它表示该文件的属性。struct file
和 inode
之间的关系是,struct file
中只记录了文件的少量属性,因此在内核中还有一个 struct inode
结构体专门用来记录一个文件的所有属性。在 struct file
中有一个指针字段指向该文件的 struct inode
对象。
而文件是包括内容加属性的,在磁盘上文件的属性用 inode
来存储,文件的内容用 block
来存储,上面说过在内核中用 struct inode
来存储文件属性,那一个文件的内容(数据)在内核中该如何表示呢?答案是采用文件页缓冲区。在 struct file
结构中有一个 struct address_space
字段的指针,该结构体里面有一个 struct radix_tree_root
结构体对象,这本质上是一个树状结构(基数数又或叫字典书),树中的每个节点都是 struct radix_tree_node
类型,该类型里面有一个名为 slots
的 void *
类型的数组,里面存的其实就是 struct page
对象的地址。总结一下就是在 struct file
结构体中有指向物理内存的指针,我们将这些物理内存就叫做文件页缓冲区。
2.2 向文件中写入的过程
在调用 C/C++ 库函数向文件中写入,首先将内容写入到语言层面的用户缓冲区,然后再在合适的时机将内容写入到该文件对应的文件页缓冲区中,最后再在合适的时机将内容写入到外设。将物理内存中的数据刷新到磁盘是由 I/O 子系统来执行,进程并不会关心,在操作系统中一定会存在非常多的 I/O 操作,可能会有很多进程都要将数据写入磁盘,操作系统会将所有的 I/O 操作根据先描述再组织的形式管理起来。内核中的 struct request
结构就是专门用来描述一个 I/O 操作的。为了提高 I/O 的效率,操作系统还会进行 I/O 排序和 I/O 合并,以减少磁头和盘面的定位次数。
总结:Linux 中,我们的每一个进程,打开的每一个文件都要有自己的 struct inode
对象和自己的文件页缓冲区(就是所谓的内核缓冲区)。
三、结语
今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,春人的主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是春人前进的动力!