linux下的内存管理

来源:互联网 发布:昌吉电信怎么用4g网络 编辑:程序博客网 时间:2024/06/06 07:27

概述

linux内存管理基础知识,反复看深入理解linux内核

内存分区


 •位于最低端的 ZONE_DMA 内存区域是为 ISA 设备保留的,这些设备仅有 16MB
的 DMA 寻址能力。
• ZONE_NORMAL 中的页面具有 1:1 线性映射的物理地址和逻辑地址。运行在内
核态的代码可以方便和高效地访问,内核中大部分数据结构的内存对象都使用
这个区中的内存。
• ZONE_HIGHMEM 来自于 32 位内核的寻址能力限制。一个 32 位指针只能寻
址 4G 字节。Linux 默认按照 1:3 的比例对进程的地址空间进行划分,其中的
1GB 地址空间保留给内核使用,剩下的 3GB 是一个进程实际可用的虚拟地址
空间。由于 1GB 可用地址空间的限制,不是所有的物理内存都可以被内核
直接访问。称内核可以直接访问的为低端内存 (low memory),不能直接访问
的为高端内存 (high memory)。Linux 在 1GB 线性地址空间中划出 128MB,主
要用于映射访问高端内存。因而低端内存区 ( ZONE_NORMAL) 和高端内存区
( ZONE_HIGHMEM) 的分界线就在 1GB-128MB=896MB 处。数量众多的用户态
进程申请使用的内存以及内核维护的文件缓存页面都优先从 ZONE_HIGHMEM
中分配,其次才考虑 ZONE_NORMAL。
• ZONE_DMA32 是为 64 位系统中的 32 位设备准备的。在一个 64 位 CPU 上运
行的 64 位 Linux 内核或进程本身没有高端内存的限制,但是系统中仍然可能
存在可选的 32 位设备。这些设备只能在 4GB 的地址空间中进行 DMA 操作,
相应的内存就需要从 ZONE_DMA32 中分配。

内存分配

 
在每个 zone 中, Linux 都维护一个 free_area 数据结构,并使用 Buddy 算法来
管理和分配空闲的页面。在 Buddy 算法中,内存被划分为二的幂次方大小,并按此大
小自然对齐的块组。Linux 实现的内存分配单位是 2i, i = 0, 1, 2, . . . , MAX_ORDER-1个页面,其中 MAX_ORDER 的缺省值是 11。如图2.2所示, free_area 结构的基本组成包括 MAX_ORDER 个链表,其中第 i 个链表上挂载的是阶数为 i 的空闲页面组,每个页面组包含 2i个自然对齐的连续页面。Buddy 算法另外还维护一个位图数组,用以快速地检索空闲页面在地址空间中的分布状况。
当需要从空闲内存池中分配 2i个连续页面时,算法首先从 free_area 中阶数为 i 的链表上获取空闲页面组。如果没有满足要求的空闲页面组,则继续在第 i + 1个链表上搜索两倍于请求大小的内存块。这个过程将一直持续到找到足够大的空闲内存块为止。如果找到的页面块大小 2j大于请求的块大小 2i,则将其移出第 j 个链表,并分割为大小分别为 2i, 2i, 2i+1, 2i+2, . . . , 2j¡1的共计 j − i + 1 个内存块,其中第一个内存块的大小与请求大小相匹配,可供给申请者使用。其余的各块分别加入第 i, i + 1, . . . , j − 1 个链表,并相应地更新位图数组。将大的页面块打碎进行分配将增加系统中零碎的空闲内存块的数目。页面回收代码在适当时机下要将相邻的空闲页面结合起来形成单一大页面块。当页面块被释放时,Buddy 算法将检查是否有相同大小的相邻内存块存在。如果有,并且合并后的内存块仍然是自然对齐的,则将它们结合起来形成一个大小为原来两倍的新空闲块。每次结合完之后,还要检查是否可以继续合并成更大的页面组。

内存回收

