【操作系统--MMU内存管理单元】Linux环境下C语言详解(附代码)

本文涉及的产品
实时数仓Hologres,5000CU*H 100GB 3个月
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 【操作系统--MMU内存管理单元】Linux环境下C语言详解(附代码)

 一、内容与要求

Linux下C语言编程模拟内存管理单元。

通过本实验,学生应达到如下要求:

1)理解操作系统中内存管理的基本方法

2)理解逻辑地址空间与物理地址空间、地址保护与地址越界等概念

3)熟练掌握分页系统的逻辑地址到物理地址的转换过程

4) 理解页表的作用和快表(TLB)的作用

5)能够熟练使用C语言编程模拟实现MMU的地址映射算法。

二、思路分析

1)实验目的:

假设某小型分页系统的逻辑地址空间8KB,物理地址空间16KB,页面大小1KB按字节寻址.设计一个模拟程序,实现操作系统中内存管理单元(MMU)从逻辑地址到物理地址的转换算法.

2)系统分析和设计

a.逻辑地址构成分析:

逻辑地址空间8KB(0~8191B),故用13个二进制表示一个逻辑地址。页大小1K,逻辑地址空间分成8页,故逻辑地址的高3位表示页面号p,低1 0位表示页内偏移d。

image.gif编辑

b.物理地址构成分析:

物理地址空间16KB(0 ~16383B),故用14个二进制表示一个物理地址。帧大小与页大小相同,物理地址空间分成16帧,故物理地址的高4位为帧号f,低10位表示页内偏移d。

image.gif编辑

c.页表条目分析:

页表共8个条目,定义一个长度为8的整型数组来存放页表,故每个条目占32位。物理地址空间共分16帧,故每个条目需要4位存储帧号,其它位用于控制位和扩展位,本实验设计的模拟程序仅需维护低8位。页表的每个条目构成如下:image.gif编辑

有效无效位:小于进程所需的实际页面数的条目的有效无效位设置为1,表示有效页,反之,设置为0,表示无效页,MMU不解析无效页的地址。

修改位:某次访问地址是否修改所在页,本实验中对某地址随机决定是否修改页面,若地址访问将写页面,设置为1,若地址访问仅读页面设置为0。

访问位:访问地址时设置其所在页面的访问位为1,与修改位一起用于TLB的替换算法。

保护位:表示访问权限,1表示只读,0表示读/写,本实验全部页面设置为0。

d.TLB条目分析:

TLB共4个条目,可缓存刚才访问过的4个页面,存储其页面号和相应帧号。定义一个长度为4的整型数组存放TLB,每个条目32位,其中低8位与页表每个条目的低8位相同。逻辑地址空间共8页,故TLB中需要有3位存放刚刚访问过的页面号。TLB的每个条目构成如下:

image.gif编辑

3)定义数据结构

#define PSIZE 1024  //页大小

#define DLEN 10  //逻辑地址和物理地址中页内偏移所占的位数

#define PNUM 8    //逻辑页面数

#define PLEN 3 //逻辑地址中页号所占的位数

#define FNUM 16  //物理帧数

#define FLEN 4    //页表条目中物理帧数占4位,可表示16帧

#define TNUM 4   //TLB中缓存的条目数

int PageTable[PNUM]; //页表数组

页表中存储如下帧号(页号是数组下标):

高24位

低8位

数组赋值

页号

扩展位

保护位

修改位

访问位

有效位

帧号(4位)

十六进制

第0页

0x000000

0

0

0

0

12 (1100)

0x000C

第1页

0x000000

0

0

0

0

8 (1000)

0x0008

第2页

0x000000

0

0

0

0

5 (0101)

0x0005

第3页

0x000000

0

0

0

0

10(1010)

0x000A

第4页

0x000000

0

0

0

0

15(1111)

0x000F

第5页

0x000000

0

0

0

0

0(0000)

0x0000

第6页

0x000000

0

0

0

0

15(1111)

0x000F

第7页

0x000000

0

0

0

0

15(1111)

0x000F

开始地址转换之前,先根据进程实际页面数,刷新有效无效位。

