Linux内核--内存管理

2023-11-17

内存管理单元MMU(memory management unit)的主要功能是虚拟地址(virtual memory addresses)到物理地址(physical addresses)的转换。除此之外,它还可以实现内存保护(memory protection)、缓存控制(cache control)、总线仲裁(bus arbitration)以及存储体切换(bank switching)等功能。本文将详细介绍MMU产生的历史原因以及其作用和工作原理。

MMU的产生背景

在计算机出现的早期,其内存资源十分有限,一般只有几十几百KB,当时的程序规模也小,对于当时的程序而言,KB级的内存资源尚足够使用。但随着计算机技术的发展,应用程序的规模不断膨胀,一个难题终于出现在程序员的面前,即应用程序太大,内存已经容纳不下那么大的程序。

最初解决该问题的办法是把程序分割成许多称为覆盖块(overlay)的片段。覆盖块0首先运行,结束时调用另一个覆盖块继续运行。

虽然覆盖块的交换是由OS完成的,但是必须先由程序员先进行分割,这是一个费时费力的工作,而且相当枯燥。

人们必须找到更好的办法从根本上解决这个问题。

不久人们找到了一个办法,这就是虚拟存储器(virtual memory)。

虚拟内存器(Virtual Memory)

虚拟内存器的基本思想是:

程序、数据、堆栈的总大小可以超过物理存储器的大小,操作系统把当前使用的部分保留在内存中,而把其他未被使用的部分保存在磁盘上。

比如,对一个16MB 的程序和一个内存只有4MB的机器,OS通过选择,可以决定各个时刻将哪4MB的内容保留在内存中,并在需要时在内存和磁盘间交换程序片段,这样就可以把这个16MB的程序运行在一个具有4MB内存机器上了。而这个16M的程序在运行前不必由程序员进行分割。

任何时候,计算机上都存在一个程序能够产生的地址集合,我们称之为地址范围。

这个范围的大小由CPU 的位数决定,例如:

一个32位的CPU ,它的地址范围是0x0 ~ 0xFFFF FFFF (4G)

而对于一个64位的CPU ,它的地址范围为0x0 ~ 0xFFFF FFFF FFFF FFFF (64T)

这个范围就是我们程序能够产生的地址范围,我们把这个地址范围称为虚拟地址空间,该空间中的某一个地址我们称之为虚拟地址。

与虚拟地址空间和虚拟地址相对应的则是物理地址空间和物理地址,大多数时候,我们的系统所具备的物理地址空间只是虚拟地址空间的一个子集,举一个最简单的例子直观的说明这两者的区别,对于一台内存为256MB的32Bit x86主机来说,它的虚拟地址空间范围是0x0 ~ 0xFFFF FFFF (4G),而物理地址空间范围是0x0000 0000 ~ 0x0FFF FFFF ( 256MB )

在没有使用虚拟地址的机器上,虚拟地址被直接送到内存总线上,使具有相同地址的物理存储被读写。而使用了虚拟存储的情况下,虚拟地址不是被直接送到内存地址总线上,而是送到内存管理单元— MMU

MMU由一个或一组芯片组成,一般存在于协处理器中,其功能是把虚拟地址映射为物理地址。

  1. CPU 看到的是 Virtual Adress (程序中的逻辑地址)
  2. Caches 和 MMU 使用的是 MVA (实际的虚拟地址 MVA = (pid << 25) | VA)
  3. 实际物理设备使用的是 Physical Address (物理地址)

MMU物理组成

内存管理单元MMU是处理器中的硬件单元,通常每个核都会有一个MMU。MMU包含TLB(Translation Lookaside Buffer)和Table Walk Unit。TLB通俗来说就是页表的缓存,用于缓存虚拟地址到物理地址的转换表。Table Walk Unit的作用是把内存页表走一走进行查表,完成虚拟地址到物理地址的转换。

TLB用于缓存最近执行的转换页表。TLB可以存储所有的页表大小,并且在需要的时候可以将页表拆分成更小的单元。

在MMU中两级TLB分为:Instruction L1 TLB,Data L1 TLB和L2 TLB。L2 TLB是指令数据共享的。