Linux 的内存管理子系统被设计为最大限度地利用可用的物理内存来缓存数据。因而它仅在 free_area 中维持的少量的空闲页面。其它即将使用、正在使用和最近被使用过的页面都排列在链表 active_list 或 inactive_list 中。双链表结构的设计初衷是, active_list 用于容纳系统所有进程的工作集 (working set),或者被活跃引用的那些页面; inactive_list 则用于缓存和回收那些不活动的页面。active_list 和 inactive_list 中的页面采用最近少使用 (LRU, Least Recently Used) 的原则排队,其中最近较少使用的页面被逐步推向队列的尾部,并最终被内存扫描算法选择丢弃。这一过程被称为页面的老化 (aging) 和回收 (reclaim)。在三种情况下,需要对上述两个 LRU 队列进行扫描,回收其中的不活动页面,同时向 free_area 补充空闲页面:
• 如果在某次内存分配操作中,当搜索完 free_area 中的相关链表后,仍然未能找到大于或等于请求大小的内存块,则内存分配失败。此时需要在当前的进程上下文中立即进行同步的内存扫描,在扫描期间程序必须等待。
• 如果内核检测到空闲的页面数过少(由预设值 min_free_kbytes 决定),则唤醒 kswapd 进行异步的内存扫描和回收。
• 在需要大量释放内存的时候。比如笔记本电脑即将进入休眠 (hibernate),或者用户通过 /proc/sys/vm/drop_caches 接口要求清空缓存。
在对每一个 zone 的内存扫描中,扫描算法遵循如下规则:
• 在 2.6.24 及之前的版本中,保证对 active_list 和 inactive_list 的扫描速度是同步的:如果在某次内存扫描中,两个队列的长度分别为 La 和 Li ,检查的页面数分别为 Sa 和 Si ,则 La: Sa = Li: Si 。
• 在 2.6.25 及其后续版本中,对不同类型的缓存页面按不同的优先级进行扫描。
首先,优先扫描 inactive_list,仅当无法获得足够数量的空闲页面时进一步扫描 active_list;其次,优先回收普通页面,仅在必要时丢弃内存映射页面 (memory mapped pages)。这一新的策略以牺牲公平性为代价,以求减少无效扫描的数量以及内存回收操作的代价。这种内存回收效率的优化有利于避免配备大容量内存的服务器在回收内存操作上花费大量的 CPU 时间。
• inactive_list 中的 mmap 页面如已被引用一次或一次以上,则将其加入 active_list,否则取消映射并立即丢弃该页面;普通页面则必须被引用两次才能避免被丢弃,并且它们在被第二次引用时会被立即移入 active_list。无法被换出的匿名页面也会被加入 active_list。
• active_list 中曾被引用的 mmap 页面:正常情况下,放回 active_list的头部;未被引用的 mmap 页面以及所有的普通页面:放入 inactive_list。如果没有可用的交换空间,则匿名页面是不可回收的,因而它们会被放回 active_list,以求减少无效扫描的次数。
• 在检查每个页面的同时,清除其引用标志。

