ARM MMU 详解

2023-11-02

一、MMU 的产生

许多年以前,当人们还在使用DOS或是更古老的操作系统的时候,计算机的内存还非常小,一般都是以 K 为单位进行计算,相应的,当时的程序规模也不大,所以内存容量虽然小,但还是可以容纳当时的程序。

但随着图形界面的兴起,还有用户需求的不断增大,应用程序的规模也随之膨胀起来,终于一个难题出现在程序员的面前,那就是应用程序太大以至于内存容纳不下该程序,通常解决的办法是把程序分割成许多称为覆盖块(overlay)的片段。覆盖块 0 首先运行,结束时他将调用另一个覆盖块。虽然覆盖块的交换是由 OS 完成的,但是必须先由程序员把程序先进行分割,这是一个费时费力的工作,而且相当枯燥。人们必须找到更好的办法从根本上解决这个问题。

不久人们找到了一个办法,这就是虚拟存储器(virtual memory).。虚拟存储器的基本思想是程序,数据,堆栈的总的大小可以超过物理存储器的大小,操作系统把当前使用的部分保留在内存中,而把其他未被使用的部分保存在磁盘上。比如对一个 16MB 的程序和一个内存只有 4MB 的机器,OS 通过选择,可以决定各个时刻将哪 4M 的内容保留在内存中,并在需要时在内存和磁盘间交换程序片段,这样就可以把这个 16M 的程序运行在一个只具有 4M 内存机器上了。而这个 16M 的程序在运行前不必由程序员进行分割。

任何时候,计算机上都存在一个程序能够产生的地址集合,我们称之为地址范围。这个范围的大小由 CPU 的位数决定,例如一个 32 位的 CPU,它的地址范围是 0 ~ 0xFFFFFFFF (4G)。而对于一个 64 位的 CPU,它的地址范围为 0 ~ 0xFFFFFFFFFFFFFFFF (64T), 这个范围就是我们的程序能够产生的地址范围,我们把这个地址范围称为虚拟地址空间,该空间中的某一个地址我们称之为虚拟地址。与虚拟地址空间和虚拟地址相对应的则是物理地址空间和物理地址,大多数时候我们的系统所具备的物理地址空间只是虚拟地址空间的一个子集,这里举一个最简单的例子直观地说明这两者,对于一台内存为 256MB 的 32 bit x86 主机来说,它的虚拟地址空间范围是0 ~ 0xFFFFFFFF(4G), 而物理地址空间范围是 0x000000000 ~ 0x0FFFFFFF(256MB)。

在没有使用虚拟存储器的机器上,虚拟地址被直接送到内存总线上,使具有相同地址的物理存储器被读写。而在使用了虚拟存储器的情况下,虚拟地址不是被直接送到内存地址总线上,而是送到内存管理单元—— MMU(主角终于出现了)。他由一个或一组芯片组成,一般存在于协处理器中,其功能是把虚拟地址映射为物理地址。


二、 MMU工作过程

大多数使用虚拟存储器的系统都使用一种称为分页(paging)。虚拟地址空间划分成称为页(page)的单位,而相应的物理地址空间也被进行划分,单位是页框(frame). 页和页框的大小必须相同。接下来配合图片我以一个例子说明页与页框之间在 MMU 的调度下是如何进行映射的:

在这里插入图片描述
在这个例子中,我们有一台可以生成 16 位地址的机器,它的虚拟地址范围从0x0000 ~ 0xFFFF(64K),而这台机器只有 32K 的物理地址,因此他可以运行 64K 的程序,但该程序不能一次性调入内存运行。

这台机器必须有一个达到可以存放 64K 程序的外部存储器(例如磁盘或是 FLASH)以保证程序片段在需要时可以被调用。在这个例子中,页的大小为 4K,页框大小与页相同(这点是必须保证的,内存和外围存储器之间的传输总是以页为单位的),对应 64K 的虚拟地址和 32K 的物理存储器,他们分别包含了 16 个页和 8 个页框。

我们先根据上图解释一下分页后要用到的几个术语,在上面我们已经接触了页和页框,上图中绿色部分是物理空间,其中每一格表示一个物理页框。橘黄色部分是虚拟空间,每一格表示一个页,它由两部分组成,分别是 Frame Index (页框索引)和 位 p(present 存在位),Frame Index的意义很明显,它指出本页是往哪个物理页框进行映射的,位 p 的意义则是指出本页的映射是否有效,如上图,当某个页并没有被映射时(或称映射无效,Frame Index 部分为 X),该位为 0,映射有效则该位为 1。


