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

本文涉及的产品
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
简介: 【操作系统--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


目录
相关文章
|
10月前
|
Ubuntu 物联网 Linux
从零安装一个Linux操作系统几种方法,以Ubuntu18.04为例
一切就绪后,我们就可以安装操作系统了。当系统通过优盘引导起来之后,我们就可以看到跟虚拟机中一样的安装向导了。之后,大家按照虚拟机中的顺序安装即可。 好了,今天主要介绍了Ubuntu Server版操作系统的安装过程,关于如何使用该操作系统,及操作系统更深层的原理,还请关注本号及相关圈子。
|
8月前
|
存储 Linux 开发工具
Linux环境下使用Buildroot配置软件包
使用Buildroot可以大大简化嵌入式Linux系统的开发和维护工作,但它需要对Linux系统和交叉编译有深入的理解。通过上述步骤,可以有效地配置和定制软件包,为特定的嵌入式应用构建高效、稳定的系统。
924 11
|
10月前
|
存储 监控 Linux
Linux环境锁定关键文件防止误删操作流程。
总结以上内容,在Linux环境下锁定重要文档避免误删涉及到多种技术手段与策略组合运作, 包括但不限于利用chatter指挥官固化文档状态至只读模式、运作ACL精准调整访问权利列表、编排自动化流程简
492 20
|
10月前
|
Linux
Linux环境下的UDEV机制及其与守护进程的关联
实际使用时管理员需要熟悉编写合适udev rules去满足特殊需求;同时也需要注意避免编写过度复杂导致无法预料结果rules.UDEVD虽然稳健但错误配置可能导致无法预料问题因此需谨慎处理相关配置工作.
366 16
|
10月前
|
存储 Linux
Linux环境下删除大文件后磁盘空间未释放问题诊断流程。
以上诊断流程涉及Linux底层机制与高级管理技能结合之处,并需要管理员根据实际环境灵活调整诊断策略与解决方案。
719 8
|
10月前
|
监控 Ubuntu Linux
什么Linux,Linux内核及Linux操作系统
上面只是简单的介绍了一下Linux操作系统的几个核心组件,其实Linux的整体架构要复杂的多。单纯从Linux内核的角度,它要管理CPU、内存、网卡、硬盘和输入输出等设备,因此内核本身分为进程调度,内存管理,虚拟文件系统,网络接口等4个核心子系统。
1027 0
|
10月前
|
Unix 物联网 Linux
都什么年代了,你还不懂啥是Linux操作系统
至于华为鸿蒙操作系统是不是独树一帜,这个留给各位阅读本文的网友们来讨论
514 0
|
10月前
|
安全 Linux iOS开发
linux属于什么操作系统
Linux是一种自由和开放源代码的操作系统,具有高度的灵活性和可定制性。与常见的操作系统如Windows和macOS相比,Linux具有自由、安全和稳定等优势。Linux已广泛应用于服务器、桌面电脑、超级计算机和嵌入式设备等领域,并且在未来的发展前景广阔。由于其自由和开放源代码的特性,Linux还促进了计算机技术和社区的发展,为全球的计算机用户提供了更多的选择和可能性。
|
10月前
|
安全 Ubuntu Unix
关于Linux操作系统,你必须要知道的事
我们可以看到无论是Debian还是Buildroot都有各自的特点,为客户提供了更大的选择空间和灵活性,大家可以根据自己的需求选择合适的版本来满足终端用户的体验和功能需求。从平技术将会一直关注更多更安全、灵敏、易于开发的Linux版本,做好适配工作,不断为客户带来“简单开发、方便应用”的使用体验。