同步页面老化

 
页面回收算法必须保证对各个 zone 的扫描速度是同步的。即如果两个 zone 的大小分别为 L1 和 L2,某次扫描的页面数分别为 S1 和 S2,则应保证 L1: S1 = L2: S2。不过 ZONE_DMA 是个例外。它非常小,并且是稀缺资源,因而通常被保留为 DMA专用内存。
如果不能保证各个 zone 扫描的步调一致,就会造成不同 zone 中页面的老化速度不一致 (imbalance of aging)。这不但是一种不公平,而且会使内存的使用效率大打折扣。例如在图2.3中的系统中,包含有两个大小相同的 zone 和一个被顺序访问的文件。按照正常的内存回收和分配策略,新申请的页面会交替地从两个 zone 取得。因而此文件的页面缓存中的 0-7 页可能是从 zone 2 中取得的,8-13 页则来自于 zone 1,最后的页面 14-19 可能又属于 zone 2。如果 zone 1 的老化速度比 zone 2 快,则此文件的第 8-13 个页面会早早地从 zone 1 中被淘汰,同时其他页面仍会在 zone 2 中驻留较长的一段时间,在此期间,在此文件中就有了一个小小的缓存空洞。如果此时这个文件被再次顺序访问,则到了此处就会引发一个或两个小 I/O,以补上这个 6页面的缓存空洞。按照 I/O 时间的简单计算公式,前者的 I/O 时间大约是 8ms 的访问时间加上 6 × 4KB ÷ 80MB/s = 0.3ms 的传输时间共计 8.3ms,后者的 I/O 时间是8 + 20 × 4KB ÷ 80MB/s = 9ms。因而对于磁盘来说,一个 6 页面的小 I/O 的跟一个更大的包括 20 页面的 I/O 所需的时间仅有 8% 的差别。或者说,被提前丢弃的 6 个页面所可能造成的 I/O 代价与丢弃所有 20 个页面的 I/O 代价差别不大。这也意味着,一旦缓存中的 6 个页面被丢弃,剩余的 14 个页面就没有太大的缓存价值了。它们因为老化速度相对较慢的缘故而继续逗留在 zone 2 中,基本上是对内存的一种浪费
因而一个总是被顺序/完整访问的文件,它的缓存页面的老化和回收也应当尽可能保持一种顺序/完整性。此类文件的页面缓存中哪怕是缺失了一个页面,也会使剩下的临近缓存页面失去其大部分缓存价值。这种相关性使某些 I/O 负载对页面老化的不平衡特别敏感。比如在 mp3 下载、视频点播等服务中,I/O 负载的类型都是顺序服务一些较大尺寸的文件。在这种情况下,页面老化速度的不均衡会造成普遍性的文件缓存空洞,从而影响到全局的内存使用效率。假设 zone 1 的扫描速度是 zone 2的 n 倍,则 zone 2 尾部的(n-1)/n将伴有缓存空洞,相应的内存就基本上被浪费了。
在一些老版本的 Linux 中,就存在这种 zone 扫描速度的不平衡现象:在每次扫描中,Linux 遍历系统中所有的 zone,并按比例对它们进行扫描。但是如果顺利地在一个 zone 中回收了一定数量的页面,对这个 zone 的扫描就会提前终止。这就使按zone 大小分配的扫描页面数量之间的比例关系被破坏,从而会使小的 zone 倾向于被过度扫描。实测表明,这种 zone 之间扫描速度的不平衡可达到 2 : 1 ∼ 3 : 1,可令系统中多达 50% ∼ 66% 的内存缓存被低效率使用。

页面缓存(page cache)