我们执行下面这些指令(本例子的指令不针对任何特定机型,都是伪指令)

例1:

MOVE REG, 0 //将 0 号地址的值传递进寄存器 REG.

虚拟地址 0 将被送往 MMU, MMU 看到该虚地址落在页 0 范围内(页 0 范围是 0 到 4095),从上图我们看到页 0 所对应(映射)的页框为 2(页框 2 的地址范围是 8192 到 12287),因此MMU 将该虚拟地址转化为物理地址 8192,并把地址 8192 送到地址总线上。内存对 MMU 的映射一无所知,它只看到一个对地址 8192 的读请求并执行它。MMU 从而把 0 到 4096 的虚拟地址映射到 8192 到 12287 的物理地址。


例2:

MOVE REG,8192

被转换为

MOVE REG,24576

因为虚拟地址 8192 在页 2 中,而页 2 被映射到页框 6(物理地址从 24576 到 28671)。


例3:

MOVE REG,20500

被转换为

MOVE REG,12308

虚拟地址 20500 在虚页 5(虚拟地址范围是 20480 到 24575 )距开头 20 个字节处,虚页 5 映射到页框 3(页框 3 的地址范围是 12288 到 16383),于是被映射到物理地址 12288+ 20=12308。

通过适当的设置 MMU,可以把 16 个虚页映射到 8 个页框中的任何一个,但是这个方法并没有有效的解决虚拟地址空间比物理地址空间大的问题。从上图中我们可以看到,我们只有 8 个页框(物理地址),但我们有 16 个页(虚拟地址),所以我们只能把 16 个页中的 8 个进行有效的映射。我们看看例 4 会发生什么情况。


例 4:

MOV REG, 32780

虚拟地址 32780 落在页 8 的范围内,从上图中我们看到,页 8 没有被有效的进行映射(该页被打上X),这时又会发生什么?MMU 注意到这个页没有被映射,于是通知 CPU 发生一个缺页故障(page fault).这种情况下操作系统必须处理这个页故障,它必须从 8 个物理页框中,找到 1 个当前很少被使用的页框,并把该页框的内容写入外围存储器(这个动作被称为 page copy),随后把需要引用的页(即虚拟地址空间的 页 8)映射到刚才释放的页框中(这个动作称为修改映射关系),然后重新执行产生故障的指令(MOV REG, 32780)。

假设操作系统决定释放页框 1,那么它将把虚页 8 装入物理地址的 4-8 K 地址范围, 并做两处修改:首先把标记虚页1未被映射(原来虚页1是被映射到页框 1 的),以使以后任何对虚拟地址 4K ~ 8K的访问都引起页故障,而使操作系统做出适当的动作(这个动作正是我们现在在讨论的),其次他把虚页 8 对应的页框号由 X 变为 1,因此重新执行 MOV REG,32780 时,MMU 将把 32780映射为 4108 。

我们大致了解了 MMU 在我们的机器中扮演了什么角色以及它基本的工作内容是什么,下面我们将举例子说明它究竟是如何工作的(注意,本例中的 MMU 并无针对某种特定的机型,它是所有 MMU 工作的一个抽象)。


首先明确一点,MMU 的主要工作只有一个,就是把虚拟地址映射到物理地址。

我们已经知道,大多数使用虚拟存储器的系统都使用一种称为分页(paging)的技术,就象我们刚才所举的例子,虚拟地址空间被分成大小相同的一组页,每个页有一个用来标示它的页号(这个页号一般是它在该组中的索引,这点和C/C++中的数组相似)。在上面的例子中0 ~ 4K 的页号为 0,4 ~ 8K的页号为 1,8 ~ 12K 的页号为 2,以此类推。

而虚拟地址(注意:是一个确定的地址,不是一个空间)被 MMU 分为 2 个部分,第一部分是页号索引(page Index),第二部分则是相对该页首地址的偏移量(offset)。