int TLB[TNUM]; //快表数组

块表需要保存页号和帧号,高位中需要3个二进制位保存页号。

初始化全为0,无任何条目,假设依次访问第0,1,2,4,页,则TLB中的条目为0,若下一个地址需要范围第3页,则发生TLB替换。

11-31位

8-10位

低8位

扩展位

页号

保护位

修改位

访问位

有效位

帧号(4位)

十六进制

全0

000

0

0

0

1

12 (1100)

0x001C

全0

001

0

0

0

1

8 (1000)

0x0118

全0

010

0

0

0

1

5 (0101)

0x0215

全0

100

0

0

0

1

10(1010)

0x001A

4)MMU地址转换算法

MMU地址转换过程(不含TLB):

算法描述:

MMU(){

   Step1:解析逻辑地址,取得页号和帧号(使用移位>>和按位与&操作)

        d=v_addr&0x03FF;//逻辑地址的低10位  p=v_addr%1024  

        p=v_addr>>DLEN;//逻辑地址右移10位,取页号 p=v_addr/1024

  Step2: 根据p查找TLB,TLB命中返回f ,不命中返回-1;

f=SearchTLB();

  Step3: 如果f==-1,直接访问PageTable[p],解析条目取得帧号f以及有效无效位v;

         v=(PageTable[p]>>FLEN)&0x1; //取有效无效位

         若v==0, 地址错误,退出

         若 v==1, 则提取低4位,得到帧号f=PageTable[p]&0xF;

  Step5: 计算物理地址

p_addr=(f<<DLEN)+d;

  Step6: 更新页表的访问位和修改位

  Step7: 将该页缓存到TLB中。UpdateTLB();

}

三、测试数据

1)输入进程大小:7000(字节)

查看页表,有效页面数量是否正确

2)依次输入逻辑地址:                                      

  9000:越界

  8000:无效页

  7000:内部碎片区域

  6000:  

  6500:

  5000:

  4500:

  0:

  4000:

  3000:

  1000:

  2000:

 验证每个逻辑地址到物理地址的转换是否正确,以及页表和TLB的信息是否正确

四、结果与分析

初始值7000

image.gif编辑

分析:[7000/1024]=7(向上取整),慢表中确实有7条有效位为1的条目

8000

image.gif编辑

分析:8000超过了7000B进程的范围,内容正确

image.gif编辑

分析:由于存储在内存中的数据地址是0位开始的,而我存入的进程的大小为7000B,而现在我查询的数据逻辑地址为7000,就刚刚好超过了进程的逻辑地址的下表,所以虽然是在有效页面中,但是是子啊内部碎片区域

6000

image.gif编辑

分析:快表未满,更新快表的第一条

5000

image.gif编辑

分析:快表未满,更新快表的第二条

4500

image.gif编辑

0

image.gif编辑

分析:快表未满,更新快表的第三条

4000

image.gif编辑 分析:快表未满,更新快表的第四条

3000

image.gif编辑

分析:快表已满,随机更新快表

1000

image.gif编辑

2000

image.gif编辑

分析:快表已满,随机更新快表

五、代码