Linux 内核的虚拟文件系统层 (VFS, Virtual File System) 为标准文件 I/O 提供统一的缓存机制,以提高常用文件的访问效率。缓存的内容包括文件的元数据(meta-data) 和数据 (data) 两大类。
各文件系统通用的元数据主要包括文件内容的相关属性和文件名。相应的 VFS维护了两个缓存池:
• icache: 缓存最近打开过的文件 i 节点 (inode)。对文件的 stat 和 open 操作都会打开它的 inode。inode 是 UNIX 文件系统中的一个核心数据结构,用于存储一个文件、目录或其它对象的基本属性。这些属性包括:大小、属主、权限、时戳、文件类型等等。inode 的这些属性描述的是文件的内容而非名字。
• dcache: 缓存最近查找过的文件名和目录项 (dentry)。为了加快文件的查找,每一个最近被访问过的目录和文件都在 dcache 中缓存一个它的 dentry 对象。一个打开的文件实例会指向一个唯一的 dentry 对象,后者又指向一个唯一的 inode对象。属于同一个文件系统名字空间 (namespace) 的所有 dentry 对象通过引用关系构成一棵树。
数据缓存的基本单位是页面 (page),因而一般称内核中的文件数据缓存为页面缓存 (page cache)。在 x86 体系结构中,页面大小一般是 4KB。本文将在一些举例中直接使用这一典型值。
页面缓存是文件数据在内存中的副本,因此页面缓存的管理与内存管理系统和文件系统都相关(周应超, 2006):一方面,页面缓存作为物理内存的一部分,需要参与物理内存的分配、老化和回收过程;另一方面,页面缓存中的数据来源于存储设备上的文件,需要通过文件系统与存储设备进行读写交互。从操作系统的角度考虑,页面缓存可以看做是内存管理系统与文件系统之间的联系纽带。因此,页面缓存管理是 Linux 内核的一个重要组成部分,它的性能直接影响着文件系统和内存管理系统的性能。
在 Linux 内核中,文件的每个数据块对应唯一的一个缓存页面。这些页面由内存管理子系统和虚拟文件系统分别通过两种方式组织起来,以满足不同的需要。
内存管理为页面缓存选择的数据结构是双向链表。Linux 内核为每一片物理内存区域 (zone) 维护 active_list 和 inactive_list 两个双向链表,这两个 LRU队列主要用来实现物理内存的老化和回收。这两个链表上除了文件缓存页面之外,主要还包括匿名页面 (anonymous page)。匿名页面中的数据不属于任何一个磁盘文件,但必要时可以临时保存到交换设备中去。用户态进程通过 malloc() 申请的内存、通过 MAP_PRIVATE,MAP_ANONYMOUS 映射的内存和共享内存、以交换设备作为后备存储的文件系统 (tmpfs) 等用的都是匿名页面。
虚拟文件系统用于索引页面缓存的数据结构是 radix tree (Rao et al., 2005)。 Linux
2.6 引入的 radix tree 可以根据一个文件的字节数偏移量,快速地确认此处的数据缓存页面是否存在,如果存在的话,获得该页面结构的地址。如图2.4所示,Linux 为每个文件 inode 都创建一棵 radix tree,用于管理属于该文件的所有缓存数据页面,并称之为该文件的地址空间 (address space)。搜索树中的每个节点的分支数缺省为 64,对于内存较少的嵌入式系统可以配置为 16。很高的分叉数意味着即使对大型文件,搜索树的高度也不大,从而可以达到很好的搜索效率。表2.1列出了不同的树高可以支持的最大页面数和文件大小。可见对于常见的 1GB 以内文件, height 一般取值仅为 1 ∼ 3,搜索代价大致上就是同等数量的内存访问次数。
 
 

页面缓存预读

当用户进程发出一个 read() 系统调用时,内核以页面为单位逐次处理读请求和进行数据传送。首先在请求文件的地址空间中查找相应的页面有没有被缓存。如果有,则不必再次从存储设备中去读,直接从该页面中拷贝数据到用户缓冲区就可以了。否则就要先申请一个空闲页面,并且将它插入该文件的地址空间 radix tree 以及该页面所在 zone 的 inactive_list,然后对新页面调用 readpage() 函数,向底层发送 I/O 请求,将所需的文件数据块从存储设备中读出来存放在该页面中,最后再从该页面中将所需数据复制到用户缓冲区。
以上是在没有预读参与情况下的文件读取过程。如果适当地对文件 I/O 的大小和时间进行调整优化,往往可以大幅度提高效率。预读算法预先向页面缓存中加入页面并发起 I/O,就可以把应用程序的读请求与实际的磁盘 I/O 操作两者分离开来,从而获得进行 I/O 优化所需的自由度。
 
本文讨论的预读算法的工作上下文如图2.5所示。在以页面缓存为中心的架构中,标准的文件系统读请求只是简单的把数据从内核中的页面缓存拷贝到程序的读缓冲区,并不直接发起磁盘 I/O。发起 I/O 并往页面缓存加入数据是预取例程的责任。通常这些内核设施是对上层透明的,应用程序并不知道缓存和预取的存在,因而也不会给预取算法以任何提示。预取算法独立自主地决定进行预取 I/O 的最佳时机、位置和大小。它的主要决策依据来自于对读请求和页面缓存的在线监控。它使用启发式的算法逻辑来猜测上层程序的 I/O 意图。
 
