Linux 版 (精华区)

发信人: netiscpu (网中自由鸟), 信区: Linux
标  题: Linux实用教程(部分)(3)
发信站: 哈工大紫丁香 (Thu May 20 17:54:59 1999), 转信

第十章

内 存 管 理

如果读者曾经在 DOS 下开发过应用程序的话,可能会对 DOS 的 640K 常规内存限制记

忆犹新。为了在 DOS 下运行大的应用程序,人们开发了各种各样的方法,其中包括象

 XMS 和 EMS 这样的内存访问规范,利用这些规范,程序可以使用 640K 之外的其他内

存。但是,这并没有从根本上解决问题。计算机中安装的内存数量各不相同,如果要在

安装有 4M 内存的计算机上运行 16M 的程序该怎么办呢?解决这一问题的办法之一就

是采用“虚拟内存”,实际上,虚拟内存是最成功的解决办法,也是流行操作系统广泛

采用的方法。

10.1 虚拟内存

虚拟内存的基本思想就是,在计算机中运行的程序,其代码、数据和堆栈的总量可以超

过实际内存的大小,操作系统只将当前使用的程序块保留在内存中,其余的程序块则保

留在磁盘上。必要时,操作系统负责在磁盘和内存之间交换程序块。

Linux 也采用虚拟内存管理机制,具体而言,虚拟内存可以提供如下好处:

大地址空间。对运行在系统中的进程而言,可用的内存总量可以超过系统的物理内存总

量,甚至可以达到好几倍。运行在 i386 平台上的 Linux 进程,其地址空间可达 4GB

。 进程保护。每个进程拥有自己的虚拟地址空间,这些虚拟地址对应的物理地址完全

和其他进程的物理地址隔离,从而避免进程之间的互相影响。 内存映射。利用内存映

射,可以将程序映象或数据文件映射到进程的虚拟地址空间中,对程序代码和数据的访

问与访问内存单元一样。 公平的物理内存分配。虚拟内存机制可保证系统中运行的进

程平等分享系统中的物理内存。 共享虚拟内存。利用虚拟内存可以方便隔离各进程的

地址空间,但是,如果将不同进程的虚拟地址映射到同一物理地址,则可实现内存共享

。这就是共享虚拟内存的本质,利用共享虚拟内存不仅可以节省物理内存的使用(如果

两个进程的部分或全部代码相同,只需在物理内存中保留一份相同的代码即可),而且

可以实现所谓“共享内存”的进程间通讯机制(两个进程通过同一物理内存区域进行数

据交换)。 Linux 中的虚拟内存采用所谓的“分页”机制。分页机制将虚拟地址空间

和物理地址空间划分为大小相同的块,这样的块称为“内存页”或简称为“页”。通过

虚拟内存地址空间的页与物理地址空间中的页之间的映射,分页机制可实现虚拟内存地

址到物理内存地址之间的转换。图 10-1 说明了两个进程的虚拟地址空间的部分页到物

理地址空间的部分页之间的映射关系。

图 10-1 虚拟地址到物理地址的映射模型

i386 平台上的 Linux 页大小为 4K 字节,而在 Alpha AXP 系统中使用 8K 字节的页

。不管是虚拟内存页还是物理内存页,它们均被给定一个唯一的“页帧编号(PFN)”

。在上述映射模型中,虚拟内存地址由两部分组成,其中一部分就是页帧编号,而另一

部分则是偏移量。CPU 负责将虚拟页帧编号翻译为相应的物理页帧编号。物理页帧编号

实际是物理地址的高位,也称为页基地址,页基地址加上偏移量就是物理内存地址(这

和 DOS 64K 段基地址及段偏移量类似)。为此,CPU 利用“页表”实现虚拟页帧编号

到物理页帧编号的转换。

如图 10-1,操作系统可以为不同的进程准备进程的私有页表,每个页表项包含物理页

帧编号、页表项的有效标志以及相应的物理页访问控制属性,访问控制属性指定了页是

只读页、只写页、可读可写页还是可执行代码页,这有利于进行内存保护,例如,进程

不能向代码页中写入数据,图 10-2 给出了 i386 系统中的页表项格式。参照图 10-1

