HACK #14 虚拟存储子系统的调整

本节介绍如何使用/proc进行虚拟存储子系统的调整。
虚拟空间存储方式
在Linux上向应用程序分配内存时,是通过以页面为单位的虚拟存储方式进行的。采用虚拟存储方式,在实际操作中具有不需要确保连续的物理内存(不用担心内存碎片)的优点。最近的处理器大部分都具备用于虚拟存储的处理器嵌入式TLB(Translation lookaside buffer,旁路转换缓冲区,或称为页表缓冲区)和处理不存在的页面访问的结构。除了部分嵌入式应用外,大多数Linux应用中都可以使用虚拟存储方式的内存管理。
使用虚拟存储方式的内存管理,具有下列特点。
程序使用的页面是在应用程序最初访问时由内核分配的。
如果分配的页面为程序文本、有初始值的数据(.data)区域或(被mmap的)数据文件区域,则在页面分配的同时从对应的文件读取数据,页面通过这些数据初始化。
如果对于不存在拥有初始值的数据的区域,则只进行页面分配处理。该页面作为匿名(anonymous)页面处理。
应用程序通过malloc()等分配可用的存储区时,不会立刻向该区域(空间)分配实际的页面。而是在必要时仅分配需要用到的页面。
多数应用程序一般都不会使用分配到的所有存储区。有时分配与最大数据量大小相等的缓冲区,有时也会需要为散列表(hash table)等分配没有实体的空间。这种情况下,使用虚拟存储方式延迟内存分配,就不需要向未使用的空间分配内存。
由于根据需要分配页面,因此应用程序启动时不能事先得知该应用程序最终使用的最大页面数,这是虚拟存储方式的缺点。进程在逻辑上的虚拟内存空间与实际分配给该空间(已使用)的实际内存空间之间不再有直接的关系,最大使用内存量会根据环境(处理的数据等)的变化而变化。
应用程序使用的内存量对系统稳定性有很大的影响,因此不能无限度地使用内存。
虚拟空间超额使用量的调整
只存在一个进程时,即使不知道该进程的最大内存使用量,也可以使用已有的进程单位的资源限制功能对内存使用量进行充分限制。实际安装的内存为10GB时,可以使用ulimiti命令将该进程的最大虚拟空间大小设置为10GB。考虑到应用程序与虚拟空间大小相比要使用多少物理内存,也可以设置比10GB更大的虚拟空间大小(在Linux中未安装进程的物理内存驻留大小限制功能,因此无法使用。)
但是,考虑到多个进程互相争夺内存的情况,就需要限制整个系统的虚拟空间量。如上所述,即使无限度地向进程分配虚拟空间,只要不实际使用也就没问题。分配给进程的虚拟空间的大小与本质上实际安装的物理量无关。但是,从系统的稳定性来看,分配的虚拟空间大小应该保证达到物理内存的量。将没有物理内存保证的虚拟空间进行大量分配,并实际访问时,如果同时分配大量的物理页面,系统就会崩溃。
在Linux中有规定“允许超过物理内存量分配多少虚拟空间”的参数,通过下列两个/proc入口来进行控制。

/proc/sys/vm/overcommit_memory
/proc/sys/vm/overcommit_ratio

/proc/sys/vm/overcommit_memory是控制虚拟空间分配的策略的参数,可以设置下列3种值。

OVERCOMMIT_GUESS(0)
OVERCOMMIT_ALWAYS(1)
OVERCOMMIT_NEVER(2)

默认为OVERCOMMIT_GUESS。

# cat/proc/sys/vm/overcommit_memory
0

设置为OVERCOMMIT_NEVER时,执行下列命令。

# echo 2 > /proc/sys/vm/overcommit_memory

可以在/proc/sys/vm/overcommit_ratio中指定允许过量使用的虚拟空间所占物理内存总量的百分比。默认为50%。可以分配的最大虚拟空间为总物理内存量的150%。
下面介绍overcommit_memory的不同取值对应的不同虚拟空间分配。

OVERCOMMIT_GUESS