#include <stdio.h>
#include <stdlib.h>
#define PSIZE 1024  //页大小
#define DLEN 10  //逻辑地址和物理地址中页内偏移所占的位数
#define PNUM 8    //逻辑页面数
#define PLEN 3    //逻辑地址中页号所占的位数,页号为0~7,故TLB中需要3位二进制来保存页号
#define FNUM 16  //物理帧数
#define FLEN 4    //页表条目中物理帧数占4位,可表示16帧
#define TNUM 4   //TLB中缓存的条目数
//初始化慢表和快表 
int PageTable[PNUM]={0x000C,0x0008,0x0005,0x000A,0x000F,0x0000,0x000E,0x000F};
int  TLB[TNUM]={0x0000,0x0000,0x0000,0x0000}; 
//展示页表具体信息 
void showtable()
{
   //TLB会先转换为2进制 
  //>>n位相当于把后面的n个位都去掉, 然后与0x1位与操作相当于就把最后一位给掏出来 
  printf("\n快表TLB:\n");
  printf("页号   保护位  修改位  访问位   有效位  帧号    十六进制\n");   
  for(int i=0;i<TNUM;i++)
  {   printf(" %d\t",TLB[i]>>8);  //8号位为页号 
    printf(" %d\t",(TLB[i]>>7)&0x1);  //7号位为保护位 
    printf(" %d\t",(TLB[i]>>6)&0x1);  //6号位为修改位 
    printf(" %d\t",(TLB[i]>>5)&0x1);  //5号位为访问位 
    printf(" %d\t",(TLB[i]>>4)&0x1);  //4号位为有效位 
    printf(" %d\t",TLB[i]&0xF);   //0~3号位为帧号 
    printf(" 0x%04X\n",TLB[i]);   //直接输出为4位16进制,不足的补上0 
  }
  printf("\n慢表PageTable:\n");
  printf("页号   保护位  修改位  访问位   有效位  帧号    十六进制\n");
  for(int i=0;i<PNUM;i++)
  { 
      printf("第%d页\t",i); 
      //与快表取法相同 
    printf(" %d\t",(PageTable[i]>>7)&0x1);
    printf(" %d\t",(PageTable[i]>>6)&0x1);
    printf(" %d\t",(PageTable[i]>>5)&0x1);
    printf(" %d\t",(PageTable[i]>>4)&0x1);
    printf(" %d\t",PageTable[i]&0xF);
    printf(" 0x%04X\n",PageTable[i]);
  }  
}
int UpdateTLB(int ppp)
{
    printf("更新TLB\n");
    int i;
    int sum=0;
   for( i=0;i<TNUM;i++) //遍历TLB
   { 
       if(TLB[i]>>8&0x7==ppp){ printf("快表找到了!!!\n");return 0;}  //找到条目就溜了 
     if(TLB[i]==0x0000){
     sum++;}
    }
    if(sum!=0)
    {  
      for(int j=0;j<TNUM;j++)
    {if(TLB[j]==0x0000){
    printf("替换TLB中第%d个条目!\n",j+1);
      TLB[j]=(ppp<<8)+(PageTable[ppp]&0x00FF); 
      return 0;}
     } 
  } 
    else
    {  
    int k= rand()%TNUM;
      printf("替换TLB中第%d个条目!\n",k+1);
      TLB[k]=(ppp<<8)+(PageTable[ppp]&0x00FF); 
  } 
    return 0;     
}
int SearchTLB(int m) // 1.找页号 2.查看有效位 
{
   int p,valid; //p页号,valid有效无效位
   int i=0;
   for( i;i<TNUM;i++) 
   { p=TLB[i]>>8&0x7;//找页号 
     if(p==m)
  {
     valid=(TLB[i]>>4)&0x1;//查看有效位 
     if(valid==1)return TLB[i]&0xF;// 若该页有效,取帧号
     else return -1;
  } }
    if(i==TNUM)return -1;//没找到帧号 
} 
int MMU( int logica,int size,int fangwen) //modify表示访问方式
{
   int physicsa,d,p,valid,f; //p页号,d页内偏移,f帧号,valid有效位 
   int i=0;
   d=logica&0x03FF;//逻辑地址的低10位  p=l_addr%1024 
   p=logica>>DLEN;//逻辑地址右移10位,取页号 p=l_addr/1024
   printf("进程长度:%dB,页内偏移%d,页号%d\n",size,d,p);
  f = SearchTLB(p);
  if (f == -1)
       printf("快表无数据,访问慢表\n");
  else {
       printf("快表有数据,直接输出\n");
       physicsa = (f << DLEN) + d;
       return physicsa;
  }
   valid=(PageTable[p]>>FLEN)&0x1;
   if(valid==0) { printf("无效页面\n");return -1;}//无效页
   else 
   {  printf("有效页面\n");
      if(logica>=size) //内部碎片地址 
      {
         printf("内部碎片区域\n");
         return -1;
       }
  if(fangwen==1){
    PageTable[p]|=0x0040;}//更新 6号修改位 
    PageTable[p]|=0x0020;//更新 5号访问位 
    f=PageTable[p]&0xF;//取低4位, 得到帧号
      physicsa=(f<<DLEN)|d;//计算物理地址
    UpdateTLB(p);
      return physicsa;
    }
}
int main()
{ 
                                          //初始化进程,修改慢表,然后show一下 
    int P,logica,physicsa;            //logica测试的逻辑地址,physicala转化的物理地址。 
    printf("请输入进程大小\n");  
    do{
       scanf("%d",&P);
         if(P>=PNUM*PSIZE||P<0) 
         {printf("小老弟你真皮,越界输入\n");
       }
      }while(P>=PNUM*PSIZE||P<0); 
    for(int i=0;i<P/PSIZE+1;i++)
     {  PageTable[i] +=0x0010; 
     }
    showtable(); 
                                                                   //测试逻辑地址 
    while(true){ 
      printf("请输入一个测试逻辑地址\n");
        scanf("%d",&logica);
        if(logica<0) {printf("小老弟你真皮,地址越界\n");continue;} 
        if(logica>=PNUM*PSIZE) { printf("小老弟你真皮,地址越界\n");continue;}
        int m=rand()%2; //随机确定是否写页面
        if(m==1)                                                    //写界面 
           {   
      printf("\n************************************************写页面**************************************************\n");
      printf("\n********************************************************************************************************\n");
      physicsa=MMU(logica,P,1);
           }
        else                                                       //读界面 
      {   
      printf("\n************************************************读页面*************************************************\n");
      printf("\n*******************************************************************************************************\n");
      physicsa=MMU(logica,P,0);
            }
        if(physicsa<0) printf("内存管理失败\n"); 
        else{ 
        printf("恭喜您,地址转化成功"); 
    printf("由逻辑地址:0x%04X转化物理地址为:0x%04X\n",logica,physicsa);
        showtable(); } 
    }        
    return 0;
}

