经过 ulimit 改善系统性能

概述

系统性能一直是一个受关注的话题,如何经过最简单的设置来实现最有效的性能调优, 如何在有限资源的条件下保证程序的运做,ulimit 是咱们在处理这些问题时,常用的一种简单手段。ulimit 是一种 linux 系统的内键功能,它具备一套参数集,用于为由它生成的 shell 进程及其子进程的资源使用设置限制。本文将在后面的章节中详细说明 ulimit 的功能,使用以及它的影响,并以具体的例子来详细地阐述它在限制资源使用方面的影响。 linux

回页首 shell

ulimit 的功能和用法

ulimit 功能简述

假设有这样一种状况,当一台 Linux 主机上同时登录了 10 我的,在系统资源无限制的状况下,这 10 个用户同时打开了 500 个文档,而假设每一个文档的大小有 10M,这时系统的内存资源就会受到巨大的挑战。 性能优化

而 实际应用的环境要比这种假设复杂的多,例如在一个嵌入式开发环境中,各方面的资源都是很是紧缺的,对于开启文件描述符的数量,分配堆栈的大小,CPU 时间,虚拟内存大小,等等,都有很是严格的要求。资源的合理限制和分配,不只仅是保证系统可用性的必要条件,也与系统上软件运行的性能有着密不可分的联 系。这时,ulimit 能够起到很大的做用,它是一种简单而且有效的实现资源限制的方式。 bash

ulimit 用于限制 shell 启动进程所占用的资源,支持如下各类类型的限制:所建立的内核文件的大小、进程数据块的大小、Shell 进程建立文件的大小、内存锁住的大小、常驻内存集的大小、打开文件描述符的数量、分配堆栈的最大大小、CPU 时间、单个用户的最大线程数、Shell 进程所能使用的最大虚拟内存。同时,它支持硬资源和软资源的限制。 多线程

做为临时限制,ulimit 能够做用于经过使用其命令登陆的 shell 会话,在会话终止时便结束限制,并不影响于其余 shell 会话。而对于长期的固定限制,ulimit 命令语句又能够被添加到由登陆 shell 读取的文件中,做用于特定的 shell 用户。 app

图 1. ulimit 的使用
ulimit 的使用

在下面的章节中,将详细介绍如何使用 ulimit 作相应的资源限制。 dom

如何使用 ulimit

ulimit 经过一些参数选项来管理不一样种类的系统资源。在本节,咱们将讲解这些参数的使用。 socket

ulimit 命令的格式为:ulimit [options] [limit] 性能

具体的 options 含义以及简单示例能够参考如下表格。 优化

表 1. ulimit 参数说明
选项 [options] 含义 例子
-H 设置硬资源限制,一旦设置不能增长。 ulimit – Hs 64;限制硬资源,线程栈大小为 64K。
-S 设置软资源限制,设置后能够增长,可是不能超过硬资源设置。 ulimit – Sn 32;限制软资源,32 个文件描述符。
-a 显示当前全部的 limit 信息。 ulimit – a;显示当前全部的 limit 信息。
-c 最大的 core 文件的大小, 以 blocks 为单位。 ulimit – c unlimited; 对生成的 core 文件的大小不进行限制。
-d 进程最大的数据段的大小,以 Kbytes 为单位。 ulimit -d unlimited;对进程的数据段大小不进行限制。
-f 进程能够建立文件的最大值,以 blocks 为单位。 ulimit – f 2048;限制进程能够建立的最大文件大小为 2048 blocks。
-l 最大可加锁内存大小,以 Kbytes 为单位。 ulimit – l 32;限制最大可加锁内存大小为 32 Kbytes。
-m 最大内存大小,以 Kbytes 为单位。 ulimit – m unlimited;对最大内存不进行限制。
-n 能够打开最大文件描述符的数量。 ulimit – n 128;限制最大可使用 128 个文件描述符。
-p 管道缓冲区的大小,以 Kbytes 为单位。 ulimit – p 512;限制管道缓冲区的大小为 512 Kbytes。
-s 线程栈大小,以 Kbytes 为单位。 ulimit – s 512;限制线程栈的大小为 512 Kbytes。
-t 最大的 CPU 占用时间,以秒为单位。 ulimit – t unlimited;对最大的 CPU 占用时间不进行限制。
-u 用户最大可用的进程数。 ulimit – u 64;限制用户最多可使用 64 个进程。
-v 进程最大可用的虚拟内存,以 Kbytes 为单位。 ulimit – v 200000;限制最大可用的虚拟内存为 200000 Kbytes。

咱们能够经过如下几种方式来使用 ulimit:

  • 在用户的启动脚本中

    若是用户使用的是 bash,就能够在用户的目录下的 .bashrc 文件中,加入 ulimit – u 64,来限制用户最多可使用 64 个进程。此外,能够在与 .bashrc 功能至关的启动脚本中加入 ulimt。

  • 在应用程序的启动脚本中

    若是用户要对某个应用程序 myapp 进行限制,能够写一个简单的脚本 startmyapp。

    ulimit – s 512 
     myapp

    之后只要经过脚本 startmyapp 来启动应用程序,就能够限制应用程序 myapp 的线程栈大小为 512K。

  • 直接在控制台输入
    user@tc511-ui:~>ulimit – p 256

    限制管道的缓冲区为 256K。

