你有没有想过,当你在电脑上同时打开多个程序,一边听音乐、一边写文档,还挂着下载任务时,系统是怎么有条不紊地运作,不让这些程序“打架”争内存的呢?答案就藏在这神奇的分页机制里。它就像是一位拥有超能力的空间魔法师,面对有限的物理内存“空间”,大手一挥,将虚拟的逻辑地址空间和物理内存空间,都精准地划分成了一块块同等大小、排列规整的“魔法方块”——也就是页。然后,凭借着一套如同精密齿轮组般的映射规则,让每个程序都以为自己拥有了广袤无垠的“专属领地”,实则是在内核的巧妙调配下,高效共享着物理内存资源。
这不仅极大地提升了内存利用率,避免了内存碎片的“乱象”,更是为虚拟内存的华丽登场铺就了坚实大道,让我们的电脑仿佛拥有了无限扩容的“超能力”。现在,就请紧跟我的脚步,一起深入探究这分页机制背后的奇妙故事,解锁Linux内核高效运行的密码吧!
在 Linux 系统的广袤天地里,内存管理犹如一座精密复杂的大厦,而分页机制则是这座大厦的基石。它就像是一位幕后英雄,默默地支撑着整个系统的稳定运行,让众多进程能够井然有序地共享物理内存资源。
想象一下,在计算机的世界中,众多进程如同一个个活跃的 “居民”,它们都渴望拥有自己的内存空间来存储数据、运行程序。然而,物理内存的容量是有限的,就好比一块有限大小的土地,如何合理地分配给这些 “居民”,让它们都能安居乐业呢?这便是分页机制大展身手的时刻。
Linux 内核通过分页机制,将物理内存划分为一个个固定大小的 “小房间”,这些 “小房间” 被称为页。一般来说,常见的页大小为 4KB 或 8KB,当然,在不同的硬件架构和系统配置下,页大小可能会有所不同。与此同时,虚拟内存空间也被按照相同的页大小进行划分。这样一来,虚拟内存中的每一页都能找到与之对应的物理内存页,就像给每个 “居民” 都分配了一个专属的小房间,它们通过 “门牌号”(地址映射)就能准确无误地找到自己的家。
这种分页的方式带来了诸多好处。一方面,它极大地简化了内存管理的复杂度。内核只需关注这些固定大小的页,而无需对每一个字节的内存进行精细管理,大大减轻了内核的负担,就如同小区管理员只需管理一个个房间,而不用操心房间里每一块砖的摆放。另一方面,分页使得内存的分配与回收变得更加高效。当一个进程需要内存时,内核可以轻松地分配若干个连续或不连续的页给它;当进程结束后,回收这些页也变得轻而易举,不会留下混乱的内存碎片,保证了内存空间的整洁有序,就像整理房间一样,把不用的东西清理出去,为新的需求腾出空间。
再者,分页机制为内存的保护与共享提供了坚实的保障。每个进程都拥有自己独立的页表,这就好比每个 “居民” 都有一把独一无二的钥匙,只能打开自己房间的门,从而有效地隔离了不同进程的内存空间,防止一个进程误闯另一个进程的 “领地”,保障了系统的安全性。而且,通过巧妙的页表映射,多个进程还能共享同一段物理内存,就像几个朋友可以一起在客厅里玩耍,共享公共空间,实现了资源的高效利用,提升了系统的整体性能。
如果没有分页机制,能否实现“虚拟内存”?答案是肯定的。
当同时运行的任务很多时,内存可能就不够用,如上图所示,每个段描述符都有 AVL 位(简称 A 位),用于表示一个段最近是否被访问过(准确地说是表明从上次操作系统清零该位后一个段是否被访问过)。
当创建描述符的时候,应该把 A 位清零。之后,每当该段被访问时,准确地说是处理器把这个段的段选择符加载进段寄存器时,CPU 就将该位置“1”;对该位的清零是由操作系统负责的,通过定期监视该位的状态,就可以统计出该段的使用频率(比如,每 1 秒钟查看一次,一旦置位就清零,统计 10 秒钟内被置位了多少次,次数越多说明使用越频繁)。当内存空间紧张时,可以把不经常使用的段退避到硬盘上,从而实现虚拟内存管理。
当某个段被换出到磁盘时,操作系统应该将这个段的描述符的 P 位清零。过上一段时间,当再次访问这个段时,因为它的描述符的 P 位是 0,处理器就会引发段不存在异常(中断号 11)。这类中断通常是由操作系统处理的,它会用同样的方法腾出空间,然后把这个段从磁盘调入内存。当这类中断返回时,处理器会再次执行引发异常的那条指令,这时候段已经在内存中(P=1),于是程序又可以继续执行了。
由此可见,即使没有分页机制,利用“分段”也可以实现“虚拟内存”。但是,因为段的长度不固定,在段的换入换出时会产生外部碎片,这样就浪费了很多内存。为了解决这个问题,从 80386 处理器开始,引入了分页机制。分页机制简单来说,是用长度固定的页来代替长度不定的段,以解决因段的长度不同带来的内存空间管理变得复杂的问题。尽管操作系统也可以利用纯软件来实施固定长度的内存分配,但是过于复杂。由处理器固件来做这件事情,可以省去很多麻烦,速度也可以提高。总结一下,引入分页机制并不是为