如图2.6所示,预取算法工作于 VFS 层,对上统一地服务于各种文件读取的系统调用 API,对下独立于具体的文件系统。当应用程序通过read(),pread(),readv(),aio_read(),sendfile(),splice() 等不同的系统调用接口请求读取文件数据时,都会进入统一的读请求处理函数 do_generic_file_read()。这个函数从页面缓存中取出数据来满足应用程序的请求,并在适当的时候调用预读例程进行必要的预读 I/O。
预读例程在 Linux 2.6.22 及之前的版本中是 page_cache_readahead(),从2.6.23 开始改为根据是否同步预读分别调用 page_cache_sync_readahead()或 page_cache_async_readahead(),进行简单的判断和预处理,如有必要再调用 ondemand_readahead() 运行按需预读算法。
预读算法发出的预读 I/O 请求交由 __do_page_cache_readahead() 进行预处理,该函数检查请求中的每一个页面是否已经在文件缓存地址空间中,如果没有的话就申请一个新页面。如果该新页面的偏移量正好是预读参数 async_size指向的位置,则为该页面置 PG_readahead 标记。最后,所有的新页面被传给 read_pages(),它们在这里被逐个加入 radix tree 和 inactive_list,并调用所在文件系统的 readpage(),将页面交付 I/O。当然,文件系统也可以提供自己的 readpages(),实现批量页面 I/O 的功能。
值得注意的是,Linux 中的预取算法对页面在块设备上的位置并不知情。它只是依据页面在文件中的逻辑偏移量进行预读决策,并期望每个文件在物理上是连续存储的。由于不连续的文件存储会严重影响 I/O 效率,Linux 下的主要文件系统都有一个重要的设计目标,即尽可能在各种恶劣条件下保证文件在存储设备上的顺序存储,把文件中连续的逻辑地址映射到存储设备中连续的物理地址。因而只要文件系统使用得当,不过份压榨可用的存储空间,就不会有严重的文件碎片 (fragmentation) 问题。