我们还是以刚才那个 16 位机器结合下图进行一个实例说明。该实例中,虚拟地址 8196被送进 MMU, MMU 把它映射成物理地址。16 位的 CPU 总共能产生的地址范围是0 ~ 64K,按每页 4K 的大小计算,该空间必须被分成 16 个页。而我们的虚拟地址第一部分所能够表达的范围也必须等于 16(这样才能索引到该页组中的每一个页),也就是说这个部分至少需要 4 个 bit。一个页的大小是 4K(4096), 也就是说偏移部分必须使用 12 个 bit来表示( 2^12=4096,这样才能访问到一个页中的所有地址), 8196 的二进制码如下图所示:

在这里插入图片描述

该地址的页号索引为 0010(二进制码),既索引的页为页 2,第二部分为000000000100(二进制),偏移量为 4。页 2 中的页框号为 6(页 2 映射在页框 6,见上图),我们看到页框6的物理地址是 24 ~ 28K。于是 MMU 计算出虚拟地址 8196 应该被映射成物理地址 24580(页框首地址+偏移量=24576+4=24580)。

同样的,若我们对虚拟地址 1026 进行读取,1026 的二进制码为 0000010000000010,page index=”0000”=0, offset=010000000010=1026。页号为 0,该页映射的页框号为2,页框 2 的物理地址范围是 8192~12287,故 MMU 将虚拟地址 1026 映射为物理地址 9218(页框首地址+偏移量= 8192+1026 =9218)。以上就是MMU的工作过程。


三、S3C24XX 的 MMU 工作过程

下面我们针对 s3c2410 的 MMU(注1) 进行讲解。

S3c2410 总共有 4 种内存映射方式,分别是:

  1. Fault (无映射)
  2. Coarse Page (粗表)
  3. Section (段)
  4. Fine Page (细表)

我们以 Section (段)进行说明。

ARM920T 是一个 32bit 的 CPU,它的虚拟地址空间为 2^32=4G 。而在 Section 模式,这 4G 的虚拟空间被分成一个一个称为段(Section)的单位(与我们上面讲的页在本质上其实是一致的), 每个段的长度是 1M (而我们之前所使用的页的长度是 4K)。4G 的虚拟内存总共可以被分成 4096 个段(1M*4096=4G),因此我们必须用 4096 个描述符来对这组段进行描述,每个描述符占用 4 个Byte, 故这组描述符的大小为 16KB (4byte * 4096),这4096 个描述符构为一个表格,我们称其为 Tralaton Table.
在这里插入图片描述

上图是描述符的结构。

Section base address:段基地址(相当于页框号首地址);
AP:访问控制位 Access Permission;
Domain:访问控制寄存器的索引。Domain 与 AP 配合使用,对访问权限进行检查;
C:当 C 被置 1 时为 write-through (WT)模式;
B:当 B 被置 1 时为 write-back (WB)模式(C,B两个位在同一时刻只能有一个被置1)。

下面是 s3c2410 内存映射后的一个示意图:

在这里插入图片描述

我的 s3c2410 上配置的 SDRSAM 大小为 64M,该 SDRAM 的物理地址范围是 0x3000_ 0000 ~ 0x33FF_FFFF(属于 Bank 6),由于 1 个 Section 的大小是 1M,所以该物理空间可以被分成 64 个物理段(页框)。

在 Section 模式下,送进 MMU 的虚拟地址(注1)被分为两部分(这点和我们上面举的例子是一样的),这两部分为 Descriptor Index(相当于上面例子的 Page Index )和 Offset,Descript index 长度为 12 bit (2^12=4096,从这个关系式你能看出什么?:) ),Offset 长度为 20 bit(2^20= 1M,你又能看出什么?:))。

观察一下一个描述符(Descriptor)中的 Section Base Address 部分,它长度为 12 bit,里面的值是该虚拟段(页)映射成的物理段(页框)的物理地址前 12 bit,由于每一个物理段的长度都是 1 M,所以物理段首地址的后 20 bit 总是为 0x00000(每个 Section 都是以 1M 对齐),确定一个物理地址的方法是: 物理页框基地址+虚拟地址中的偏移部分= Section Base Address << 20 + Offset 。呵呵,可能你有点糊涂了,还是举一个实际例子说明吧。

