操作系统——段页式内存管理与虚拟内存

目录

1.如何将段和页结合在一起

2.段页结合时进程对内存的使用


1.如何将段和页结合在一起

        在对内存进行使用的过程中,用户更希望程序以段的形式被放入内存,这样符合用户的使用习惯,譬如找内存中“代码段的第40条指令”。而内存更希望将自己等分成若干页,可以避免因内存碎片导致的内存利用效率的降低。

        为了同时满足用户和内存的要求,操作系统需要一种中间结构来完成段与页机制的统一,这就是虚拟内存。 

        虚拟内存是一个抽象的概念,本身并不存在,他是一连串的虚拟地址构成的。当程序分段后,从虚拟内存上分割出相应的分区与各段建立映射关系,完成分段机制;再将虚拟内存分割成页,将这些页放在页框中,并建立页和页框的映射,完成分页机制。


2.段页结合时进程对内存的使用

        提出了虚拟内存的概念后,我们已经能够将分段机制和分页机制有机地结合在一起了。现在又要引出两个问题?程序又是如何放置到内存中去的了?又是如何得以正确执行的了?

        如上图所示,当一个程序想要放入内存,会经历以下的步骤:

(1)在虚拟内存中划分区域,将程序分段载入到虚拟内存中,其实就是建立了程序段与虚拟内存各个分区间的映射关系。

(2)建立如上图所示的段表,记录各段与虚拟内存的映射关系。

(3)将虚拟内存中的各段打散成页,将每页的内容,实际上指被映射的各程序段的内容放入内存的页框中。

(4)建立如上图所示的页表,建立虚拟页号和物理页框号之间的映射关系。        

        以指令“call 40”为例,逻辑地址CS=40。设代码段为第一段,页面大小为100。段号为0,找到该段的偏移地址为1000,1000+40=1040,这是在虚拟内存中的地址。再用1040除以页面大小100,得到虚拟页号为10,查找页表发现对应的物理页框号为5,那么实际内存地址为5*100+40=540,就是“mov 1, [300]”指令。 

        只要将特定的寄存器(LDTR、CR3)的值设置为正确段表初始地址和页表初始地址,执行每条指令时MMU都会自动完成上述地址转换过程。

3.段页式内存机制实例

一个真实的段页式内存机制的实例,就是将上述所说的概念轮廓用代码表示出来。

        代码的实现要从进程创建fork()开始。接着进程调用copy_process函数,进程管理要利用这个函数完成进程PCB的创建、完成内核栈的分配与初始化、完成内核栈和PCB之间的关联。现在,内存管理要在这个函数中给进程建立地址空间:

int copy_process(int nr, long ebp,……)
{
    ......
    copy_mem(nr, p);
    ......
}

         这里的copy_mem(nr, p)就是要为进程建立内存空间,copy是指复制父进程的内存空间。接下来要实现在虚拟内存中分段、建立段表、将虚拟页映射到空闲物理页框,建立页表。

copy_mem(int nr, task_struct *p)
{
    unsigned int new_data_base;
    new_data_base = nr * 0x4000000;//64Mb * nr
    set_base(p->ldt[1], new_data_base);
    set_base(p->ldt[2], new_data_base);
}

        上图程序中的核心就是set_base,就是向LDT表中填入内容,即建立段表。

        new_data_base = nr * 0x4000000即是为虚拟段分割内存。0号进程的nr=0,1号进程的nr=1,每个进程都分到了64MB的虚存空间。如上图所示。

        接下来就是进行分页。其实在系统初始化的时候,mem_init函数中的mem_map数组就是用来记录页的空闲情况,初始化时全部为0,表示空闲状态。紧接着就是将虚拟内存分割成页,并建立和物理页框号的映射关系。

int copy_mem(int nr, task_struct *p)
{
    unsigned long old_data_base;
    old_data_base = get_base(current->ldt[2]);//得到父进程的虚拟内存空间
    copy_page_tables(old_data_base, new_data_base, data_limit)
    ......
}

        copy_page_tables的作用就是将父进程的虚拟内存对物理内存的映射关系,复制给子进程的虚存空间new_data_base。会得到如下图所示的结构。

        copy_page_tables的代码实现如下:

int copy_page_tables(unsigned long from, unsigned long to, long size)
{
    from_dir = (unsigned long*)((from>>20) & 0xffc);//获得父进程虚拟内存中的页表地址
    to_dir = (unsigned long*)((to>>20) &0xffc);
    for(; size->0; from_dir ++, to_dir ++)//将父进一个页目录项下的所有页表项复制到子进程的目录项下
    {
        from_page_table = (0xfffff000 & from_dir);
        to_page_table = get_free_page();
        *to_dir = ((unsigned long)to_page_table)|7;
        for(; nr->0; from_page_table ++, to_page_table ++) 
          {        
                this_page = *from_page_table;
                this_page &= 2;//设置为只读
                *to_page_table = this_page;
                *from_[age_table = this_page;
                this_page -= LOW_MEM;
                this_page >>= 12;
                mem_map[this_page++];
          }
    }
}
        

 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值