,CPU 利用虚拟页帧编号作为访问进程页表的索引来检索页表项,如果当前页表项是有

效的,处理器就可以从该页表项中获得物理页帧编号,进而获得物理内存中的页基地址

,加上虚拟内存中的偏移量就是要访问的物理地址;如果当前页表项无效,则说明进程

访问了一个不存在的虚拟内存区,在这种情况下,CPU 将会向操作系统报告一个“页故

障”,操作系统则负责对页故障进行处理。

图 10-2 i386 系统中的页表项格式

一般而言,页故障的原因可能是因为进程访问了非法的虚拟地址,也可能是因为进程要

访问的物理地址当前不在物理内存中,这时,操作系统负责将所需的内存页装入物理内

存。

上面就是虚拟内存的抽象模型,但 Linux 中的虚拟内存机制要复杂一些。从性能的角

度考虑,如果内核本身也需要进行分页,并为内核代码和数据页维护一个页表的话,则

系统的性能会下降很多,为此,Linux 的内核运行在所谓的“物理地址模式”,CPU 不

必在这种模式下进行地址转换,物理地址模式的实现和实际的 CPU 类型有关。

10.2 Linux 的内存页表

在 i386 系统中,虚拟地址空间的大小是 4G,因此,全部的虚拟内存空间划分为 1M 

页。如果用一个页表描述这种映射关系,那么这一映射表就要有 1M 个表项,当每个表

项占用 4 个字节时,全部表项占用的字节数就为 4M,为了避免占用如此巨大的内存资

源来存储页表,i386 系列 CPU 采用两级页表。类似地,Alpha AXP 系统使用三级页表

。Linux 为了避免硬件的不同细节影响内核的实施,假定有三级页表。如图 10-3 所示

,一个虚拟地址可分为多个域,不同域的数据指出了对应级别页表中的偏移量。为了将

一个虚拟地址转换为物理地址,处理器根据这三个级别域,每次将一个级别域中的值转

换为对应页表的物理页偏移量,然后从中获得下一级页表的页帧编号。如此进行三次,

就可以找出虚拟内存对应的实际物理内存。前面提到,不同 CPU 的页级数目不同,Li

nux 内核源代码则利用 C 语言的宏将具体的硬件差别隐藏了起来。例如,对 i386 的

 Linux 内核代码来说,三级的页表转换宏实际只有两个在起作用。

10.3 内存页的分配和释放

系统运行过程中,经常需要进行物理内存页的分配或释放。例如,执行程序时,操作系

统需要为相应的进程分配页,而进程终止时,则要释放这些物理页。再如,页表本身也

需要动态分配和释放。物理页的分配和释放机制及其相关数据结构是虚拟内存子系统的

关键部分。

图 10-3 多级页表示意图

一般而言,有两种方法可用来管理内存的分配和释放。一种是采用位图,另外一种是采

用链表。

利用位图可记录内存单元的使用情况。例如,如果某个系统有 1024 字节内存,而内存

的分配单元是 4 字节,则可以利用 1024/(4*8) = 32 个字节来记录使用情况。这 

32 个字节的每个位分别代表相应分配单元的使用情况。如果位图中某个位为 1,则对

应的分配单元是空闲的。利用这一办法,内存的分配就可以通过对位值的检测来简化。

如果一次要分配 5 个单元的空间,内存管理程序就需要找出 5 个连续位值均为 1 的

位图位置,但这种操作比较慢,因为连续的位有时要跨越字节边界。

利用链表则可以分别记录已分配的内存单元和空闲的内存单元。通常这些内存单元设计

为双向链表结构,从而可加速空闲内存的搜索或链表的处理。这种方法相对位图方法要

好一些,也更加有效。

Linux 的物理页分配采用链表和位图结合的方法。参照图 10-4,Linux 内核定义了一

个称为 free_area 的数组,该数组的每一项描述某一种页块的信息。第一个元素描述

单个页的信息,第二个元素则描述以 2 个页为一个块的页块信息,第三个元素描述以

 4 个页为一个块的页块信息,依此类推,所描述的页块大小以 2 的倍数增加。free_

area 数组的每项包含两个元素:list 和 map。list 是一个双向链表的头指针,该双

