【操作系统--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。

b.物理地址构成分析:

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

c.页表条目分析:

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

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

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

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

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

d.TLB条目分析:

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

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

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

8000

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

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

6000

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

5000

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

4500

0

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

4000

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

3000

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

1000

 2000

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

五、代码

#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;
}



 

  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
【课程简介】本课程是《Armv8/Armv9架构从入门到精通 第二期》中的第四章。建议购买大课程。本课程以为armv8-aarch64、armv9为基准,不涉及armv7及其以前的版本,也不涉及aarch32。本课程包含但不限于以下内容MMU的基础学习:啥是MMU?工作原理?MMU和cache之间的关系?及其怎样相互影响的?哪些是MMU硬件自动的行为?哪些是软件可配置的行为?地址空间基础:啥是虚拟地址空间?啥是物理地址空间?啥时memory-map? 代码程序中的虚拟地址空间是怎样的?arm core硬件决定的物理地址空间是怎样的?SOC memory-map时的地址空间是怎样的?具体的外设又是怎样的地址空间?Translation Regime:系统里有多少个MMU?系统里有多少个Translation Regime?他们之间的关系是怎样的?页表翻译:MMU是怎样翻译的?页表有几级?这些信息是怎么告诉MMU的,MMU又是怎样工作的,软件又需要怎样设计?stage1和stage2的区别和使用?Descriptor:啥是Descriptor、页表项、entry、条目、页表条目? Descriptor的格式是怎样的?每一个属性位是怎样的?MMU除了完成地址转换,属性权限的控制/cache的缓存策略是怎样配置的?optee中使用MMU的示例MMU深度思考篇:开启MMU瞬间可能出现的问题以及多种常用的解决方案。注意这里提到的是多种方案,绝大多数人只知道第一种,除此之外还有没有其它的设计方式? MMU关闭时cache的缓存策略会怎样?【思考】01、一个大系统中有多少个MMU ?02、一个ARM Core中有多少个Translation regime?03、EL1&0 Translation regime Stage2、EL2 Translation regime stage1、EL2&0 Translation regime stage1 这三者的区别是什么?04、TTBR1_EL2寄存器有啥特殊性,这个寄存器是给谁使用的?05、有没有TTBR1_EL3寄存器?为什么?06、什么是memory-map? 一个ARM的系统中,有几套物理地址空间?07、页表到底有几级? 页表最少可以有几级?页表最多可以有几级?08、页表有多大? 页表存放在哪里?页表由谁来管理?09、页表是否能放在cache中?10、什么是Translation Table walk? 什么是TLB?11、请简述页表查询的过程?12、在一个大系统中,物理地址是多少位的? 物理地址是多少位指的是什么?虚拟地址又是多少位的? 物理地址的位数和虚拟地址的位数都是由谁来决定的?13、请说明entry、descriptor、页表项、条目 这个四个词汇的由来?14、TCR中有缓存属性和共享属性、页表的entry中也有缓存属性和共享属性,这俩有什么区别?15、请简述TTBR0和TTBR1的概念和意义?16、请简述cacheability和shareability属性的含义?17、stage1和stage2有何区别?18、stage1和stage2的descriptor中,有很多重复的属性,当两者属性有冲突了怎么办?19、L1、L2、L2的descriptor中也有部分相同的属性,重复时怎么办?20、Descriptor的格式有哪几种?21、Descriptor的类型有哪几种?22、简述您所观察到的系统软件中(操作系统或hypervisor)的shareability和cacheability一般都是怎样配置的?23、开启一个MMU的步骤是怎样的?24、啥是flat map? 啥是full level table?25、页表中的AF属性位、DBM属性位分别是干啥的?26、页表中的nG属性位是干啥的?27、页表中的Contiguous属性位是干啥的?28、在开启MMU的瞬间会考虑哪些事情?【课程目录】当前21节课,6.8小时

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北村南

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值