Read-ahead和Read-around

 
Linux 中的文件操作 API 按其预读策略可以分为三类,如图2.7所示。最常用的文件访问方式是,通过 read() 等系统调用接口,经由页面缓存访问一个磁盘文件。内核对这一标准文件访问路径调用通用预读算法,跟踪程序的访问序列,并进行恰当的预读。
有些应用程序——主要是 ORACLE/DB2 等大型数据库管理系统——维护自己的数据缓存池,并自主运行缓存和预读算法。因而它们希望绕过内核维护的页面缓存及其预读算法,避免重复缓存和缓存之间的拷贝开销,并实现对底层 I/O 的最终控制。内核为这些高级应用提供了直接 I/O(direct I/O) API,以及对裸设备 (raw device)的读写操作。
还有一种特别的文件操作方式是对内存映射文件 (memory mapped file) 的读写。通过系统调用接口 mmap(),应用程序可以把一个文件的地址空间映射到进程的地址空间中去。之后程序可以直接访问经过映射的虚拟内存区域来读写文件缓存中的相应数据。这种用户态进程通过内存映射访问内核中的页面缓存的方式可以简化程序设计,并避免内核缓存与应用程序缓存之间的一次内存拷贝。
mmap 文件的缓存页面是通过页面请求 (page fault) 机制加载的,其工作过程下:
1. 应用程序调用 mmap(),陷入到内核中后调用 do_mmap_pgoff()。该函数从应用程序的地址空间中分配一段区域,并创建一个 VMA 结构 ( vm_area_struct)指向该区域,并标记它与文件的映射关系,然后返回到应用程序。
2. 当应用程序访问该 VMA 中的内存时,由于内核尚未在该进程的地址空间中为相应的逻辑页面建立页表项 (PTE, Page Table Entry),会触发缺页中断,进入缺页中断处理函数。在缺页中断处理函数中,内核找到缺失的逻辑页面所属区域的 VMA 结构,判断出该区域属于文件映射,于是调用 filemap_fault()读入相应的缓存页面,并将页面地址写入当前进程的页表。经过这些步骤之后,应用程序就可以正常访问相应的数据了。
Linux 在建立文件的内存映像时,仅建立必要的 VMA 结构,其余的建立页表项及其对应的物理页面的工作全部延后到应用程序实际引用该页面,并触发缺页中断的时候去做;在每次缺页时,也仅为当前的请求页面填入页表项。这种策略被称为按需调页 (demand paging),它可以极大地减少不必要的浪费。
然而简单的按需调页会带来大量的单页面小 I/O,导致极低的磁盘 I/O 性能。因而 filemap_fault() 实现了简单的预取功能。规则如下:
• 发生缺页中断时,如果发现页面缓存中已经有相应的文件页面,则直接在进程页表中登记该页后返回。
• 如果无缓存页面,则以当前页面为中心,预取 ra_pages(=32) 个页面。不过 Linux 没有实现完整的预先调页 (pre-fault) 功能,即将这些预读的页面进一步填入进程的页表中去。只有被实际访问的页面才会被填入当前进程的页表,成为一个映射页面 (mapped page)。映射页面享有较高的缓存优先级,比普通页面更不容易被内存管理算法回收。Linux 不进行预先调页,一方面节省了一些不必要的开销,另一方面也避免了无关的缓存页面成为映射页面,无谓地占用更长时间的内存。
• 如果预读次数比预读命中次数还多 MMAP_LOTSAMISS=100,则放弃预读。这一条件是为了防止出现大量的无效预读。例如当负载是一些稀疏的随机读,在预读了 MMAP_LOTSAMISS 次后,基本上就会停止预读。但这一机制仅对大文件和大访问量有效,小于 100 × 32 × 4 = 12, 800KB 的文件访问量不能触发上述停止条件。
综上所述,Linux 实现了两种预读算法:一个适用于常规文件访问方式的通用read-ahead 算法,和一个适用于 mmap 文件访问方式的简单 read-around 算法。两种预读的调用路径如图2.8所示。其中 read-ahead 算法的设计目标为支持尽可能多的 I/O负载类型,力求准确地识别出请求序列中的顺序成分,并选择适当的时间和大小进行预读;而 read-around 算法仅执行一个简单并且贪婪的预取策略。read-around 算法对于那些以 mmap 方式访问的程序代码和数据是非常适用的,因为它们的 I/O 往往是兼有随机性和顺序性,并且具有很强的引用局域性 (locality of reference) 特征。
 

预读API

预取有两种方案:启发性的 (heuristic prefetching) 和知情的 (informed prefetching)。前者自动自发的进行预读决策,对上层应用是透明的,但是对算法的要求较高,存在命中率的问题。read-ahead 和 read-around 两种算法就属于这一范畴。后者则提供一套预读 API 接口,以便由上层程序给予明确的预读指示。
 
Linux 为应用程序提供了三个预读的系统调用: readahead(), posix_fadvise(), madvise()。其中 readahead() 是 Linux 特有的预读 API,对于需要跨平台的应用,不建议使用。它的功能等同于 posix_fadvise(POSIX_FADV_WILLNEED)。 posix_fadvise() 针对的是普通文件,它的参数和功能见表2.2; madvise() 针对的是mmap 文件,见表2.3。
这些预读 API 大体上可以分为两类。一类是告知内核预期的访问模式: NORMAL,SEQUENTIAL,RANDOM,NOREUSE,它们可以影响内核预读算法的参数和行为,使之进行更有针对性的预读。另一类是直接进行缓存页面的加载 ( WILLNEED) 和( DONTNEED) 操作,它们允许应用程序自己主导预读和缓存。例如当 MySQL 数据库管理系统即将访问一些非连续分布的表项,如果它确切地知道相关页面的地址,就可以利用 posix_fadvise(POSIX_FADV_WILLNEED) 先行展开预读。