向链表的每个节点包含空闲页块的起始物理页帧编号;而 map 则是记录这种页块组分

配情况的位图,例如,位图的第 N 位为 1,则表明第 N 个页块是空闲的。从图中也可

以看到,用来记录页块组分配情况的位图大小各不相同,显然页块越小,位图越大。

图 10-4 中,free_area 数组的元素 0包含了一个空闲页(页帧编号为 0);而元素 

2 则包含了两个以 4 页为大小的空闲页块,第一个页块的起始页帧编号为 4,而另一

个页块的起始页帧编号为 56。

图 10-4 Linux 物理页块的分配示意

Linux 采用 Buddy 算法有效分配和释放物理页块。按照上述数据结构,Linux 可以分

配的内存大小只能是 1 个页块,2 个页块或 4 个页块等等。在分配物理页块时,Lin

ux 首先在 free_area 数组中搜索大于或等于要求尺寸的最小页块信息,然后在对应的

 list 双向链表中寻找空闲页块,如果没有空闲页块,Linux 则继续搜索更大的页块信

息,直到发现一个空闲的页块为止。如果搜索到的页块大于满足要求的最小页块,则只

需将该页块剩余的部分划分为小的页块并添加到相应的 list 链表中。

页块的分配会导致内存的碎片化,而页块的释放则可以将页块重新组合成大的页块。如

果和被释放的页块大小相等的相邻页块是空闲的,则可以将这两个页块组合成一个大的

页块,这一过程一直继续,直到把所有可能的页块组合成尽可能大的页块为止。

知道了上述原理,读者可以自己想象系统启动时,初始的 free_area 数组中的信息。

10.4 内存映射和需求分页

当某个程序映象开始运行时,可执行映象必须装入进程的虚拟地址空间。如果该程序用

到了任何一个共享库,则共享库也必须装入进程的虚拟地址空间。实际上,Linux 并不

将映象装入物理内存,相反,可执行文件只是被链接到进程的虚拟地址空间中。随着程

序的运行,被引用的程序部分会由操作系统装入物理内存。这种将映象链接到进程地址

空间的方法称为“内存映射”。

每个进程的虚拟内存由一个 mm_struct 结构代表,我们将在下一章中详细讲述该结构

。该结构中实际包含了当前执行映象的有关信息,并且包含了一组指向 vm_area_stru

ct 结构的指针。如图 10-5 所示,每个 vm_area_struct 描述了一个虚拟内存区域的

起点和终点、进程对内存的访问权限以及一个对内存的操作例程集。操作例程集是 Li

nux 操作该内存区域时所使用的例程集合。例如,当进程试图访问的虚拟内存当前不在

物理内存当中时(通过页故障),Linux 就可以利用操作集中的一个例程执行正确的操

作,在这种情况下为 nopage 操作。

图 10-5 vm_area_struct 数据结构示意图

当可执行映象映射到进程的虚拟地址空间时,将产生一组 vm_area_struct 结构来描述

虚拟内存区域的起始点和终止点,每个 vm_area_struct 结构代表可执行映象的一部分

,可能是可执行代码,也可能是初始化的变量或未初始化的数据。随着 vm_area_stru

ct 结构的生成,这些结构所描述的虚拟内存区域上的标准操作函数也由 Linux 初始化



某个可执行映象映射到进程虚拟内存中并开始执行时,因为只有很少一部分装入了物理

内存,因此很快就会访问尚未装入物理内存的虚拟内存区域。这时,处理器将向 Linu

x 报告一个页故障及其对应的故障原因。

这种页故障的出现原因有两种,一是程序出现错误,例如向随机物理内存中写入数据,

这种情况下,虚拟内存是无效的,Linux 将向程序发送 SIGSEGV 信号并终止程序的运

行;另一种情况是,虚拟地址有效,但其所对应的页当前不在物理内存中,这时,操作

系统必须从磁盘映象或交换文件中将内存装入物理内存。

那么,Linux 如何判断页故障发生时,虚拟内存地址是否是有效的呢?如前所述,Lin

ux 利用 vm_area_struct 数据结构描述进程的虚拟内存空间,为了查找出现页故障虚