当程序发送的虚拟地址VA在TLB中找不到对应的转换页表的时候,就需要Table Walk Unit去系统的Cache或者内存取新的页表。用取回来的新页表更新TLB中的旧页表。

MMU工作机制

 大多数使用虚拟存储器的系统都使用一种称为分页(paging)。

虚拟地址空间划分为页(page)的单位,而相应的物理地址空间也被进行划分,单位是页框(frame)。

页和页框的大小必须相同。

接下来配合图片,以一个例子说明页与页框之间在MMU 的调度下是如何进行映射的:

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

这台机器必须有一个达到可以存放64K程序的外部存储器(例如磁盘或Flash)以保证程序片段在需要时可以被调用。

这个例子中,页的大小为64K,页框大小与页相同(这点必须保证的,内存和外围存储器之间传输总是以页为单位),对应64K的虚拟地址和32K的物理存储器,它们分别包含了16个页和8个页框。

执行下面这些指令:

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

虚拟地址 0 将被送往MMU,MMU看到该虚拟地址落在页 0 范围内(页0范围是0到4095),从上图我们可以看出页0所对应的(映射)的页框为2(页框2的地址范围是8192 到 12287)。

因此,MMU将该虚拟地址转化为物理地址8192, 并把地址8192送到地址总线上。

内存对MMU的映射一无所知,它只看到一个对地址8192的读请求并执行它,MMU从而将8192到 12287换虚拟地址解析为对应的物理地址0到4096 。

MOVE REG , 20500
被转换为---->  MOVE REG, 12308

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

MOV REG , 32780

虚拟地址32780 落在页 8 的范围内,从上图我们看出,页8并没有被有效的进行映射(该页被打上X),这时又会发生什么呢?

MMU注意到这个页没有被映射,于是通知CPU发生了一个缺页故障(page fault),这种情况下,操作系统必须处理这个页故障,它必须从8个物理页框中找到一个很少被使用的页框,并把该页框的内容写入外围存储器(这个动作被称为page copy),随后把需要引用的页(本例是页8)映射到刚才被释放的页框中(这个动作被称为修改映射关系),然后重新执行产生故障的指令(MOV REG, 32780)。

假定操作系统,决定释放页框1,以使以后任何对虚拟地址4K到8K的访问都引起故障而使操作系统做出适当的动作。

其次它把虚页8对应的页框号由X变为1,因此得新执行MOV REG, 32780,MMU将32780 映射为 4180。