image.gif


相关实践学习
CentOS 7迁移Anolis OS 7
龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
目录
相关文章
|
13天前
|
安全 Linux 数据安全/隐私保护
Vanilla OS:下一代安全 Linux 发行版
【10月更文挑战第30天】
34 0
Vanilla OS:下一代安全 Linux 发行版
|
16天前
|
人工智能 安全 Linux
|
21天前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
33 3
|
1月前
|
Unix 物联网 大数据
操作系统的演化与比较:从Unix到Linux
本文将探讨操作系统的历史发展,重点关注Unix和Linux两个主要的操作系统分支。通过分析它们的起源、设计哲学、技术特点以及在现代计算中的影响,我们可以更好地理解操作系统在计算机科学中的核心地位及其未来发展趋势。
|
1月前
|
编译器 程序员 C语言
深入C语言:动态内存管理魔法
深入C语言:动态内存管理魔法
|
1月前
|
监控 开发者
深入理解操作系统:内存管理的艺术
【10月更文挑战第2天】在数字世界的幕后,操作系统扮演着至关重要的角色。本文将深入探索操作系统的心脏——内存管理,揭示它是如何协调和管理计算机的宝贵资源。通过浅显易懂的语言和生活化的比喻,我们将一起走进内存管理的奥秘世界,了解它的原理、机制以及为何对整个系统的性能和稳定性有着不可替代的影响。无论你是技术新手还是资深开发者,这篇文章都将为你打开新的视角,让你对日常使用的设备有更深层次的认识和尊重。
|
1月前
|
存储 程序员 编译器
C语言——动态内存管理与内存操作函数
C语言——动态内存管理与内存操作函数
|
1月前
|
C语言
保姆级教学 - C语言 之 动态内存管理
保姆级教学 - C语言 之 动态内存管理
19 0
|
1月前
|
存储 程序员 C语言
C语言动态内存管理
C语言动态内存管理
|
1月前
|
存储 算法 C语言
MacOS环境-手写操作系统-17-内存管理算法实现
MacOS环境-手写操作系统-17-内存管理算法实现
36 0