拟内存相应的 vm_area_struct 结构的位置,Linux 内核同时维护一个由 vm_area_st

ruct 结构形成的 AVL(Adelson-Velskii and Landis)树。利用 AVL 树,可快速寻找

发生页故障的虚拟地址所在的内存页区域。如果搜索不到这一内存区域,则说明该虚拟

地址是无效的,否则该虚拟地址是有效的。

也有可能因为进程在虚拟地址上进行的操作非法而产生页故障,例如在只读页中写入数

据。这时操作系统会同样发送内存错误信号到该进程。有关页的访问控制信息(只读页

、只写页、可读可写页、可执行代码页等)包含在页表项中。

对有效的虚拟地址,Linux 必须区分页所在的位置,即判断页是在交换文件中,还是在

可执行映象中。为此,Linux 通过页表项中的信息区分页所在的位置。如果该页的页表

项是无效的,但非空,则说明该页处于交换文件中,操作系统要从交换文件装入页(有

关内存交换的内容在下一节中讲述)。否则,默认情况下,Linux 会分配一个新的物理

页并建立一个有效的页表项;对于映象的内存映射来讲,则会分配新的物理页,更新页

表项属性信息,并从映象中装入页。

这时,所需的页装入了物理内存,页表项也同时被更新,然后进程就可以继续执行了。

这种只在必要时才将虚拟页装入物理内存的处理称为“需求分页”。

在处理页故障的过程中,因为要涉及到磁盘访问等耗时操作,因此操作系统会选择另外

一个进程进入执行状态。

10.5 Linux 页缓存

经内存映射的文件每次只读取一页内容,读取后的页保存在页缓存中,利用页缓存,可

提高文件的访问速度。如图 10-6 所示,页缓存由 page_hash_table 组成,它是一个

 mem_map_t 数据结构的指针向量。页缓存的结构是 Linux 内核中典型的哈希表结构。

众所周知,对计算机内存的线性数组的访问是最快速的访问方法,因为线性数组中的每

一个元素的位置都可以利用索引值直接计算得到,而这种计算是简单的线性计算。但是

,如果要处理大量数据,有时由于受到存储空间的限制,采用线性结构是不切合实际的

。但如果采用链表等非线性结构,则元素的检索性能又会大打折扣。哈希表则是一种折

衷的方法,它综合了线性结构和非线性结构的优点,可以在大量数据中进行快速的查找

。哈希表的结构有多种,在 Linux 内核中,常见的哈希结构和图 10-6 的结构类似。

要在这种哈希表中访问某个数据,首先要利用哈希函数以目标元素的某个特征值作为函

数自变量生成哈希值作为索引,然后利用该索引访问哈希表的线性指针向量。哈希线性

表中的指针代表一个链表,该链表所包含的所有节点均具有相同的哈希值,在该链表中

查找可访问到指定的数据。哈希函数的选择非常重要,不恰当的哈希函数可能导致大量

数据成涞酵还V担庵智榭鱿拢氐牟檎医嗟焙氖薄5牵绻≡袂〉钡墓

:蚩梢栽谛阅芎涂占渖系玫骄庑Ч在 Linux 页缓存中,访问 page_hash

_table 的索引由文件的 VFS(虚拟文件系统)索引节点 inode 和内存页在文件中的偏

移量生成。有关 VFS 索引节点的内容将在第十三章中讲述,在这里,应知道每个文件

的 VFS 索引节点 inode 是唯一的。

图 10-6 Linux 页缓存示意图

当系统要从内存映射文件中读取某页时,首先在页缓存中查找,如果发现该页保存在缓

存中,则可以免除实际的文件读取,而只需从页缓存中读取,这时,指向 mm_map_t 数

据结构的指针被返回到页故障的处理代码;如果该页不在缓存中,则必须从实际的文件

系统映象中读取页,Linux 内核首先分配物理页然后从磁盘读取页内容。

如果可能,Linux 还会预先读取文件中下一页内容,这样,如果进程要连续访问页,则

下一页的内容不必再次从文件中读取了,而只需从页缓存中读取。

随着映象的读取和执行,页缓存中的内容可能会增多,这时,Linux 可移走不再需要的

