3.21内存的换入换出

datecommentscategoriestagspermalinktitle
2020/3/5
true
操作系统
内存
换入换出
3.21
内存的换入换出

换入换出概念的引入

前面说过为了保证内存在用户程序看起来是分段,而实际是分页的效果,引入了虚拟内存。对于用户来说,虚拟内存是一个完整的内存,用户可以随意使用该“内存”,假设为4G,那么对于用户来说就有4G的空间可以使用,即使实际内存只有2G甚至1G。那么这是如何实现的呢?这就引出了换入和换出。

换入

假设计算机的物理内存是1G,而给进程分配的逻辑内存是4G,那么当进程访问0-1G的逻辑内存时,就把这1G的内存与实际物理内存做映射。当进程访问1-2G的逻辑内存的时候,就把此时的1G逻辑内存与实际物理内存做映射。而之前占用着实际内存的0-1G的逻辑内存被换出到虚拟内存中(给1-2G腾出空间)。这样在用户看起来似乎,自己确实拥有了4G的内存空间。这个换入采用的是中断的形式,如果load[addr]的时候,发现addr在页表里面没有对应映射,那么就将中断向量寄存器的某一位置为一,说明有中断产生。然后在中断服务函数里面将addr导入到物理内存中。然后再次执行load[addr]这条语句。当然查表的操作是MMU做的.

请求换入的方法实现

具体如何将某页从磁盘换入到内存呢?从中断服务函数开始,cpu就到中断向量表去查找中断号,然后转去执行该中断服务程序。

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)
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $0x10, %edx
mov %dx, %ds
mov %dx, %es
mov %dx, %fs
movl %cr2, %edx

首先push一些信息到堆栈中(包括错误码),然后mov一些东西,做现场保护。

pushl %edx
pushl %eax
testl $1, %eax        // 测试标志
jne 1f
call _do_no_page
jmp 2f
1: call _do_wp_page //保护
2: add $8, %esp
pop %fs
pop %es
pop %ds
pop %edx
pop %ecx
pop %eax
iret

一般push后面加了一个call来调用c函数,前面push的都是压入参数。调do_no_page();很明显就是处理缺页的情况。

//在linux/mm/memory.c中

void do_no_page(unsigned long error_code, unsigned long address)
{
    address&=0xfffff000;                 //页面地址
    tmp=address–current->start_code;     //页面对应的偏移
    if(!current->executable||tmp>=current->end_data)  //判断不是代码和数据
    {
        get_empty_page(address);
        return;
    }
    //核心就是下面这三句
    page=get_free_page();//得到一个空闲页赋值给page
    bread_page(page, current->executable->i_dev, nr);//磁盘里面的页读到内存中
    put_page(page, address);//建立内存映射
    ……
}
//在linux/mm/memory.c中
//参数1就是要计算后返回的物理地址
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;
}

换出

有换入,就肯定有换出,就是把真实内存中的数据,挪放到虚拟内存中,腾出真实物理内存空间,“假装”这些腾出了的空间是和之前的内存连续的一段空间。

换出的方法

换出容易,但是找出来哪一页内存“适合”换出就比较困难了。

FIFO方法

这种方法太简单粗暴,无法保证换出的操作次数尽可能的少。

MIN算法

把暂时不使用或者最久时间不用的那个换出,但是在实际中无法预测哪一页是最近不访问的。

LRU算法

根据过往的经验,谁最不经常用就把谁换出。由于一个进程通常也就访问局部的一部分内存,例如一个for循环。这样把最长时间不用的那页换出似乎很合理。

LRU算法的时间戳实现

这种方式理论上是可行的,每次使用某页的时候记录下时间戳,但是每次地址访问都要修改时间戳,需要维护一个全局时钟,需要找到最小值……实现代价太大了。

LRU的近似实现 clock算法

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

二次机会算法

具体操作是这样的,每页增加一个引用位R,每一次访问该页时,就将该位置为1。当发生缺页时用一个指针查看每一页的引用位,如果是1则将其置为0,如果是0就直接淘汰。在实际中,缺页的情况肯定不会很多;如果缺页很多了,说明内存太小了或者算法不行。发生缺页的间隔时间相对太长,这时候几乎可以肯定每个页的引用为都是1了,此时再把1都编程0,指针再转一圈,把第一个0的页换出——竟然编程了FIFO方式了。 如何解决这个问题呢?有一个解决办法是再加一个指针用来清除每一页的引用位,可以放在时钟中断里面,定时清除(变成0);这个时间可以事先设置好,也可以在软件里面实现。

给进程分配多少个页空间

如果分配的多了,那么请求调页导致的内存高效利用就没有了。而且内存就那么大,如果每一个进程分配很多的话,跑的进程数量就少了。如果分配的少,系统内进程增多,每个进程的缺页率增大,当缺页率大到一定程度,进程就会总等待调页完成,导致cpu利用率降低。如下图

中间那个急剧下降的状态称为颠簸。一种可以采用的方法是,先给进程分配一定数量的页框,如果增加页框能增加cpu利用率,就慢慢增加,如果导致cpu利用率减少,就降低页框分配。当然实际情况下每个进程对应的页框数量肯定是得动态调整的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值