我们已经知道,大多数使用虚拟存储器的系统都使用一种称为分页(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。

该地址的页号索引为0010(二进制码),即索引的页为页2,第二部分为000000000100(二进制),偏移量为4。

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

同样的,若我们对虚拟地址1026进行读取,1026的二进制码为0000010000000010page index="0000"=0,offset=010000000010=1026

页号为0,该页映射的页框号为2,页框2的物理地址范围是8192~12287,故MMU将虚拟地址1026映射为物理地址9218(页框首地址+偏移量=8192+1026=9218)。

以上就是MMU的工作过程。一句话总结为:CPU在任何时候,都将发出虚拟地址,这个虚拟地址发给MMU后,MMU通过页表查询该虚拟地址对应的物理地址,并将该物理地址送到内存总线。这个过程可称为虚拟地址到物理地址的映射。

虚拟内存管理

现代操作系统普遍采用虚拟内存管理(Virtual Memory Management)机制,这需要处理器中的MMU(Memory Mangement Unit,内存管理单元)提供支持。

首先引入两个概念,虚拟地址和物理地址。

  • 如果处理器没有MMU,或者有MMU但没有启用,CPU执行单元发出的内存地址将直接传到芯片引脚上,被物理内存芯片接收,这称为物理地址。
  • 如果处理器启用了MMU,CPU执行单元发出的内存地址将被 MMU 截获,从CPU到MMU的地址称为虚拟地址,而MMU 将这个地址翻译成另一个地址,发到CPU芯片的外部地址引脚上,也就是将VA映射成了PA 了。

如果是32位处理器, 则内存地址总线是32位的,与CPU 执行单元相连,而经过MMU转换后的外地址总线则不一定是32位。

也就是说,虚拟地址空间与物理地址空间是独立的,32位处理器的虚拟地址空间是4GB,而物理地址空间既可以大于也可以小于4G。

MMU将VA映射到PA是以页(page)为单位的,32位处理器的页尺寸通常是4KB。

例如:

MMU可以通过一个映射项将VA的一页0xB7001000 - 0xB7001FFFF映射到PA的一页0x2000 ~ 0x2FFF

如果CPU执行单元要访问虚拟地址0xB7001008,则实际访问到的物理地址是0x2008。

物理内存中的页称为物理页帧(page frame),虚拟内存的哪个页面映射到物理内存的哪个页帧是通过页表(Page Table)来描述的,页表保存在物理内存中,MMU会查找页表来确定一个VA应该映射到什么PA。

操作系统和 MMU 是这样配合的:操作系统在初始化或分配、释放内存时会执行一些指令在物理内存中填写页表,然后用指令设置MMU,告诉MMU页表在物理内存中的什么位置。

设置好之后,CPU 每次执行访问内存的指令都会自动引发MMU做查表和地址转换操作,地址转换操作由硬件自动完成,不需要用指令控制MMU去做。

我们在程序中使用的变量和函数都有各自的地址,程序被编译后,这些地址就成了指令中的地址,指令中的地址被CPU解释执行,就成了CPU的执行单元发出的内存地址,所以在启用MMU的情况下,程序中使用的地址都是虚拟地址,都会引发MMU做查表和地址转主换操作。

那为什么要设计这么复杂的内存管理机制呢? 多了一层 VA 到 PA 的转换到底换来什么好处?

MMU除了做地址转换之外,还提供内存保护机制,各种体系结构都有用户模式(User Mode)和特权模式(Privileged Mode)之分,操作系统可以在页表中设置每个内存页面的访问权限,有些页面不允许访问,有些页面只有在CPU 处于特权模式时才允许访问,有些页面在用户模工和特权模式都可以访问,访问权限又分为可读、可写 和可执行三种。

这样设定好之后,当CPU要访问一个 VA 时,MMU都会检查CPU当前处于用户模式还是特权模式,访问内存的目的是读数据、写数据、还是取指令,如果和操作系统设定的页面权限相符,就允许访问,把它转换成PA ;如果不允许访问,就产生一个异常(Exception)。

异常处理过程和中断类似,不同的是中断由外部设备产生而异常由CPU内部产生,中断产生的原因和CPU当前执行的指令无关,而异常的产生就是由于CPU当前执行的指令出了问题,例如,访问内存的指令被 MMU 检查出权限错误,除法指令的除数为0都会产生异常。

用户空间和内核空间

通常操作系统把虚拟地址划分为用户空间和内核空间,例如X86平台的Linux 系统虚拟地址空间是0x00000000 - 0xFFFFFFFF,前3GB(0x00000000 - 0xBFFFFFFF)是用户空间,后1GB(0xC0000000 - 0xFFFFFFFF)是内核空间。

用户程序加载到用户空间,在用户模式下执行,不能访问内核中的数据,也不能跳转到内核代码中执行。

这样可以保护内核,如果一个进程访问了非法地址,顶多这一个进程崩溃,而不会影响到内核和整个系统的稳定性。

CPU在产生中断和异常时不仅会跳转到中断或异常服务程序,还会自动切换模式,从用户模式切换到特权模式,因此从中断或异常服务程序可以跳转到内核代码中执行。

事实上,整 个内核就是由各种中断和异常处理程序组成的。

总结下:

在正常情况下 ,处理器在用户模式执行用户程序,在中断或异常情况下处理器切换到特权模式执行内核程序,处理完中断或异常之后再返回用户模式继续执行用户程序。

段错误

段错误是这样产生的:用户程序要访问一个VA, 经MMU检查无权访问,MMU产生一个异常,CPU从用户模式切换到特权模式,跳转到内核代码中执行异常服务程序。内核把这个异常解释为段错误,把相发异常的进程终止掉。

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

Linux内核--内存管理 的相关文章

  • BIOS和BootLoader uboot

    BIOS BIOS是英文 Basic Input Output System 的缩略语 直译过来后中文名称就是 基本输入输出系统 其实 它是一组固化到计算机内主板上一个ROM芯片上的程序 它保存着计算机最重要的基本输入输出的程序 系统设置信
  • Linux内核同步之RCU机制基础

    Why RCU 1 中断与抢占 当一个进程被时钟中断打断后 kernel运行tick中断处理程序 一般是top half 中断处理程序运行结束后 有两种情况 之前的进程获得CPU继续运行 另一个进程获得了CPU开始运行 而之前的进程则被抢占
  • 一文读懂 PyTorch 显存管理机制

    点击上方 视学算法 选择加 星标 或 置顶 重磅干货 第一时间送达 作者丨米阿罗 知乎 已授权 来源丨https zhuanlan zhihu com p 486360176 编辑丨极市平台 首发于踢翻炼丹炉 https www zhihu
  • Linux内存管理(7) - page fault

    了解linux page fault 1 概述 A page fault sometimes called PF PF or hard fault a is a type of exception raised by computer ha
  • 嵌入式100题(86):简述处理器在读内存的过程中,CPU核、cache、MMU如何协同工作?画出CPU核、cache、MMU、内存之间的关系示意图加以说明...

    简述处理器在读内存的过程中 CPU核 cache MMU如何协同工作 画出CPU核 cache MMU 内存之间的关系示意图加以说明 现代操作系统普遍采用虚拟内存管理机制 这需要MMU Memory Management Unit 内存管理
  • C++从入门到放弃之:C++ 左值引用与右值引用详解

    C 从入门到放弃 C 引用 1 左值引用 2 万能引用 常引用 3 右值引用 4 引用型函数返回值 5 引用和指针 6 函数传参传递指针和引用的区别 总结 C 引用 1 左值引用 定义 引用即别名 某个变量的别名 对引用的操作就等同于对变量
  • java实现:《操作系统实验三》模拟内存管理

    固定分区分配 固定分区分配是最简单的一种多道程序存储管理方式 它将用户内存空间划分为若干个固定大小的区域 每个分区只装入一道作业 当有空闲分区时 便可以再从外存的后背作业队列中 选择适当大小的作业装入该分区 如此循环 优缺点 分区大小相等
  • UBOOT命令总结(转)

    UBOOT命令总结 转 很好的UBOOT命令总结 我在起步时就是看的这篇东西 熟悉了以后就用 看自带帮助就行 Printenv 打印环境变量 Uboot gt printenvbaudrate 115200ipaddr 192 168 1
  • 大端小端以及内存对齐的优势

    为什么区分大小端 编程语言中不同类型的变量所占的字节大小不一样 大端 低位数据存放在高位地址为大端编码 小端 低位数据存放在低位地址为小端编码 如图所示 对于选择大端小端对程序性能的思考 首先 数据离散程度不同 字节序会在很大程度上影响速度
  • C++栈区、堆区、全局静态区、代码区的介绍

    示例代码 include
  • 手把手带你实现Linux内核编译步骤及配置

    linux 系统体系结构 linux kernel体系结构 arm有7种工作模式 x86也实现了4个不同级别RING0 RING3 RING0级别最高 这样linux用户代码运行在RING3下 内核运行在RING0 这样系统本身就得到了 充
  • ubuntu从内核源代码编译内核及替换内核

    1 下载ubuntu对应的linux内核源代码 apt catch search linux source 查看当前linux内核版本 apt get install linux source lt 对应的内核版本好 gt 下载对应的lin
  • 进程间通信之共享内存分析

    零拷贝技术 https strikefreedom top linux io and zero copy 一 内存映射和共享内存的区别 1 1 内存映射之mmap函数 将一个文件或者其它对象映射到进程的地址空间 实现文件磁盘地址和进程虚拟地
  • 2017校招 360 笔试题 编程题 内存管理

    内存管理 时间限制 C C 语言 1000MS 其他语言 3000MS 内存限制 C C 语言 65536KB 其他语言 589824KB 题目描述 物联网技术的蓬勃发展 各种传感器纷纷出现 小B所在的项目组正在开发一个物联网项目 她们在研
  • Linux内核--内存管理

    内存管理单元MMU memory management unit 的主要功能是虚拟地址 virtual memory addresses 到物理地址 physical addresses 的转换 除此之外 它还可以实现内存保护 memory
  • 内存管理方案

    内存管理方案 Memory Management System Author Owen 目 录 内存管理方案 1 目 录 1 1 概述 2 2 理论依据 2 2 1 不对内存进行管理 2 2 2 对内存进行内部管理 2 3 实现方案 2 3
  • Go内存管理及性能观测工具

    内存管理 TCMalloc Golang内存分配算法主要源自Google的TCMalloc算法 TCMalloc将内存分成三层最外层Thread Cache 中间层Central Cache 最里层Page Heap Thread Cach
  • Linux进程管理:deadline调度器

    一 概述 实时系统是这样的一种计算系统 当事件发生后 它必须在确定的时间范围内做出响应 在实时系统中 产生正确的结果不仅依赖于系统正确的逻辑动作 而且依赖于逻辑动作的时序 换句话说 当系统收到某个请求 会做出相应的动作以响应该请求 想要保证
  • Linux内核源码学习(1)

    一 内核简介 1 在安装好的Linux系统中 内核的源代码位于 usr src linux 2的10次方就是1K 1024 16位CPU的地址空间是64K X86结构的80386是32位CPU 段描述结构伪代码 typedef struct
  • linux内核学习(7)粗略走走kbuild Makefile编译流程

    今天看Makefile文件 我头大了 此Makefile非彼Makefile 里面多了很多内置命令 比如origin patsubst等等啦 这些都没听说过 更可恶的是 连网上都没有 可见 这是一件多么伤人的事情 分析这样的 真是让人折寿啊

随机推荐

  • CMU15-213 课程笔记 01-课程概览

    知识点 这门课的目的 深入理解当你执行代码时 计算机在做什么 LLDB 基于 LLVM 的命令行调试器 类似 GBD 内存引用 Bug typedef struct int a 2 double d struct t double fun
  • 如何确定一次完整的请求过程——服务链路跟踪

    微服务体系下 一个请求会调用多个服务 整个请求就会形成一个调用链 普通的日志输出是无法将整个体系串联起来 调用过程中某一个节点出现异常 定位排查难度系数增高 这种情况下就需要一个组件 来分析系统性能 展现调用链路 以便出现故障时快速定位并解
  • 【笔记整理】通信原理第四章复习——数字基带传输

    4 1 引言 数字基带信号 数字信号 补充 基带信号 指未经调制的信号 特征是其频谱从零频率或很低频率开始 占据较宽的频带 基带在传输前 必须经过一些处理或某些变换 比如码型变换 波形变换和频谱变换 才能送入信道中传输 处理或变换是为了使信
  • Django-rest-framework框架

    目录 一 Web应用模式 1 1 前后端不分离 二 API接口 三 接口测试工具 Postman 四 RESTful API规范 4 1 数据的安全保障 4 2 接口特征表现 4 3 多数据版本共存 4 4 数据即是资源 均使用名词 可复数
  • VHDL语言实现8位LED流水灯

    VHDL语言实现8位LED流水灯 包含对50MHz时钟信号分频产生1Hz信号 library ieee use ieee std logic 1164 all use ieee std logic unsigned all entity l
  • 1、mos管的工作原理

    文章目录 一 导体 绝缘体 半导体 二 半导体的制作 掺杂 pn结 半导体的单向导电性 三 mos管的工作原理 源极 栅极和漏极 nmos和pmos 电路符号 四 总结 一 导体 绝缘体 半导体 导体 能够导电的介质 绝缘体 不能导电的介质
  • 程序猿眼中的协议:TCP / IP 五层网络模型

    哈喽 大家好 我是你们的老朋友 保护小周 本期为大家带来的是 网络基础原理中的 TCP IP 五层网络模型 主要从协议的概念 网络模型 数据分层传输的流程 几个方面讲解 看完之后可以轻松的理解数据是如何在网络中传输的 确定不来看看嘛 更多精
  • List循环删除集合

    目录 For循环遍历List 增强For循环遍历List 迭代器iterator的remove方法 创建新的对象添加值 For循环遍历List 删除后list大小发生变化 因此索引发生变化 所以删除的元素不是你想要的 解决办法 倒着遍历li
  • qt学习笔记(五) QGraphicsPixmapItem与QGraphicsScene的编程实例 图标拖动渐变效果

    应大家的要求 还是把完整的工程文件贴出来 大家省点事 http www kuaipan cn file id 48923272389086450 htm 先看看运行效果 我用的群创7寸屏 主机是mini2440 分辨率是800 480 程序
  • 转】M1卡密钥破解,收藏

    M1卡说明及使用proxmark3破解方法 看了网上写的一些关于M1卡的文章 多数有些误导之嫌 首先谈谈M1卡的规格 M1卡的容量为1KB 好多网上写8KB 这里其实是有个误区 应该是8K位 1Byte 1B 8位 其实也就是说8k位想到于
  • Oracle数据库启动过程

    一 Oracle数据库的四种状态 Oracle数据库有四种状态 SHUTDOWN NOMOUNT MOUNT OPEN 二 Oracle数据库的启动过程详解 Oracle数据库启动主要包括三个过程 1 shutdown状态 数据库没有启动
  • Linux·设备文件devfs

    目录 设备文件系统 devfs udev mdev 关于file和inode数据结构在内核中的探究 设备文件系统 Linux引入了虚拟文件系统 从而使设备的访问可以像访问普通文件系统一样 因此在内核中描述打开文件的数据inode中的rdev
  • Python实现自动化办公

    人工智能 AI 是当今世界最为热门的话题之一 而Python是从事AI领域最为流行的编程语言 在本文中 我将介绍如何使用Python实现自动化办公和处理工资表等知识教程 帮助您从零入门人工智能Python 一 制作Excel可视化报表 在传
  • Qt槽和信号参数

    Qt的信号只需要申明 不需要定义 signals void signalA int a Qt的槽和普通的成员函数是一样的 可以加上public potected 以及virtual等标记 public void slotA int a 把这
  • JAVA远程调试功能

    JAVA 支持调试功能 本身提供了一个简单的调试工具JDB 支持设置断点及线程级的调试同时 不同的JVM通过接口的协议联系 本地的Java文件在远程JVM建立联系和通信 一 基本使用 服务端配置 1 服务器端开启调试模式 启动时增加JVM启
  • unity利用Image画直线,以及折线图

    using System Collections using System Collections Generic using UnityEngine using UnityEngine UI
  • QT qrand()随机函数

    在Qt中 生成随机数的函数为 qrand 该函数是标准C 函数 rand 的线程安全版本 如果我们仅仅只是调用该函数来生成随机数 那么每次得到的随机数都将是相同的 这是因为Qt生成的随机数严格来说是一个 伪随机 它的产生是根据随机数种子计算
  • 山东大学 研一 高级算法设计与分析期末考试回忆版

    新鲜出炉 1 给了一个无序的数组 要求给定时间复杂度为n的平方和nlogn的2中排序算法进行排序 并证明其时间复杂度 2 证明WPAR问题是npc问题 即给定一个集合 我们能够找到它的一个子集 剩余部分是这个子集的C倍 C是整数 1 利用划
  • TensorFlow在MNIST中的应用 识别手写数字(OpenCV+TensorFlow+CNN)

    参考 1 TensorFlow技术解析与实战 2 http blog csdn net sparta 117 article details 66965760 3 http blog csdn net HelloZEX article de
  • Linux内核--内存管理

    内存管理单元MMU memory management unit 的主要功能是虚拟地址 virtual memory addresses 到物理地址 physical addresses 的转换 除此之外 它还可以实现内存保护 memory