之九:磁盘页面的抽象

在讲《X86页式内存管理》中提到过“页面交换”。一个系统的物理内存总是有限的,但是运行在其上的进程却不管不顾的一味“索取”,为了解决这种困境,在计算机的发展史上很早就有将内存中的内容与一个专用的磁盘空间交换的技术,即把内存中暂时不用的内容存到磁盘上,为其他急用的内容腾出内存空间,到需要时再将磁盘上的内容读到内存中。

在继续之前,要明确几个概念。首先“虚存页面”指虚拟地址空间中一个固定大小,边界与页面大小(4KB)对齐的区间及内容。“虚存页面”最终要落实(映射)到某种物理存储介质上,这就是“物理页面”。根据介质的不同,“物理页面”分为“内存页面”(在内存中)和“磁盘页面”(在磁盘上)。当我们谈及(物理)内存页面的分配和释放时,指的仅是物理介质;而在谈及页面的换入换出时,指的是页面中的内容。

我们在讨论(物理)内存页面时提到page结构就是内存页面的户口,系统在初始化阶段,内核根据检测到的内存的大小,为每一个页帧建立page结构,形成一个page结构的数组,并用全局变量mem_map指向这个数组,mem_map就像是物理内存的户口簿,有了这个户口簿系统就可以跟踪到每一个页帧。

那么“磁盘页面”呢?“磁盘页面”是如何管理的呢?

1.交换设备的抽象swap_info_struct

首先,对“磁盘页面”内核并没有对应的结构体,因为磁盘页面要依附在“交换设备”(通常是磁盘,也可以是普通文件,设备即文件)上,从内核管理磁盘页面角度来看,只需知道它在交换设备上的位置,以及该页面的使用计数(表示该页面是否被使用,以及有几个用户在共享该页面)即可。一个交换设备上的磁盘页面肯定具有相同的属性,这些属性信息从交换设备上获取即可。因而,内核对磁盘页面的管理是按交换设备来进行的。交换设备对应的抽象为swap_info_struct结构,而“磁盘页面”的定义也是依附在swap_info_struct结构体中。

swap_info_struct结构定义在<include/linux/swap.h>文件中

struct swap_info_struct {
   unsigned int flags;
   kdev_t swap_device;
   spinlock_t sdev_lock;
   struct dentry * swap_file;
   struct vfsmount *swap_vfsmnt;
   unsigned short * swap_map;
   unsigned int lowest_bit;
   unsigned int highest_bit;
   unsigned int cluster_next;
   unsigned int cluster_nr;
   int prio;       /* swap priority */
   int pages;
   unsigned long max;
   int next;       /* next entry on swap list */
};

swap_file、swap_vfsmnt与文件系统相关。

swap_map指向一个unsigned short型的数组,数组中的每一个元素代表一个磁盘页面:元素对应的unsigned short值代表该磁盘页面的引用计数;而下标则表示磁盘页面在文件中或者磁盘上的位置。其中第一个元素即swap_map[0]所代表的那个磁盘页面是不用做页面交换的,它包含了该交换文件或设备自身的信息以及一个代表哪些页面可供使用的位图,这个页面相当于该交换设备的“控制块”。数组的大小由成员pages表示,它表示该页面交换设备的大小。由于交换设备上某些页面是不用做交换的,而这些页面大都集中在交换设备的开头和结尾,所以lowest_bit和highest_bit分别表示交换设备上用户交换的页面的“起始”和“终止”。另一成员max则表示该交换设备上最大的磁盘页面号,也就是设备或文件的物理大小。

linux内核允许使用多个交换设备,所以在内核中还有一个swap_info_struct结构数组swap_info,其定义如下:

<include/linux/swap.h>
#define MAX_SWAPFILES 8
<mm/swapfile.c>
struct swap_info_struct swap_info[MAX_SWAPFILES];

可见,最多可以使用8个交换设备。注意这个全局数组swap_info,内核就是通过这个全局数组找到对应的交换设备的。

2.  磁盘页面的“页面表项”的抽象swp_entry_t

就像虚存页面通过页面表项pte_t与物理内存页面建立映射关系一样,磁盘页面也有类似的“页面表项”swp_entry_t。swp_entry_t的定义如下:

<include/linux/shmem_fs.h>
 
 /*
 * A swapentry has to fit into a "unsigned long", as
 * the entryis hidden in the "index" field of the
 * swapperaddress space.
 *
 * We have tomove it here, since not every user of fs.h is including
 * mm.h, butm.h is including fs.h via sched .h :-/
 */
typedef struct {
   unsignedlong val;
} swp_entry_t;

可见,swp_entry_t也为32位的整数,与pte_t一样,swp_entry_t也将32位分段使用,这里分成的3段,示意如下:


type段指示该页面在哪个交换设备(文件)中,是个序号,其实就是全局数组swap_info的下标。

offset段表示页面在交换设备(文件)的位置,也是一个序号,其实就是swap_info_struct结构中数组swap_map的下标。

swp_entry_t的bit0必须为0。其实swp_entry_t就是pte_t的“变身”。回忆前面pte_t的定义,当bit0为1,表示页面在内存中,而其余各bit表示该内存页面的起始地址以及页面属性。而当页面在磁盘上时,则相应的页面表项pte_t不再指向一个物理内存页面,而是“变身”为swp_entry_t,其bit0为0,表示页面不在内存中,所以MMU对其余各bit都忽略不顾,留待内核自由支配,内核也就顺势利用其余的bit指明了这个页面的去向,何乐而不为呢。

需要说明:当内存页面交换到磁盘页面上之后,对应的页面表项pte_t就“变身”为swp_entry_t,“变身”后仍驻留在之前的页表中,此时虚存页面被映射到一个磁盘页面上了。当在用户空间访问虚存页面时,根据CR3一步步找到这个页面表项(swp_entry_t),MMU发现其bit0为0,了解到该页面不在内存中,于是触发页面异常。我们在前面分析过页面异常的处理程序do_page_fault,此种情况又对应另一种场景:出错地址没有落在空洞中,而且就落在某个虚存区间vma中,内核通过读取对应的swp_entry_t可以知道该虚存页面的去向,然后将其换入到内存中。有兴趣的同学可以自行一观。

pte_t与swp_entry_t的关系是如此的密切,内核为此定义了一组宏:

/* Encode and de-code a swap entry */
#define SWP_TYPE(x)         (((x).val>> 1) & 0x3f)
#define SWP_OFFSET(x)       ((x).val >> 8)
#define SWP_ENTRY(type, offset)   ((swp_entry_t) { ((type) << 1) |((offset) << 8) })
#define pte_to_swp_entry(pte)     ((swp_entry_t) { (pte).pte_low })
#define swp_entry_to_pte(x)    ((pte_t) { (x).val })


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值