overcommit_memory的默认值为OVERCOMMIT_GUESS。指定这个参数时,预测将空闲内存、页面缓存量、空闲交换区量、可回收slab(长字节)量等回收的页面数,虚拟空间要求分配的量比这个数小时,分配成功。
(请注意,在多个进程同时要求大量的虚拟空间时是无法正确预测的。下面所述的OVERCOMMIT_NEVER中就没有这种问题。)
在OVERCOMMIT_GUESS的情况下,可分配的虚拟空间大小基本就是物理内存大小和交换区大小的合计值。物理内存为2GB,交换区为2GB,当前消耗1GB时,还可以分配约3GB的虚拟空间。

OVERCOMMIT_ALWAYS

在OVERCOMMIT_ALWAYS的情况下,虚拟空间分配总是成功。即使对于过大的虚拟空间要求,也会分配虚拟空间。可以在与实际安装的物理内存量完全无关的形态下使用虚拟空间,如前面所述的散列表等。
OVERCOMMIT_NEVER
在OVERCOMMIT_NEVER的情况下,对可分配虚拟空间量的管理更加严格。
首先,记录下整个系统内已分配的虚拟空间量。这个值严格由系统进行集中管理,在分配或释放虚拟空间时重新计算。这个值为/proc/meminfo的Committed_AS。
对于虚拟空间大小的计算也比其他参数严格。例如,在OVERCOMMIT_GUESS的情况下,对mmap系统调用设置了MAP_NORESERVE的虚拟空间量不添加到Committed_AS中。但是,在OVERCOMMIT_NEVER的情况下会添加到Committed_AS中。指定了MAP_NORESERVE的区域也作为可能分配物理内存的虚拟空间处理。
将“所有物理内存量+总交换区量”加上通过/proc/sys/vm/overcommit_ratio指定的比例得到的值,作为可分配的虚拟空间总量。这个值可以使用/proc/meminfo的CommitLimit查看。CommitLimit的值仅在利用OVERCOMMIT_NEVER时有效。在OVERCOMMIT_GUESS、OVERCOMMIT_ALWAYS的情况下,这个项目没有意义。
要求分配虚拟空间时,如果“整个系统中已经分配(Committed_AS)”的虚拟空间量超过“可分配虚拟空间量(CommitLimit)”,则分配失败。
小结
本节介绍了虚拟空间的过量使用。根据系统的不同,需要对虚拟空间分配策略进行调整,管理要确保的内存量。
—Naohiro Ooiwa
关于进程的虚拟内存分配
在Hack #13中已经介绍过,用malloc()等预留内存后,就会分配虚拟内存。但使用mmap()时分配虚拟内存有一些限制条件。
例如,启动进程后,动态链接器就会进行动态库(library)的安装。由动态库的执行文件信息决定mmap的大小。接着,链接器会配置文本段和数据段,如果是64位操作系统,就会设置alignment,当文本段和数据段较小时,它们之间有约2MB的空间。
以适当的访问权限对代码段和数据段执行mmap命令,使用mprotect(PROT_NONE)让此外未使用的段无法访问。
下面是在CentOS 5.4 64位操作系统下启动apache时ps命令的结果。

# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
...
root 4469 8.5 0.5 238244 11136 ? Ss 10:27 0:00 /usr/sbin/httpd
apache 4471 0.0 0.3 238244 6516 ? S 10:27 0:00 /usr/sbin/httpd
apache 4472 0.0 0.3 238244 6512 ? S 10:27 0:00 /usr/sbin/httpd
apache 4473 0.0 0.3 238244 6512 ? S 10:27 0:00 /usr/sbin/httpd
...

每一个进程确保了200MB以上的虚拟内存(VSZ)。使用pmap命令查看详细内容。pmap命令显示的是进程的内存映射。