假设现在执行指令 MOV REG, 0x30000012,虚拟地址的二进制码为 00110000 00000000 00000000 00010010,前 12 位是 Descriptor Index= 00110000 0000 = 768,故在Translation Table 里面找到第 768 号描述符,该描述的 Section Base Address = ”0x0300 ”,也就是说描述符所描述的虚拟段(页)所映射的物理段(页框)的首地址为0x3000 0000(物理段(页框)的基地址= Section Base Address 左移 20 bit = 0x0300 << 20 = 0x3000 0000),而 Offset = 000000 00000000 00010010 = 0x12,故虚拟地址 0x30000012 映射成的物理地址 = 0x3000 0000 + 0x12 = 0x3000 0012(物理页框基地址+虚拟地址中的偏移)。你可能会问怎么这个虚拟地址和映射后的物理地址一样?这是由我们定义的映射规则所决定的。在这个例子中我们定义的映射规则是把虚拟地址映射成和他相等的物理地址。我们这样书写映射关系的代码:

void mem_mapping_linear(void)
{
    unsigned long descriptor_index, section_base, sdram_base, sdram_size;
    sdram_base=0x30000000;
    sdram_size=0x04000000;
    for (section _base= sdram_base,descriptor_index = section _base>>20;
          section _base < sdram_base+ sdram_size;
          descriptor_index+=1;section _base +=0x100000)
    {
        *(mmu_tlb_base + (descriptor_index)) = (section _base>>20) |  
                         MMU_OTHER_SECDESC;
    }
}

上面的这段段代码,把虚拟空间 0x3000 0000 ~ 0x33FF FFFF 映射到物理空间 0x3000 0000 ~ 0x33FF FFFF,由于虚拟空间与物理空间空间相吻合,所以虚拟地址与他们各自对应的物理地址在值上是一致的。当初始完 Translation Table 之后,记得要把Translation Table 的首地址(第 0 号描述符的地址)加载进协处理器 CP15 的 Control Register 2 ( 2 号控制寄存器)中,该控制寄存器的名称叫做 Translation table base (TTB) register。

以上讨论的是 descriptor 中的 Section Base Address 以及虚拟地址和物理地址的映射关系,然而 MMU 还有一个重要的功能,那就是访问控制机制(Access Permission )。简单说访问控制机制就是,CPU 通过某种方法判断当前程序对内存的访问是否合法(是否有权限对该内存进行访问),如果当前的程序并没有权限对即将访问的内存区域进行操作,则 CPU 将引发一个异常,s3c2410 称该异常为 Permission fault,x86 架构则把这种异常称之为通用保护异常(General Protection),什么情况会引起 Permission fault呢 ?比如处于 User 级别的程序要对一个 System 级别的内存区域进行写操作,这种操作是越权的,应该引起一个 Permission fault,搞过 x86 架构的朋友应该听过保护模式(Protection Mode),保护模式就是基于这种思想进行工作的,于是我们也可以这么说:s3c2410 的访问控制机制其实就是一种保护机制。那 s3c2410 的访问控制机制到底是由什么元素去参与完成的呢?它们间是怎么协调工作的呢?这些元素总共有:

  1. 协处理器 CP15 中 Control Register3:DOMAIN ACCESS CONTROL REGISTER;
  2. 段描述符中的 AP 位和 Domain 位;
  3. 协处理器 CP15 中 Control Register 1(控制寄存器 1)中的 S bit 和 R bit;
  4. 协处理器 CP15 中 Control Register 5(控制寄存器 5);
  5. 协处理器 CP15 中 Control Register 6(控制寄存器 6);

DOMAIN ACCESS CONTROL REGISTER 是访问控制寄存器,该寄存器有效位为 32,被分成 16 个区域,每个区域由两个位组成,他们说明了当前内存的访问权限检查的级别,如下图所示:

在这里插入图片描述
每区域可以填写的值有 4 个,分别为 00,01,10,11(二进制),他们的意义如下所示:
在这里插入图片描述
00:当前级别下,该内存区域不允许被访问,任何的访问都会引起一个 domain fault;
01:当前级别下,该内存区域的访问必须配合该内存区域的段描述符中 AP 位进行权检查;
10:保留状态(我们最好不要填写该值,以免引起不能确定的问题);
11:当前级别下,对该内存区域的访问都不进行权限检查。

我们再来看看 discriptor 中的 Domain 区域,该区域总共有 4 个bit,里面的值是对DOMAIN ACCESS CONTROL REGISTER 中 16 个区域的索引。而 AP 位配合 S bit 和 A bit 对当前描述符描述的内存区域被访问权限的说明,他们的配合关系如下图所示:

在这里插入图片描述
AP 位也是有四个值,结合实例对其进行说明。
在下面的例子中,我们的 DOMAIN ACCESS CONTROL REGISTER 都被初始化成0xFFFF BDCF,如下图所示:
在这里插入图片描述
例 1:
Discriptor 中的 domain=4,AP=10(这种情况下 S bit ,A bit 被忽略)。

假设现在我要对该描述符描述的内存区域进行访问:
由于domain=4,而DOMAIN ACCESS CONTROL REGISTER中field 4的值是01,系统会对该访问进行访问权限的检查。
假设当前CPU处于Supervisor模式下,则程序可以对该描述符描述的内存区域进行读写操作。
假设当前CPU处于User模式下,则程序可以对该描述符描述的内存进行读访问,若对其进行写操作则引起一个permission fault.


例 2:
Discriptor 中的domain=0,AP=10(这种情况下S bit ,A bit 被忽略)
domain=0,而DOMAIN ACCESS CONTROL REGISTER中field 0的值是11,系统对任何内存区域的访问都不进行访问权限的检查。
由于统对任何内存区域的访问都不进行访问权限的检查,所以无论CPU处于合种模式下(Supervisor模式或是User模式),程序对该描述符描述的内存都可以顺利地进行读写操作


例 3:
Discriptor 中的domain=4,AP=11(这种情况下S bit ,A bit 被忽略)
由于domain=4,而DOMAIN ACCESS CONTROL REGISTER中field 4的值是01,系统会对该访问进行访问权限的检查。
由于AP=11,所以无论CPU处于合种模式下(Supervisor模式或是User模式),程序对该描述符描述的内存都可以顺利地进行读写操作


例 4:
Discriptor 中的domain=4,AP=00, S bit=”0”,A bit=”0”
由于domain=4,而DOMAIN ACCESS CONTROL REGISTER中field 4的值是01,系统会对该访问进行访问权限的检查。
由于AP=00,S bit=”0”,A bit=”0”,所以无论CPU处于合种模式下(Supervisor模式或是User模式),程序对该描述符描述的内存都只能进行读操作,否则引起permission fault.
通过以上4个例子我们得出两个结论:
1.对某个内存区域的访问是否需要进行权限检查是由该内存区域的描述符中的Domain域决定的。
2.某个内存区域的访问权限是由该内存区域的描述符中的AP位和协处理器CP15中Control Register1(控制寄存器1)中的S bit和R bit所决定的。


原文链接

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ARM MMU 详解 的相关文章