回页首

用户进程的有效范围

ulimit 做为对资源使用限制的一种工做,是有其做用范围的。那么,它限制的对象是单个用户,单个进程,仍是整个系统呢?事实上,ulimit 限制的是当前 shell 进程以及其派生的子进程。举例来讲,若是用户同时运行了两个 shell 终端进程,只在其中一个环境中执行了 ulimit – s 100,则该 shell 进程里建立文件的大小收到相应的限制,而同时另外一个 shell 终端包括其上运行的子程序都不会受其影响:

Shell 进程 1
ulimit – s 100 
 cat testFile > newFile 
 File size limit exceeded
Shell 进程 2
cat testFile > newFile 
 ls – s newFile 
 323669 newFile

那么,是否有针对某个具体用户的资源加以限制的方法呢?答案是有的,方法是经过修改系统的 /etc/security/limits 配置文件。该文件不只能限制指定用户的资源使用,还能限制指定组的资源使用。该文件的每一行都是对限定的一个描述,格式以下:

<domain> <type> <item> <value>

domain 表示用户或者组的名字,还可使用 * 做为通配符。Type 能够有两个值,soft 和 hard。Item 则表示须要限定的资源,能够有不少候选值,如 stack,cpu,nofile 等等,分别表示最大的堆栈大小,占用的 cpu 时间,以及打开的文件数。经过添加对应的一行描述,则能够产生相应的限制。例如:

* hard noflle 100

该行配置语句限定了任意用户所能建立的最大文件数是 100。

现 在已经能够对进程和用户分别作资源限制了,看似已经足够了,其实否则。不少应用须要对整个系统的资源使用作一个总的限制,这时候咱们须要修改 /proc 下的配置文件。/proc 目录下包含了不少系统当前状态的参数,例如 /proc/sys/kernel/pid_max,/proc/sys/net/ipv4/ip_local_port_range 等等,从文件的名字大体能够猜出所限制的资源种类。因为该目录下涉及的文件众多,在此不一一介绍。有兴趣的读者可打开其中的相关文件查阅说明。

回页首

ulimit 管理系统资源的例子

ulimit 提供了在 shell 进程中限制系统资源的功能。本章列举了一些使用 ulimit 对用户进程进行限制的例子,详述了这些限制行为以及对应的影响,以此来讲明 ulimit 如何对系统资源进行限制,从而达到调节系统性能的功能。

使用 ulimit 限制 shell 的内存使用

在这一小节里向读者展现如何使用 – d,– m 和 – v 选项来对 shell 所使用的内存进行限制。

首先咱们来看一下不设置 ulimit 限制时调用 ls 命令的状况:

图 2. 未设置 ulimit 时 ls 命令使用状况
未设置 ulimit 时 ls 命令使用状况

你们能够看到此时的 ls 命令运行正常。下面设置 ulimit:

>ulimit -d 1000 -m 1000 -v 1000

这里再温习一下前面章节里介绍过的这三个选项的含义:

-d:设置数据段的最大值。单位:KB。

-m:设置可使用的常驻内存的最大值。单位:KB。

-v:设置虚拟内存的最大值。单位:KB。

经过上面的 ulimit 设置咱们已经把当前 shell 所能使用的最大内存限制在 1000KB 如下。接下来咱们看看这时运行 ls 命令会获得什么样的结果:

haohe@sles10-hehao:~/code/ulimit> ls test -l 
 /bin/ls: error while loading shared libraries: libc.so.6: failed to map segment 
 from shared object: Cannot allocate memory

从上面的结果能够看到,此时 ls 运行失败。根据系统给出的错误信息咱们能够看出是因为调用 libc 库时内存分配失败而致使的 ls 出错。那么咱们来看一下这个 libc 库文件到底有多大:

图 3. 查看 libc 文件大小
查看 libc 文件大小

从上面的信息能够看出,这个 libc 库文件的大小是 1.5MB。而咱们用 ulimit 所设置的内存使用上限是 1000KB,小于 1.5MB,这也就充分证实了 ulimit 所起到的限制 shell 内存使用的功能。

使用 ulimit 限制 shell 建立的文件的大小

接下来向读者展现如何使用 -f 选项来对 shell 所能建立的文件大小进行限制。

首先咱们来看一下,没有设置 ulimit -f 时的状况:

图 4. 查看文件
查看文件

现有一个文件 testFile 大小为 323669 bytes,如今使用 cat 命令来建立一个 testFile 的 copy:

图 5. 未设置 ulimit 时建立复本
未设置 ulimit 时建立复本

从上面的输出能够看出,咱们成功的建立了 testFile 的拷贝 newFile。

下面咱们设置 ulimt – f 100:

> ulimit -f 100

-f 选项的含义是:用来设置 shell 能够建立的文件的最大值。单位是 blocks。

如今咱们再来执行一次相同的拷贝命令看看会是什么结果:

图 6. 设置 ulimit 时建立复本
设置 ulimit 时建立复本

这 次建立 testFile 的拷贝失败了,系统给出的出错信息时文件大小超出了限制。在 Linux 系统下一个 block 的默认大小是 512 bytes。因此上面的 ulimit 的含义就是限制 shell 所能建立的文件最大值为 512 x 100 = 51200 bytes,小于 323669 bytes,因此建立文件失败,符合咱们的指望。这个例子说明了如何使用 ulimit 来控制 shell 所能建立的最大文件。

使用 ulimit 限制程序所能建立的 socket 数量

考 虑一个现实中的实际需求。对于一个 C/S 模型中的 server 程序来讲,它会为多个 client 程序请求建立多个 socket 端口给与响应。若是刚好有大量的 client 同时向 server 发出请求,那么此时 server 就会须要建立大量的 socket 链接。但在一个系统当中,每每须要限制单个 server 程序所能使用的最大 socket 数,以供其余的 server 程序所使用。那么咱们如何来作到这一点呢?答案是咱们能够经过 ulimit 来实现!细心的读者可能会发现,经过前面章节的介绍彷佛没有限制 socket 使用的 ulimit 选项。是的,ulimit 并无哪一个选项直接说是用来限制 socket 的数量的。可是,咱们有 -n 这个选项,它是用于限制一个进程所能打开的文件描述符的最大值。在 Linux 下一切资源皆文件,普通文件是文件,磁盘打印机是文件,socket 固然也是文件。在 Linux 下建立一个新的 socket 链接,实际上就是建立一个新的文件描述符。以下图所示(查看某个进程当前打开的文件描述符信息):

图 7. 查看进程打开文件描述符
查看进程打开文件描述符

所以,咱们能够经过使用 ulimit – n 来限制程序所能打开的最大文件描述符数量,从而达到限制 socket 建立的数量。

使用 ulimit 限制 shell 多线程程序堆栈的大小(增长可用线程数量)

在最后一个例子中,向你们介绍如何使用 -s(单位 KB)来对线程的堆栈大小进行限制,从而减小整个多线程程序的内存使用,增长可用线程的数量。这个例子取自于一个真实的案例。咱们所遇到的问题是系统对咱们的多线程程序有以下的限制:

ulimit -v 200000

根据本文前面的介绍,这意味着咱们的程序最多只能使用不到 200MB 的虚拟内存。因为咱们的程序是一个多线程程序,程序在运行时会根据须要建立新的线程,这势必会增长总的内存需求量。一开始咱们对堆栈大小的限制是 1024 (本例子中使用 1232 来讲明):

# ulimit – s 1232

当咱们的程序启动后,经过 pmap 来查看其内存使用状况,能够看到多个占用 1232KB 的数据段,这些就是程序所建立的线程所使用的堆栈:

图 8. 程序线程所使用的堆栈
程序线程所使用的堆栈

每当一个新的线程被建立时都须要新分配一段大小为 1232KB 的内存空间,而咱们总的虚拟内存限制是 200MB,因此若是咱们须要建立更多的线程,那么一个能够改进的方法就是减小每一个线程的固定堆栈大小,这能够经过 ulimit – s 来实现:

# ulimit -s 512

咱们将堆栈大小设置为 512KB,这时再经过 pmap 查看一下咱们的设置是否起做用:

图 9. 设置 ulimit 后堆栈大小
设置 ulimit 后堆栈大小

从上面的信息能够看出,咱们已经成功的将线程的堆栈大小改成 512KB 了,这样在总内存使用限制不变的状况下,咱们能够经过本小节介绍的方法来增长能够建立的线程数,从而达到改善程序的多线程性能。

回页首

总结

综 上所述,linux 系统中的 ulimit 指令,对资源限制和系统性能优化提供了一条便捷的途径。从用户的 shell 启动脚本,应用程序启动脚本,以及直接在控制台,均可以经过该指令限制系统资源的使用,包括所建立的内核文件的大小、进程数据块的大小、Shell 进程建立文件的大小、内存锁住的大小、常驻内存集的大小、打开文件描述符的数量、分配堆栈的最大大小、CPU 时间、单个用户的最大线程数、Shell 进程所能使用的最大虚拟内存,等等方面。本文中的示例很是直观的说明了 ulimit 的使用及其产生的效果,显而易见,ulimit 对咱们在 Linux 平台的应用和开发工做是很是实用的。

回页首

特别感谢

贺皓为本篇文章的撰写提出了不少建议,作了大量实验,提供了文章中的实验数据和结论。

贺皓是 IBM CSTL(中国系统与科技开发中心)的软件工程师。他在复旦大学得到了计算机科学专业的学士与硕士学位。目前他在 IBM SVC Agent 开发小组从事研发工做。

相关文章
相关标签/搜索