# pmap -d 4469
4469: /usr/sbin/httpd
Address Kbytes Mode Offset Device Mapping
...
00002aff7a62e000 20 r-x-- 0000000000000000 008:00005 mod_cgi.so 文本段
00002aff7a633000 2048 ----- 0000000000005000 008:00005 mod_cgi.so 剩余区域
00002aff7a833000 8 rw--- 0000000000005000 008:00005 mod_cgi.so 数据段
00002aff7a835000 8 r-x-- 0000000000000000 008:00005 mod_version.so
00002aff7a837000 2044 ----- 0000000000002000 008:00005 mod_version.so
00002aff7aa36000 8 rw--- 0000000000001000 008:00005 mod_version.so
00002aff7aa38000 216 r-x-- 0000000000000000 008:00005 mod_perl.so
00002aff7aa6e000 2044 ----- 0000000000036000 008:00005 mod_perl.so
00002aff7ac6d000 16 rw--- 0000000000035000 008:00005 mod_perl.so
00002aff7ac71000 1200 r-x-- 0000000000000000 008:00005 libperl.so
00002aff7ad9d000 2044 ----- 000000000012c000 008:00005 libperl.so
00002aff7af9c000 36 rw--- 000000000012b000 008:00005 libperl.so
...
00002aff85b6e000 3844 rw--- 00002aff85b6e000 000:00000 [ anon ]
00007fff79a9c000 84 rw--- 00007ffffffea000 000:00000 [ stack ]
ffffffffff600000 8192 ----- 0000000000000000 000:00000 [ anon ]
mapped: 246436K writeable/private: 6580K shared: 692K

在32位的CentOS下的情况如下所示。

# ps aux
...
root 2715 6.6 7.6 23168 9488 ? S 10:58 0:00 /usr/sbin/httpd
apache 2718 0.0 3.8 23168 4796 ? S 10:58 0:00 /usr/sbin/httpd
apache 2719 0.0 3.8 23168 4796 ? S 10:58 0:00 /usr/sbin/httpd
apache 2720 0.0 3.8 23168 4796 ? S 10:58 0:00 /usr/sbin/httpd
...

每一个进程的虚拟内存(VSZ)为约23MB。使用pmap命令查看内存映射,内容如下。

# pmap -d 2715
...
00508000 28 r-x-- 0000000000000000 008:00002 mod_proxy_ftp.so
0050f000 8 rwx-- 0000000000006000 008:00002 mod_proxy_ftp.so
00511000 4 r-x-- 0000000000000000 008:00002 mod_suexec.so
00512000 8 rwx-- 0000000000000000 008:00002 mod_suexec.so
00514000 16 r-x-- 0000000000000000 008:00002 mod_disk_cache.so
00518000 8 rwx-- 0000000000004000 008:00002 mod_disk_cache.so
0051a000 8 r-x-- 0000000000000000 008:00002 mod_file_cache.so
0051c000 8 rwx-- 0000000000001000 008:00002 mod_file_cache.so
0051e000 20 r-x-- 0000000000000000 008:00002 mod_cgi.so
00523000 8 rwx-- 0000000000004000 008:00002 mod_cgi.so
00525000 8 r-x-- 0000000000000000 008:00002 libutil-2.5.so
00527000 4 r-x-- 0000000000001000 008:00002 libutil-2.5.so
00528000 4 rwx-- 0000000000002000 008:00002 libutil-2.5.so
...
mapped: 23168K writeable/private: 4916K shared: 684K

在32位操作系统的情况下不存在访问权限为-----(PROT_NONE)的区域。这是因为32位操作系统、64位操作系统中链接器执行共享库映射的策略不同。将上述pmap命令的结果进行比较,看起来像是64位操作系统浪费了虚拟内存,但不用担心。
映射的区域中没有访问权限(PROT_NONE)的区域、写入权限(PROT_WRITE)或不是共享(MAP_SHARED)的区域不添加到Committed_AS中。因此在64位操作系统的情况下,即使消耗虚拟内存地址空间,也不会消耗实际的虚拟内存。