随机推荐

  • 憨批的语义分割5——DeeplabV3+模型解析以及训练自己的DeeplabV3+模型(划分斑马线)

    憨批的语义分割5 DeeplabV3 模型解析以及训练自己的DeeplabV3 模型 划分斑马线 学习前言 模型部分 什么是DeeplabV3 模型 DeeplabV3 模型的代码实现 1 主干模型Xception 2 DeeplabV3
  • C++学习(二八七)获取Android手机各种路径

    data app 存放用户安装的APK的目录 安装时 把APK拷贝于此 data data 应用安装完成后 在 data data目录下自动生成和APK包名 packagename 一样的文件夹 用于存放应用程序的数据 data dalvi
  • yolov5训练自己的数据(详细)

    已经说过了yolov3的编译 训练的流程了 那今天就来说说yolov5的训练过程了 1 环境准备 centos 7 9 2009 python 3 7 0 cuda 10 2 89 cudnn 7 6 5 torch 1 6 0 torch
  • 修改缓存服务器文件,演练:更改服务器上的工作簿中的缓存数据

    演练 更改服务器上的工作簿中的缓存数据 02 24 2013 本文内容 此演练演示如何通过使用 ServerDocument 类在不启动 Excel 的情况下修改 Microsoft Office Excel 工作簿中缓存的数据集 适用于
  • Tcp/Ip四层模型---应用层、传输层、网络层和链路层以及他们的各自的主要工作

    Tcp Ip四层模型 TCP IP 协议栈是一系列网络协议的总和 是构成网络通信的核心骨架 它定义了电子设备如何连入因特网 以及数据如何在它们之间进行传输 TCP IP 协议采用4层结构 分别是应用层 传输层 网络层和链路层 每一层都呼叫它
  • The value of the local variable total is not used报错解决

    变量未输出 加上System out print 变量
  • 比找女朋友还难的技术点,Python 面向对象

    有人整个 Python 学习生涯都没有搞明白的技术之一 面向对象 先放美图调整下心情 欢迎关注 点赞 评论 Python 面向对象的编程 Python 准确地说也是一门面向对象编程的语言 简称 OOP 咱已经知道在 Python 中所有的数
  • Node.js简介,记录Node.js入门基础概念

    有必要整理一下Node js基础知识啦 Node js是什么 Node js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境 Node js 使用了一个事件驱动 非阻塞式 I O 的模型 使其轻量又高效 Node j
  • 算法的三种练习

    除了具体写代码 做这些练习 1 循环不变式 用循环不变式去解释算法 2 递归 动归的 递推式 3 搜索问题的隐式图构造 隐式树还是图 一个前驱 多个前驱 点是什么 边是什么 怎么扩展
  • 【云原生进阶之PaaS中间件】第二章Zookeeper-3.2架构详解

    1 Zookeeper工作原理 1 1 Zookeeper的角色 领导者 leader 负责进行投票的发起和决议 更新系统状态 学习者 learner 包括跟随者 follower 和观察者 observer follower用于接受客户端
  • Vue3 + Element Plus 实现动态标签页及右键菜单

    文章目录 1 前言 1 1 目的 1 2 普通右键菜单 1 3 本文右键菜单方式 2 生成动态标签页 2 1 准备变量容器 2 2 构造标签页 2 3 动态添加标签页 2 4 动态移除标签页 3 生成右键菜单 3 1 扩展标签页 3 2 增
  • subprocess执行命令行获取返回

    subprocess subprocess 模块允许我们启动一个新进程 并连接到它们的输入 输出 错误管道 从而获取返回值 Popen 是 subprocess的核心 子进程的创建和管理都靠它处理 import subprocess p s
  • 编写教师和学生信息的程序

    编写教师和学生信息的程序 1 定义一个抽象类Person 在Person类中声明两个属性 name和age 并设置其对应的getter方法 用于获取人的姓名和年龄 在Person类中声明一个有参构造方法 用于对Person类中的属性进行初始
  • 两个多项式求和

    单链表的应用 两个多项式求和 提到多项式想必定会想到其系数和指数 定义数据结构 typedef struct Polynomial float coe 多项式系数 int exp 多项式指数 struct Polynomial next P
  • 【模式识别&目标检测】——基于机器视觉的无人机避障&RP-YOLOv3实例

    目录 引入 一 YOLOv3模型 1 实时目标检测YOLOv3简介 2 改进的实时目标检测模型 二 数据集建立 结果分析 1 数据集建立 2 模型结果分析 三 无人机避障实现 参考文献 引入 目前对于障碍物的检测整体分为 激光 红外线 超声
  • ubuntu18.04多版本cuda安装与转换(实测有效)

    ubuntu18 04 CUDA详解 Ubuntu18 04安装cuda 10 1及10 0 和cudnn 1 安装显卡驱动 1 1 禁用nouveau驱动 1 2 安装NVIDIA显卡驱动 2 安装CUDA10 1 2 1 下载cuda安
  • 公司前端开发架构改造

    要看更多的文章 欢迎访问我的个人博客 http oldli net 现在的前端早已不是几年前的前端 再也不是jQuery加一个插件就能解决问题的时代 最近对公司前端的开发进行了一系列的改造 初步达到了我想要的效果 但是未来还需要更多的改进
  • 【DOCKER】docker run的-d,-v等参数用处

    0 引用 ref1 docker ps的详解 表格和文本的记录版本 1 手册查询内容 root master cpu docker run help Usage docker run OPTIONS IMAGE COMMAND ARG Ru
  • linux脚本的注释符号是什么,linux的shell编程中的符号`是什么

    bin sh 是指此脚本使用 bin sh来解释执行 是特殊的表示符 其后面根的是此解释此脚本的shell的路径 bash 表示系统提示符 表示此用户为普通用户 超级用户的提示符是 bash是shell的一种 是linux下最常用的一种sh
  • ARM MMU 详解

    一 MMU 的产生 许多年以前 当人们还在使用DOS或是更古老的操作系统的时候 计算机的内存还非常小 一般都是以 K 为单位进行计算 相应的 当时的程序规模也不大 所以内存容量虽然小 但还是可以容纳当时的程序 但随着图形界面的兴起 还有用户