内存换入-换出--OS

内存换入-请求调页

用换入、换出实现“大内存”

我们设置虚拟内存大小位4G,但是实际内存只有1G,所以虚拟内存没办法完整的映射到实际内存中,那么如何才能让用户觉得虚拟内存就是实际的内存呢?换入换出可以实现这一点,当用户使用虚拟内存的(0G-1G)时,就将这一段的虚拟内存映射到物理内存上,当用户使用(3G-4G)处的虚拟内存时,就将虚拟内存(3G-4G)映射到物理内存上,这样就相当于4G的虚拟内存都可以使用了

请求的时候才映射

请求调页

当MMU进行重定位时,发现虚拟内存没有映射,就会发起中断,同时不让PC+1,然后从磁盘中查找对应的页,再从内存中申请一段内存,将磁盘中读到的页放入内存中,再建立映射,最后中断返回,MMU重新计算,然后重新执行中断时的那一条指令。

一个实际系统的请求调页

请求调页,当然从缺页中断开始
中断号      名称                说明
12      Segment not present    描述符所指的段不存在
14      Page fault             页不在内存
void trap_init(void)
{
    set_trap_gate(14, &page_fault);
}
#define set_trap_gate(n,addr) \
    _set_gate(&idt[n], 15, 0, addr);

//在linux/mm/page.s
.globl _page_fault
    xchgl %eax,(%esp)  //错误码esp被压入栈中
    ...
    movl $0x10, %edx
    ...
    movl %cr2, %edx //cr2寄存器里存放着页错误线性地址,将其赋给edx;
    pushl %edx
    pushl %eax
    ...
    call _do_no_page  //调用_do_no_page函数,参数就是eax、edx,保存在栈中
    ...
//在linux/mm/memory.c中
void do_no_page(unsigned long error_code, unsigned long address)
{
    address &= 0xfffff000  //将后面三位去掉,得到页面地址
    tmp = address-current->state_code;  //页面对应的偏移
    if(!current->executable || tmp>=current->end_data)
    {
        get_empty_page(address);
            return;
    }
    page = get_free_page();  //申请内存空间
    bread_page(page, current->executable->i_dev, nr);  //从磁盘中读缺少的页到page中
    put_page(page, address);  //建立页表映射
}
//在linux/mm/memory.c中
unsigned long put_page(unsigned long page, //物理内存
                        unsigned long address)
{
    unsigned long tmp, *page_table;
    page_table = (unsigned long *)((address>>20)&ffc);  //找到页目录项
    if((*page_table)&1)
        page_table = (unsigned long *)(0xfffff000&*page_table);
    else
    {
        tmp = get_free_page();
        *page_table = tmp|7;
        page_table = (unsigned long *)tmp;
    }
    //根据页目录项找到页表项
    page_table[(address>>12)&0x3ff] = page|7; //将页表项映射到新建的物理内存上
    return page;
}

内存换出

虚拟内存的实现靠的是换入和换出,上面我们只是实现了换入,但是内存终究是有限的,必须从内存中取出一部分页来换出才能得到空闲的内存

page = get_free_page();  //申请内存空间
bread_page(page, current->executable->i_dev, nr);  //从磁盘中读缺少的页到page中

FIFO页面置换

因为程序具有局部性和连续性,一般刚用到的页会多次被调用,所以FIFO可能导致一个页刚被置换出去,接下来又需要用到这个页,因此效率太差

MIN页面置换

MIN页面置换是最优方案,选最远将被使用的页淘汰,但是不可能预知将来会用到什么页,因此也没有办法能够实现MIN算法

LRU页面置换

LRU算法:选最近最长一段时间没有使用的页淘汰(因为程序具有局部性,一般最近使用的页未来也会使用,而长时间没有被使用的页未来一段时间也不会被使用)

LRU算法实现

LRU算法准确实现

使用时间戳

实现思路:每一个页维护一个时间戳,当被使用时,修改时间戳,换页时选最小的时间戳换出

该思路是可以实现LRU,但是代价太大,每执行一条指令就需要在页中修改时间戳,并且还要处理时间戳溢出的情况,在实际中不可行

页面栈

实现思路:维护一个栈,当一个页被用到时,将其换到栈顶,每次页面换出就淘汰栈底的页

该思路可行,但是同样的代价较大,每一次执行指令需要修改几次指针。

LRU算法近似实现 --将时间计数变为是和否

SCR(Second Chance Replacement)算法 | Clock Algorithm 算法

  • 每个页加一个引用位R(reference bit)
  • 选择淘汰页:扫描R,是1时清0,并继续扫描;是0时淘汰该页

因为只是修改一位,因此这一项工作可以由MMU来完成,速度非常快。

Clock算法的分析和改造

如果缺页很少(程序具有局部性),那所有的R都会变成1,这样这个算法就退化成FIFO算法了,因此需要定时清除R位,也就是定义一个定时清除R位指针,该扫描指针要比选择淘汰页的扫描指针速度快,这就是这个算法叫做Clock算法的原因。

进程页框分配

  • 给进程分配多少页框(帧frame)

    • 分配的多,请求调页导致的内存高效利用就没有意义了,而且内存也是有限的
    • 分配的少

    Y03VT1.png

    • 颠簸现象

因此,给进程分配的页框数量必须适中,不能太多也不能太少,还要限制进程的个数

猜你喜欢

转载自blog.csdn.net/jump_into_zehe/article/details/106115287