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

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
实时数仓Hologres,5000CU*H 100GB 3个月
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 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的迁移。
目录
相关文章
|
26天前
|
Unix 编译器 Shell
[oeasy]python0033_先有操作系统还是先有编程语言_c语言是怎么来的
本文回顾了计算机语言与操作系统的起源,探讨了早期 Unix 操作系统及其与 C 语言的相互促进发展。Unix 最初用汇编语言编写,运行在 PDP-7 上,后来 Thompson 和 Ritchie 开发了 C 语言及编译器,使 Unix 重写并成功编译。1974 年 Ritchie 发表论文,Unix 开始被学术界关注,并逐渐普及。伯克利分校也在此过程中发挥了重要作用,推动了 Unix 和 C 语言的广泛传播。
40 9
[oeasy]python0033_先有操作系统还是先有编程语言_c语言是怎么来的
|
5天前
|
安全 Linux 数据安全/隐私保护
探索Linux操作系统的文件权限管理
【9月更文挑战第29天】在数字世界中,文件权限管理如同保护我们隐私的锁。本文将带你了解如何在Linux系统中设置和管理文件权限,确保你的数据安全。我们将一起学习如何通过命令行工具来控制文件访问,就像学习一门新语言一样有趣。准备好了吗?让我们一起开启这场技术之旅!
|
23天前
|
存储 安全 Linux
探索Linux操作系统的心脏:内核
在这篇文章中,我们将深入探讨Linux操作系统的核心—内核。通过简单易懂的语言和比喻,我们会发现内核是如何像心脏一样为系统提供动力,处理数据,并保持一切顺畅运行。从文件系统的管理到进程调度,再到设备驱动,我们将一探究竟,看看内核是怎样支撑起整个操作系统的大厦。无论你是计算机新手还是资深用户,这篇文章都将带你领略Linux内核的魅力,让你对这台复杂机器的内部运作有一个清晰的认识。
52 3
|
23天前
|
存储 数据挖掘 Linux
服务器数据恢复—Linux操作系统网站服务器数据恢复案例
服务器数据恢复环境: 一台linux操作系统服务器上跑了几十个网站,服务器上只有一块SATA硬盘。 服务器故障: 服务器突然宕机,尝试再次启动失败。将硬盘拆下检测,发现存在坏扇区
|
1月前
ARM64技术 —— MMU处于关闭状态时,内存访问是怎样的?
ARM64技术 —— MMU处于关闭状态时,内存访问是怎样的?
|
21天前
|
存储 监控 安全
探究Linux操作系统的进程管理机制及其优化策略
本文旨在深入探讨Linux操作系统中的进程管理机制,包括进程调度、内存管理以及I/O管理等核心内容。通过对这些关键组件的分析,我们将揭示它们如何共同工作以提供稳定、高效的计算环境,并讨论可能的优化策略。
22 0
|
2月前
|
网络协议 Linux Shell
探索Linux操作系统:从基础到高级编程
【8月更文挑战第31天】本文旨在为读者提供一条清晰的路径,从Linux操作系统的基础知识出发,逐步深入到高级编程技巧。我们将一起揭开Linux神秘的面纱,了解其内部工作原理,并通过实际代码示例加深理解。无论你是初学者还是有一定经验的开发者,这篇文章都将为你带来新的视角和技能提升。
|
3月前
|
缓存 监控 关系型数据库
深入理解Linux操作系统的内存管理机制
【7月更文挑战第11天】在数字时代的浪潮中,Linux操作系统凭借其强大的功能和灵活性,成为了服务器、云计算以及嵌入式系统等领域的首选平台。内存管理作为操作系统的核心组成部分,对于系统的性能和稳定性有着至关重要的影响。本文将深入探讨Linux内存管理的基本原理、关键技术以及性能优化策略,旨在为读者提供一个全面而深入的理解视角,帮助开发者和系统管理员更好地优化和管理Linux系统。
|
5月前
|
算法 安全 Linux
深度解析:Linux内核内存管理机制
【4月更文挑战第30天】 在操作系统领域,内存管理是核心功能之一,尤其对于多任务操作系统来说更是如此。本文将深入探讨Linux操作系统的内核内存管理机制,包括物理内存的分配与回收、虚拟内存的映射以及页面替换算法等关键技术。通过对这些技术的详细剖析,我们不仅能够理解操作系统如何高效地利用有限的硬件资源,还能领会到系统设计中的性能与复杂度之间的权衡。
|
5月前
|
缓存 监控 算法
Linux内核的SLAB内存管理机制
Linux内核的SLAB内存管理机制
292 4
下一篇
无影云桌面