• 页面置换算法


    1 分段、分页、多级页表情况下的地址翻译流程

    在进程中,我们不直接对物理地址进行操作,CPU在运行时,指定的地址要经过MMU转换后才能访问到真正的物理内存。

    地址转换的过程分为两部分,分段和分页。

    分段机制简单的来说是将进程的代码、数据、栈分在不同的虚拟地址段上,从而避免进程间的互相影响。分段之前的地址我们称之为逻辑地址,它有两部分组成,高位的段选择符和低位的段内偏移。在分段时先用段选择符在相应的段描述符表中找到段描述符,也就是某一个段的基地址,再加上段内偏移量就得到了对应的线性地址,线性地址也称之为虚拟地址。
    而在实际的应用中,Linux为了增加可移植性并没有完整的使用分段机制,它让所有的段都指向相同的段地址范围,段的基地址都为0,这样逻辑地址和线性地址在数值上就相同了。

    所以,这里我们分析的重点在分页,也就是由线性地址到物理地址的转换过程。

    一级页表

    我们使用一个虚拟地址对应一个物理地址的方式。

    为何要有页表

    为了保证进程在各个页离散存储在物理块上的情况下,依旧可以正确找到对应。

    由于虚拟内存中是连续的,而在物理内存中每个页都是不连续的,被离散的插在了不同地方。所以我们要有一个类似于目录的东西,来找到虚拟内存中的每一块对应到物理内存中的某一块。

    也就是我给你一个虚拟地址,你就要给我一个对应的物理地址,指向某一个数据。

    我们使用一个页表来存储物理内存对应的页表项。这个页表类似于一个目录。

    那么我们页表共有1048576页也就是1M个条目,每个条目占4byte,里面存储的数据是物理页序号。这个物理页序号我们不需要4byte32位来存储,我们只需要其中的20位就可以了。因为只需要定位到是物理哪个页,一共有1M个页。所以20位就够了。其他位作为其他标记。

    每个4byte条目对应表示物理内存中的4K内存空间。

    这样页表就可以和实际物理内存映射起来了。

    一个一级页表大小是4M。

    在这里插入图片描述

    一级页表寻址

    给定一个虚拟地址转换成物理地址
    在这里插入图片描述

    举例:比如给定虚拟地址为0x1234,页部件就是MMU。首先虚拟地址分为两部分高20位和低12位

    MMU取出0x1234的高20位,十六进制表示就是0x00001。他表示一级页表的索引,乘4并且加上一级页表的物理地址,可以定位到一级页表中的某一个PTE

    从PTE中取出内容(存放的是物理地址)就是某个物理页的地址,0x9000,用它就可以定位到物理页的位置。

    虚拟地址的后12位是offset,我们将物理页地址0x9000加上offset=0x234就得到最终的物理地址0x9234

    二级页表

    为何要有二级页表

    一级页表大小位4M。多个进程占用4M也很大了。这是32位系统,如果64位系统那就是2的64次方除以10.非常大,放到内存中是不可能的。

    解决方案

    使用二级页表,动态创建页表项。并不需要把页表一次性都创建好。

    二级页表将1M个标准页4K。平均放到1024个页表中,而不是统一放到一个页表中(1M个PTE)。这样每个二级页表就被分为1024个PTE。也表示4G的内存空间。

    我们用一个页目录表来进行管理1024个二级页表。

    在这里插入图片描述

    每个页表还是4K大小。

    二级页表如何节省空间

    举例:

    如果一个进程只使用了4M的空间。

    1. 如果使用一级页表, 则要使用4M + 4M = 8M
    2. 二级页表:首先页目录占4K,然后他会创建一个二级页表占用4K来表示这个4M。那么实际占用内存就是4M + 4k + 4k.

    二级页表地址转换

    虚拟地址转换为物理地址
    在这里插入图片描述

    举例:

    虚拟地址0x1234567,二级页表虚拟地址分为三部分。

    1. 高10位用来定位页目录项,页目录物理地址是一直有的,然后+高10位乘4就得到页目录中的某个项位置(0x4*4),取出其中的内容是:页表物理地址。
    2. 我们通过页表物理地址定位到某个页表。然后用VA中间10位乘4加上页表物理地址,就得到了页表中的某个PTE的位置。取出PTE内容就是某个物理页的物理地址。
    3. 取到的这个物理地址是某个4K页的物理地址,最后用该物理地址加上第三部分低12位offset就得到最终物理地址。

    计算题:

    假设页长为1KB(1024字节),逻辑地址为2500,请利用页表把逻辑地址转换成物理地址。

    物理地址的计算公式为:物理地址=块的大小(即页的大小L)* 块号f+页内地址d

    代入本题解答:

    页号=int(2500/1024)=2;页内位移=2500mod1024=452;假设页号2对应块号1,则物理地址为:

    物理地址=1024*1+452=1476

    2 虚拟内存管理中为什么会有请求调页?请求调页的过程是什么?

    请求调页

    我们程序都是放在磁盘中,我们CPU执行程序的时候,程序需要加载到内存中,也就是我们如何从磁盘加载可执行程序到内存。

    一种选择是:在程序执行时将整个程序加载到物理内存。(这个我们在前面讲过了,加载整个程序太浪费内存了,又不一定用的到)

    另一种选择是:仅在需要时才加载页面。

    不用想就知道肯定选最后一种,因为程序的局限性一般最近执行的都在这附近的几个页中,等到需要的时候才加载进内存,这种技术被称为请求调页,常常用于虚拟内存系统。

    请求调页的过程

    步骤1: 申请空闲的物理内存页;
    步骤2: 从磁盘读取缺页数据到物理内存页;
    步骤3: 建立虚拟内存与物理内存页的映射关系,即修改页表,新增一条映射记录;

    3 调研常见的页面置换算法(FIFO、MIN、LRU、CLOCK等),并编写程序模拟LRU算法的实现

    1.最佳置换算法(OPT)(理想置换算法):从主存中移出永远不再需要的页面;如无这样的页面存在,则选择最长时间不需要访问的页面。于所选择的被淘汰页面将是以后永不使用的,或者是在最长时间内不再被访问的页面,这样可以保证获得最低的缺页率。
    2.先进先出置换算法(FIFO):是最简单的页面置换算法。这种算法的基本思想是:当需要淘汰一个页面时,总是选择驻留主存时间最长的页面进行淘汰,即先进入主存的页面先淘汰。其理由是:最早调入主存的页面不再被使用的可能性最大。
    3.最近最久未使用(LRU)算法:这种算法的基本思想是:利用局部性原理,根据一个作业在执行过程中过去的页面访问历史来推测未来的行为。它认为过去一段时间里不曾被访问过的页面,在最近的将来可能也不会再被访问。所以,这种算法的实质是:当需要淘汰一个页面时,总是选择在最近一段时间内最久不用的页面予以淘汰。

    FIFO MIN LRU CLOCK算法实现

    #include 
    #include 
    #include 
    
    int numbers[20]={7,0,1,2,
        0,3,0,4,
        2,3,0,3,
        2,1,2,0,
        1,7,0,1};//本地数据,与课本一致,方便测试
    int nums=0;//输入栈的个数,为了方便使用,
    int stack[20][7]={10};
    
    void begin();
    void randomnum();//用于产生随机数
    void init();//初始化
    void FIFO();//FIFO算法
    void LRU();//LRU算法
    void OPT();//最优页面置换算法(OPT)
    void print();//输出
    
    int main() {
        begin();
        FIFO();
        LRU();
        OPT();
        return 0;
    }
    void begin()//开始菜单界面
    {
        int i,j,k;
        printf("请输入页面帧的数量(1-7):");
        scanf("%d",&nums);
        for(k=0;;k++)
        {
            printf("是否使用随机数产生输入串(0:是,1:否)");
            scanf("%d",&j);
            if(j==0)
            {
                randomnum();
                break;
            }
            else if(j==1)
            {
                break;
            }
            else
            {
                printf("请输入正确的选择!");
            }
        }
        
        printf("页面引用串为:");
            for(i=0;i<20;i++)
            {
                printf("%d  ",numbers[i]);
            }
            printf("");
            init();
    }
    void randomnum()//如果需要使用随机数生成输入串,调用该函数
    {
        srand(time(0));//设置时间种子
        for(int i = 0; i < 20; i++) {
            numbers[i] = rand() % 10;//生成区间0`9的随机页面引用串
        }
    }
    void init()//用于每次初始化页面栈中内容,同时方便下面输出的处理
    {
        int i,j;
        for(i=0;i<20;i++)
            for(j=0;j<nums;j++)
                stack[i][j]=10;
    }
    
    void print()//输出各个算法的栈的内容
    {
        int i,j;
        for(i=0;i<nums;i++)
        {
            for(j=0;j<20;j++)
            {
                if(stack[j][i]==10)
                    printf("*  ");
                else
                    printf("%d  ",stack[j][i]);
            }
            printf("");
        }
        
    }
    
    void FIFO()//FIFO算法
    {
        init();
        int i,j=1,n=20,k,f,m;
        stack[0][0]=numbers[0];
        
        for(i=1;i<20;i++)
        {
            f=0;
            for(m=0;m<nums;m++)
            {
                stack[i][m]=stack[i-1][m];
            }
            for(k=0;k<nums;k++)
            {
                if(stack[i][k]==numbers[i])
                {
                    n--;
                    f=1;
                    break;
                }
            }
            if(f==0)
            {
                stack[i][j]=numbers[i];
                j++;
            }
            if(j==nums)
                j=0;
        }
        printf("");
        printf("FIFO算法:");
        print();
        printf("缺页错误数目为:%d",n);
    }
    
    void LRU()//LRU算法
    {
        int i,j,m,k,sum=1,f;
        int sequence[7]={0};//记录序列
        init();
        stack[0][0]=numbers[0];
        sequence[0]=nums;
        for(i=1;i<nums;i++)//前半部分,页面空置的情况
        {
            for(j=0;j<nums;j++)
            {
                stack[i][j]=stack[i-1][j];
            }
            
            for(j=0;j<nums;j++)  //判断要插入的是否在栈中已经存在
            {
                f=0;
                if(stack[i][j]==numbers[i])
                {
                    f=1;
                    sum--;
                    sequence[j]=nums;
                    break;
                }
            }
            
            for(j=0;j<nums;j++)
            {
                if(sequence[j]==0&&f==0)
                {
                    stack[i][j]=numbers[i];
                    sequence[i]=nums;//最近使用的优先级列为最高
                    break;
                }
            }
            for(j=0;j<i;j++)//将之前的优先级序列都减1
            {
                if(sequence[j]!=0)
                    sequence[j]--;
            }
            //sequence[i]=nums;
            sum++;
        }
        
        for(i=nums;i<20;i++)//页面不空,需要替换的情况
        {
            int f;
            f=0;
            for(j=0;j<nums;j++)
            {
                stack[i][j]=stack[i-1][j];
            }
            for(j=0;j<nums;j++)//判断输入串中的数字,是否已经在栈中
            {
                if(stack[i][j]==numbers[i])
                {
                    f=1;
                    k=j;
                    break;
                }
            }
            if(f==0)//如果页面栈中没有,不相同
            {
                for(j=0;j<nums;j++)//找优先序列中为0的
                {
                    if(sequence[j]==0)
                    {
                        m=j;
                        break;
                    }
                }
                for(j=0;j<nums;j++)
                {
                    sequence[j]--;
                }
                sequence[m]=nums-1;
                stack[i][m]=numbers[i];
                sum++;
            }
            else//如果页面栈中有,替换优先级
            {
                if(sequence[k]==0)//优先级为最小优先序列的
                {
                    for(j=0;j<nums;j++)
                    {
                        sequence[j]--;
                    }
                    sequence[k]=nums-1;
                }
                else if(sequence[k]==nums-1)//优先级为最大优先序列的
                {
                    //无需操作
                }
                else//优先级为中间优先序列的
                {
                    for(j=0;j<nums;j++)
                    {
                        if(sequence[k]<sequence[j])
                        {
                            sequence[j]--;
                        }
                    }
                    sequence[k]=nums-1;
                }
            }
        }
        printf("");
        printf("LRU算法:");
        print();
        printf("缺页错误数目为:%d",sum);
    }
    
    void OPT()//OPT算法
    {
        int i,j,k,sum=1,f,q,max;
        int seq[7]={0};//记录序列
        init();
        stack[0][0]=numbers[0];
        seq[0]=nums-1;
        
        for(i=1;i<nums;i++)//前半部分,页面空置的情况
        {
            for(j=0;j<nums;j++)
            {
                stack[i][j]=stack[i-1][j];
            }
            
            for(j=0;j<nums;j++)  //判断要插入的是否在栈中已经存在
            {
                f=0;
                if(stack[i][j]==numbers[i])
                {
                    f=1;
                    sum--;
                    //b++;
                    seq[j]=nums;
                    break;
                }
            }
            
            for(j=0;j<nums;j++)
            {
                if(seq[j]==0&&f==0)
                {
                    stack[i][j]=numbers[i];
                    seq[j]=nums;//最近使用的优先级列为最高
                    break;
                }
                //            else if(seq[j]==0&&f==1){
                //                b++;
                //                sum--;
                //                seq[j]=nums-1;
                //                break;
                //            }
            }
            for(j=0;j<nums;j++)//将之前的优先级序列都减1
            {
                if(seq[j]!=0)
                    seq[j]--;
            }
            
            sum++;
        }
        for(i=nums;i<20;i++)//后半部分,页面栈中没有空的时候情况
        {
            //k=nums-1;//最近的数字的优先级
            for(j=0;j<nums;j++)//前面的页面中内容赋值到新的新的页面中
            {
                stack[i][j]=stack[i-1][j];
            }
            for(j=0;j<nums;j++)
            {
                f=0;
                if(stack[i][j]==numbers[i])
                {
                    f=1;
                    break;
                }
            }
            if(f==0)//页面中没有,需要替换的情况
            {
                for(q=0;q<nums;q++)//优先级序列中最大的就是最久不会用的,有可能出现后面没有在用过的情况
                {
                    seq[q]=20;
                }
                for(j=0;j<nums;j++)//寻找新的优先级
                {
                    for(q=i+1;q<20;q++)
                    {
                        if(stack[i][j]==numbers[q])
                        {
                            seq[j]=q-i;
                            break;
                        }
                    }
                }
                max=seq[0];
                k=0;
                for(q=0;q<nums;q++)
                {
                    if(seq[q]>max)
                    {
                        max=seq[q];
                        k=q;
                    }
                }
                stack[i][k]=numbers[i];
                sum++;
            }
            else
            {
                //页面栈中有需要插入的数字,无需变化,替换的优先级也不需要变化
            }
        }
        printf("");
        printf("OPT算法:");
        print();
        printf("缺页错误数目为:%d",sum);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346

    LRU算法

    #include 
    #include 
    #include 
    //主要数据结构
    #define total_instrucion 15//总的页面访问次数
    #define max_block_num 10//最大内存分配
    
    int Access_Series[total_instrucion];//内存访问序列
    char Lack[total_instrucion];//记录是否缺页
    struct one_block {//一个物理块
        int page_no;//物理块内的页面号
        int tim;//计时,用于确定最近最久未使用的页面(LRU)
    }blocks[max_block_num];
    int table[max_block_num][total_instrucion];//显示矩阵
    void LRU(int block_num){
        int diseffect;//缺页数
        for (int i = 0; i < total_instrucion; i++)
            Lack[i] = 'N';//初始化不缺页
        //数据结构blocks的初始化
        for (int i = 0; i < block_num; i++) {
            blocks[i].page_no = -1;//初始化页号为-1,当前有空闲页面
            blocks[i].tim = 0;//初始化计时
        }
        //开始访问页面
        for (int i = 0; i < total_instrucion; i++) {
            char full = 'T';//是否内存满
            char hit = 'F';//是否命中
            for (int j = 0; j < block_num; j++)
                blocks[j].tim++;//计时器加1
            for (int j = 0; j < block_num; j++) {
                if (blocks[j].page_no == Access_Series[i]) {//命中的情况
                    hit = 'T';
                    blocks[j].tim=0;//刚刚访问,值变为0
                    break;
                }else if (blocks[j].page_no == -1) {//未命中,且内存未满的情况
                    blocks[j].page_no = Access_Series[i];
                    blocks[j].tim=0;//刚刚访问,值变为0
                    Lack[i] = 'Y';//缺页
                    diseffect++;
                    full = 'F';//内存未满
                    break;
                }
            }
            if (full == 'T') {//内存已满
                if (hit == 'F') {//若未命中
                    diseffect++;
                    Lack[i] = 'Y';
                    int target = 0;//选中替换目标
                    for (int j = 1; j < block_num; j++) {
                        if (blocks[j].tim>blocks[target].tim)
                            target = j;
                    }
                    blocks[target].page_no = Access_Series[i];
                    blocks[target].tim=0;//刚刚访问,值变为0
                }
            }
            for (int j = 0; j < block_num; j++)
                table[j][i] = blocks[j].page_no;
        }
        
        /*输出运行过程及结果*/
        printf("采用LRU页面置换算法结果如下:");
        printf("\n");
        printf("页号:");
        for (int i = 0; i < total_instrucion; i++)
            printf("%3d", Access_Series[i]);
        printf("\n");
        printf("-----------------------------------------------------\n");
        for (int i = 0; i < block_num; i++) {
            printf("块%2d:", i+1);
            for (int j = 0; j < total_instrucion; j++)
                printf("%3d", table[i][j]);
            printf("\n");
        }
        printf("-----------------------------------------------------\n");
        printf("缺页:");
        for (int i = 0; i < total_instrucion; i++)
            printf("%3c", Lack[i]);
        printf("\n");
        printf("-----------------------------------------------------\n");
        printf("\t缺页次数:%d\t缺页率:%d/%d\n", diseffect, diseffect, total_instrucion);
        printf("-----------------------------------------------------\n");
    }
    int main(){
        //初始化
        //父进程随机产生内存访问页面序列,存于数组Acess_Series[total_instruction]中
        srand((unsigned)time(NULL));
        for (int i = 0; i < total_instrucion; i++)
            Access_Series[i] = rand() % total_instrucion;
        int block_num;
        while(1){
            printf("请输入分配的物理块数(-1退出):");
            scanf("%d",&block_num);
            if(block_num==-1) break;
            LRU(block_num);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
  • 相关阅读:
    [Spring MVC6]事务管理与缓存机制
    nginx
    Java并发编程学习六:阻塞队列
    Wireshark抓包工具配置以及MQTT抓包分析
    优先队列题目:多次求和构造目标数组
    【ARM Trace32(劳特巴赫) 使用介绍 2 -- Trace32 cmm 脚本基本语法及常用命令】
    React中setState的异步与合并
    2-3 Moves Codeforces 1716A
    【AN-Animate教程——熟悉工作区】
    KeyarchOS的CentOS迁移实践:使用操作系统迁移工具X2Keyarch V2.0
  • 原文地址:https://blog.csdn.net/L6666688888/article/details/127785882