《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #14 虚拟存储子系统的调整的更多相关文章

  1. 《Linux内核精髓:精通Linux内核必会的75个绝技》目录

    1章 内核入门HACK #1 如何获取Linux内核HACK #2 如何编译Linux内核HACK #3 如何编写内核模块HACK #4 如何使用GitHACK #5 使用checkpatch.pl检 ...

  2. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #1 如何获取Linux内核

    HACK #1 如何获取Linux内核 本节介绍获取Linux内核源代码的各种方法.“获取内核”这个说法看似简单,其实Linux内核有很多种衍生版本.要找出自己想要的源代码到底是哪一个,必须首先理解各 ...

  3. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #15 ramzswap

    HACK #15 ramzswap 本节介绍将一部分内存作为交换设备使用的ramzswap.ramzswap是将一部分内存空间作为交换设备使用的基于RAM的块设备.对要换出(swapout)的页面进行 ...

  4. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #3 如何编写内核模块

    HACK #3 如何编写内核模块 本节将介绍向Linux内核中动态添加功能的结构—内核模块的编写方法.内核模块Linux内核是单内核(monolithic kernel),也就是所有的内核功能都集成在 ...

  5. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #19 ext4的调整

    HACK #19 ext4的调整 本节介绍可以从用户空间执行的ext4调整.ext4在sysfs中有一些关于调整的特殊文件(见表3-6).使用这些特殊文件,就不用进行内核编译.重启,直接从用户空间确认 ...

  6. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #21FUSE

    HACK #21FUSE 本节将介绍使用用户进程的文件系统框架—FUSE.FUSE概要FUSE(Filesystem in Userspace,用户空间文件系统),是用来生成用户空间的一般进程的框架. ...

  7. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #20 使用fio进行I/O的基准测试

    HACK #20 使用fio进行I/O的基准测试 本节介绍使用fio进行模拟各种情况的I/O基准测试的操作方法.I/O的基准测试中有无数需要考虑的因素.是I/O依次访问还是随机访问?是通过read/w ...

  8. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #17 如何使用ext4

    HACK #17 如何使用ext4 本节介绍ext4的编写和挂载方法.开发版ext4的使用方法.ext4是ext3的后续文件系统,从Linux 2.6.19开始使用.现在主要的发布版中多数都是采用ex ...

  9. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #16 OOM Killer的运行与结构

    HACK #16 OOM Killer的运行与结构(1) 本节介绍OOM Killer的运行与结构. Linux中的Out Of Memory(OOM) Killer功能作为确保内存的最终手段,可以在 ...

随机推荐

  1. Intel Caffe 与原生Caffe

    1.  首先安装好docker,拉取intel caffe image: $ docker pull bvlc/caffe:intel 试着运行: $ docker run -it bvlc/caff ...

  2. ubuntu下修改matlab R2016b的快捷键为windows下相同

    选为: windows默认类.

  3. C#属性升级版--自动属性-chapter 3 P34-36

    使用C#属性,能够通过将数据与它的设置和检索方法分离的方式公开类中的一段数据.   例如:   namespace LanguageFeatures { public class Product { ...

  4. MyBatis Generator配置文件context元素的defaultModelType属性

    MyBatis Generator配置文件context元素的defaultModelType属性 MyBatis Generator配置文件context元素有一个defaultModelType属 ...

  5. git 如何关联多个库

    git 如何关联多个库 在本地同一个库进行操作多个库,可以把代码推送到不同的库中,可以拉取某个库指定的代码,这样更加灵活使用 git remote 查看远程主机名 git remote -v // λ ...

  6. [转载]java正则表达式

    转载自:http://butter.iteye.com/blog/1189600 1.正则表达式的知识要点1.正则表达式是什么?正则表达式是一种可以用于模式匹配和替换的强有力的工具.2.正则表达式的优 ...

  7. 用django发送异步邮件

    太阳底下没有新鲜事,github是一个神奇的地方,你有什么想法,需求,点子.其实别人早就想到,而且也已经做到. 所以不要高估自己,有什么想法还是GITHUB一下,免得成了井底之娃. 这几天一直在研究p ...

  8. Swift 图片浏览器

    class ViewController: UIViewController, UIScrollViewDelegate{ var scrollView: UIScrollView? var imag ...

  9. iOS-----多线程之NSThread

    多线程 iOS平台提供了非常优秀的多线程支持,程序可以通过非常简单的方式来启动多线程,iOS平台不仅提供了NSThread类来创建多线程,还提供了GCD方式来简化多线程编程,提供了NSOperatio ...

  10. Redis学习笔记-数据操作篇(Centos7)

    一.基本操作 1.插入数据 127.0.0.1:6379> set name cos1eqlg0 OK 这样就在redis中设置了一个key-value键值对 2.查询数据 127.0.0.1: ...