页。当系统中可用的物理内存量变小时,Linux 也会通过缩小页缓存的大小而释放更多

的物理内存页。

10.6 内存交换

当物理内存出现不足时,Linux 内存管理子系统需要释放部分物理内存页。这一任务由

内核的交换守护进程 kswaped 完成,该内核守护进程实际是一个内核线程,它的任务

就是保证系统中具有足够的空闲页,从而使内存管理子系统能够有效运行。

在系统启动时,这一守护进程由内核的 init 进程启动。当内核的交换定时器到期时,

该进程开始运行。如果 kswaped 发现系统中的空闲页很少,该进程将按照下面的三种

方法减少系统使用的物理页:

1. 减少缓冲区和页高速缓存的大小。页高速缓存中包含内存映射文件的页,可能包含

一些系统不再需要的页,类似地,缓冲区高速缓存中也可能包含从物理设备中读取的或

写入物理设备的数据,这些缓冲区也可能不再需要,因此,这两个高速缓存可用来释放

出空闲页。但是,同时处于这两个高速缓存中的页是不能丢弃的。Linux 利用“时钟”

算法从系统中选择要丢弃的页,也即每次循环检查 mem_map 页向量中不同的页块,象

时钟的分针循环转动一样。时钟算法的原理见图 10-7。每次内核的交换进程运行时,

根据对物理内存的需求而选择不同页块大小的 mem_map 向量进行检查。如果发现某页

块处于上述两个高速缓存中,则释放相应的缓冲区,并将页块重新收入 free_area 结

构。

图 10-7 页交换的时钟算法

2. 将System V 共享内存页交换出物理内存。System V 共享内存页实际是一种进程间

通讯机制,系统通过将共享内存页交换到交换文件而释放物理内存。Linux 同样使用时

钟算法选择要交换出物理内存的页。

3. 将页交换出物理内存或丢弃。kswaped 首先选择可交换的进程,或其中某些页可从

内存中交换出或丢弃的进程。可执行映象的大部分内容可从磁盘映象中获取,因此,这

些页可丢弃。选定要交换的进程之后,Linux 将把该进程的一小部分页交换出内存,而

大部分不会被交换,另外,被锁定的页也不会被交换。Linux 利用页的寿命信息选择要

交换的页,也即所谓“最近最少使用(LRU)”算法。

页的寿命信息保存在 mem_map_t 结构中。最初分配某个页时,页的寿命为 3,每次该

页被访问,其寿命增加 3,直到 20 为至;而当内核的交换进程运行时,页寿命减 1。

如果某个页的寿命为 0,则该页可作为交换候选页。如果是“脏”页(该信息保存在页

表项中),则可将该页交换出物理内存。但是,进程的虚拟内存区域可具有自己的交换

操作例程(定义在虚拟内存操作集中),这时,将利用该例程执行交换操作,否则,交

换守护进程在交换文件中分配页,并将该页写入交换文件。

当某物理页交换到交换文件之后,该页对应的页表项被标志为无效,同时包换该页在交

换文件中的位置信息;而被释放出的物理页则被收回到 free_area 数据结构中。

根据被释放的页数目,kswaped 会自动调节交换定时器的间隔,以便能够有足够的时间

释放更多的页而保证足够的空闲页。

交换文件中的页是经过修改的页(通过在页表项中设置相应的位而标志该页为“脏”页

),则当进程再次访问该页时,操作系统必须从交换文件中将该页交换到物理内存。

10.7 高速缓存

不管在硬件设计还是软件设计中,高速缓存是获得高性能的常用手段。Linux 使用了多

种和内存管理相关的高速缓存:

缓冲区高速缓存:缓冲区高速缓存中包含了由块设备使用的数据缓冲区。这些缓冲区中

包含了从设备中读取的数据块或写入设备的数据块。缓冲区高速缓存由设备标识号和块

标号索引,因此可以快速找出数据块。如果数据能够在缓冲区高速缓存中找到,则系统

就没有必要在物理块设备上进行实际的读操作。 页高速缓存:这一高速缓存用来加速

对磁盘上的映象和数据的访问。它用来缓存某个文件的逻辑内容,并通过文件的 VFS 