高速缓存

分为buffer cache和page cache的意义?
Buffer cache是包含磁盘块数据的内存。
Page cache是磁盘缓存,专门用来存储页面帧,这些帧包含属于普通文件的数据。

缓冲区高速缓存

使用目的:用于磁盘块;缓冲区高速缓存用来将通过VFS访问的块的内容保留在内存中。
该部分比较复杂;需要多看几遍;

页高速缓存

使用目的:用于在磁盘上有文件映象的页;页高速缓存用来存放访问磁盘文件内容时生成的磁盘数据页;
这个部分相对于缓冲区高速缓存较为简单;
Read、write和mmap操作都是针对页高速缓存完成的,信息单元是整页,因为页IO操作要传输整页数据;页未必包含物理上相邻的磁盘块,因此不能用设备号和块号来标识页;相反,页高速缓存中的一个页的标识是通过文件的索引节点和文件的偏移量标识的;
由于页高速缓存的缓存作用,写操作实际上会被延迟。当页告诉缓存中的数据比后台存储的数据更新时,那么该数据就被称作脏数据。在内存中累积起来的脏页最终必须被写回磁盘。在以下两种情况发生时,脏页被写回磁盘:
(1)当空闲内存低于一个特定的阀值时,内核必须将脏页写回磁盘,以便释放内存。
(2)当脏页在内存中驻留时间超过一个特定的阀值时,内核必须将超时的脏页写回磁盘,以确保脏页不会无限期的驻留在内存中。
   首先,pdflush线程在系统中的空闲内存低于一个特定的阀值时,将脏页刷新回磁盘。特定的内存阀值可以通过drity_background_ratio sysctl系统调用设置。
   其次,pdflush后台例程会被周期性唤醒,将那些在内存中驻留时间过长的脏页写出,确保内存中不会有长期存在的脏页。系统管理员可以在/proc/sys/vm中设置回写的相关参数,也可以通过sysctl系统调用来设置他们。


LINUX的虚拟内存实现

地址映射机制 VA到PA
内存的分配与回收
请页机制
交换机制:page in (换入)和page out(换出)
内存共享机制
由于几乎每次对虚拟内存中的页面访问都必须先解析它,从而得到物理内存中的对应地址,所以页面操作的性能非常关键。但不幸的是,搜索内存中的物理地址速度 很有限,因此为了加快搜索,多数体系结构都实现了一个翻译后缓冲器(translation   lookaside buffer, TLB)。TLB作为一个将虚拟地址映射到物理地址的硬件缓存,当请求访问一个虚拟地址时,处理器将首先检查TLB中是否缓存了该虚拟地址到物理地址的映 射,若在缓存中直接命中,物理地址立刻返回。

交换分区swap partion

Linux 中的 交换空间(Swap space) 在物理内存(RAM)被充满时被使用。如果系统需要更多的内存资源,而物理内存已经充满,内存中不活跃的页就会被移到交换空间去。虽然交换空间可以为带有少量内存的机器提供帮助,但是这种方法不应该被当做是对内存的取代。交换空间位于硬盘驱动器上,它比进入物理内存要慢。
交换空间可以是一个专用的交换分区(推荐的方法),交换文件,或两者的组合。

Linux: 查看swap分区信息

swapon –s

点击打开链接
使用磁盘分区作为swap
mkswap -f /dev/hda6swapon /dev/hda6

使用文件作为swap交换空间
dd if=/dev/zero of=/root/swapfile bs=1024 count=131072mkswap –f /root/swapfileswapon /root/swapfile


查看进程的虚拟内存
pmap PID

系统中可分配的内存总量= 物理内存的大小+ 交换设备的大小
0 0
原创粉丝点击