索引节点和偏移量访问。当页从磁盘上读到物理内存时,就缓存在页高速缓存中。 交

换高速缓存:只有修改后(脏)的页才保存在交换文件中。修改后的页写入交换文件后

,如果该页再次被交换但未被修改时,就没有必要写入交换文件,相反,只需丢弃该页

。交换高速缓存实际包含了一个页表项链表,系统的每个物理页对应一个页表项。对交

换出的页,该页表项包含保存该页的交换文件信息,以及该页在交换文件中的位置信息

。如果某个交换页表项非零,则表明保存在交换文件中的对应物理页没有被修改。如果

这一页在后续的操作中被修改,则处于交换缓存中的页表项被清零。当 Linux 需要从

物理内存中交换出某个页时,它首先分析交换缓存中的信息,如果缓存中包含该物理页

的一个非零页表项,则说明该页交换出内存后还没有被修改过,这时,系统只需丢弃该

页。 硬件高速缓存:常见的硬件缓存是对页表项的缓存,这一工作实际由处理器完成

,其操作和具体的处理器硬件有关,对这一缓存的描述已超出本书的讲述范围。 10.8

 相关系统工具和系统调用

实际上,Linux 可以利用文件系统中通常的文件作为交换文件,也可以利用某个分区进

行交换操作,因此,通常把交换文件或交换分区称为“交换空间”。在交换分区上的交

换操作较快,而利用交换文件可方便改变交换空间大小。Linux 还可以使用多个交换分

区或交换文件进行交换操作。本节主要讲述有关交换空间的系统工具。

10.8.1 建立交换空间

作为交换空间的交换文件实际就是通常的文件,但文件的扇区必须是连续的,也即,文

件中必须没有“洞”,另外,交换文件必须保存在本地硬盘上。

由于内核要利用交换空间进行快速的内存页交换,因此,它不进行任何文件扇区的检查

,而认为扇区是连续的。由于这一原因,交换文件不能包含洞。可用下面的命令建立无

洞的交换文件:

$ dd if=/dev/zero of=/extra-swap bs=1024 count=2048

2048+0 records in

2048+0 records out

上面的命令建立了一个名称为 extra-swap,大小为 2048K 字节的交换文件。对 i386

 系统而言,由于其页尺寸为 4K,因此最好建立一个大小为 4K 倍数的交换文件;对 

Alpha AXP 系统而言,最好建立大小为 8K 倍数的交换文件。

交换分区和其他分区也没有什么不同,可象建立其他分区一样建立交换分区。但该分区

不包含任何文件系统。分区类型对内核来讲并不重要,但最好设置为 Linux Swap 类型

(即类型 82)。

建立交换文件或交换分区之后,需要在文件或分区的开头写入签名,写入的签名实际是

由内核使用的一些管理信息。写入签名的命令为 mkswap,如下所示:

$ mkswap /extra-swp 2048

Setting up swapspace, size = 2088960 bytes

$

这时,新建立的交换空间尚未开始使用。使用 mkswap 命令时必须小心,因为该命令不

会检查文件或分区内容,因此极有可能覆盖有用的信息,或破坏分区上的有效文件系统

信息。

Linux 内存管理子系统将每个交换空间的大小限制在 127M (实际为 (4096-10)*8*40

96 = 133890048 Byte = 127.6875Mb)。可以在系统中同时使用 16 个交换空间,从而

使交换空间总量达到 2GB。

10.8.2 使用交换空间

利用 swapon 命令可将经过初始化的交换空间投入使用。如下所示:

$ swapon /extra-swap

$

如果在 /etc/fstab 文件中列出交换空间,则可自动将交换空间投入使用:

/dev/hda5 none swap sw 0 0

/extra-swap none swap sw 0 0

实际上,启动脚本会运行 swapon a 命令,从而将所有出现在 /etc/fstab 文件中的交

换空间投入使用。

利用 free 命令,可查看交换空间的使用。如下所示:

$ free

total used free shared buffers

Mem: 15152 14896 256 12404 2528

-/+ buffers: 12368 2784

Swap: 32452 6684 25768

$

该命令输出的第一行(Mem: ) 显示了系统中物理内存的使用情况。total 列显示的是

系统中的物理内存总量;used 列显示正在使用的内存数量;free 列显示空闲的内存量

;shared 列显示由多个进程共享的内存量,该内存量越多越好;buffers 显示了当前

的缓冲区高速缓存的大小。

输出的最后一行 (Swap: ) 显示了有关交换空间的类似信息。如果该行的内容均为 

0,表明当前没有活动的交换空间。

利用top 命令或查看 /proc 文件系统中的 /proc/meminfo 文件可获得相同的信息。

利用 swapoff 命令可移去使用中的交换空间。但该命令应只用于临时交换空间,否则

有可能造成系统崩溃。

swapoff a 命令按照 /etc/fstab 文件中的内容移去所有的交换空间,但任何手工投入

使用的交换空间保留不变。

10.8.3 分配交换空间

大多数人认为,交换空间的总量应该是系统物理内存量的两倍,实际上这一规则是不正

确的,正确的交换空间大小应按如下规则确定:

1. 估计需要的内存总量。运行想同时运行的所有程序,并利用 free 或 ps 程序估计

所需的内存总量,只需大概估计。

2. 增加一些安全性余量。

3. 减去已有的物理内存数量,然后将所得数据圆整为 MB,这就是应当的交换空间大

小。

4. 如果得到的交换空间大小远远大于物理内存量,则说明需要增加物理内存数量,否

则系统性能会因为过分的页交换而下降。

当计算的结果说明不需要任何交换空间时,也有必要使用交换空间。Linux 从性能的角

度出发,会在磁盘空闲时将某些页交换到交换空间中,以便减少必要时的交换时间。另

外,如果在不同的磁盘上建立多个交换空间,有可能提高页交换的速度,这是因为某些

硬盘驱动器可同时在不同的磁盘上进行读写操作。

10.8.4 关于缓冲区高速缓存

Linux 采用了缓冲区高速缓存机制,而不同于其他操作系统的“写透”方式,因此有可

能出现这种情况:写磁盘的命令已经返回,但实际的写操作还未执行。

基于上述原因,应当使用正常的关机命令关机,而不应直接关掉计算机的电源。用户也

可以使用 sync 命令刷新缓冲区高速缓存。在 Linux 系统中,除了传统的 update 守

护进程之外,还有一个额外的守护进程 dbflush,这一进程可频繁运行不完整的 sync

 从而可避免有时由于 sync 命令的超负荷磁盘操作而造成的磁盘冻结。

dbflush 在 Linux 系统中由 update 启动。如果由于某种原因该进程僵死了,则内核

会发送警告信息,这时需要手工启动该进程(/sbin/update)。

10.8.5 系统调用

如期所述,系统调用是应用程序和内核之间的功能接口。大部分的系统调用包含在 Li

nux 的 libc 库中,通过标准的 C 函数调用方法可以调用这些系统调用。但是,少数

系统调用并没有相应的 libc 库接口,这时,可通过 syscall 系统调用使用这些系统

调用:

syscall (SYS_num, arg1, ...);

其中,SYS_num 是系统调用的编号,arg1 是该系统调用的参数。在 i386 系统中,可

最多传递 5 个系统调用参数,这和 CPU 可用的寄存器数量有关。

表 10-1 简要列出了和内存管理相关的系统调用。标志列中各字母的意义为:

m:手册页可查;

+:POSIX 兼容;

-:Linux 特有;

c:libc 包含该系统调用;

!:该系统调用和其他系统调用类似,应改用其他 POSIX 兼容系统调用。

在后面介绍系统调用时,使用相同的标志字母。

表 10-1 相关系统调用

系统调用

 说明

 标志

 

bdflush

 将脏的缓冲区刷新到磁盘上

 -c

 

brk

 修改数据段(虚拟内存区域)的大小

 mc

 

gettrlimit

 获取资源限制

 mc

 

gettrusage

 获取资源使用情况

 m

 

idle

 将某个进程设置为可选择交换的进程

 


--
☆ 来源:.哈工大紫丁香 bbs.hit.edu.cn.[FROM: bin@mtlab.hit.edu.cn]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:217.543毫秒