概述
系统性能一直是一个受关注的话题,怎样通过最简单的设置来实现最有效的性能调优,怎样在有限资源的条件下保证程序的运作,ulimit 是我们在处理这些问题时,常常使用的一种简单手段。ulimit 是一种 linux 系统的内键功能,它具有一套參数集,用于为由它生成的 shell 进程及其子进程的资源使用设置限制。
本文将在后面的章节中具体说明 ulimit 的功能。使用以及它的影响,并以具体的样例来具体地阐述它在限制资源使用方面的影响。
ulimit 的功能和使用方法
ulimit 功能简述
如果有这样一种情况,当一台 Linux 主机上同一时候登陆了 10 个人,在系统资源无限制的情况下,这 10 个用户同一时候打开了 500 个文档,而如果每一个文档的大小有 10M。这时系统的内存资源就会受到巨大的挑战。
而实际应用的环境要比这样的如果复杂的多,比如在一个嵌入式开发环境中,各方面的资源都是很紧缺的。对于开启文件描写叙述符的数量,分配堆栈的大小,CPU 时间,虚拟内存大小,等等。都有很严格的要求。资源的合理限制和分配,不不过保证系统可用性的必要条件,也与系统上软件执行的性能有着密不可分的联系。这时。ulimit 能够起到很大的作用,它是一种简单而且有效的实现资源限制的方式。
ulimit 用于限制 shell 启动进程所占用的资源,支持下面各种类型的限制:所创建的内核文件的大小、进程数据块的大小、Shell 进程创建文件的大小、内存锁住的大小、常驻内存集的大小、打开文件描写叙述符的数量、分配堆栈的最大大小、CPU 时间、单个用户的最大线程数、Shell 进程所能使用的最大虚拟内存。
同一时候,它支持硬资源和软资源的限制。
作为暂时限制。ulimit 能够作用于通过使用其命令登录的 shell 会话。在会话终止时便结束限制,并不影响于其它 shell 会话。而对于长期的固定限制,ulimit 命令语句又能够被加入到由登录 shell 读取的文件里,作用于特定的 shell 用户。
图 1. ulimit 的使用
在以下的章节中,将具体介绍怎样使用 ulimit 做对应的资源限制。
怎样使用 ulimit
ulimit 通过一些參数选项来管理不同种类的系统资源。在本节,我们将解说这些參数的使用。
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 命令使用情况
大家能够看到此时的 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 库文件的大小是 1.5MB。而我们用 ulimit 所设置的内存使用上限是 1000KB,小于 1.5MB,这也就充分证明了 ulimit 所起到的限制 shell 内存使用的功能。
使用 ulimit 限制 shell 创建的文件的大小
接下来向读者展示怎样使用 -f 选项来对 shell 所能创建的文件大小进行限制。
首先我们来看一下,没有设置 ulimit -f 时的情况:
图 4. 查看文件
现有一个文件 testFile 大小为 323669 bytes,如今使用 cat 命令来创建一个 testFile 的 copy:
图 5. 未设置 ulimit 时创建复本
从上面的输出能够看出,我们成功的创建了 testFile 的拷贝 newFile。
以下我们设置 ulimt – f 100:
> ulimit -f 100
-f 选项的含义是:用来设置 shell 能够创建的文件的最大值。单位是 blocks。
如今我们再来运行一次同样的拷贝命令看看会是什么结果:
图 6. 设置 ulimit 时创建复本
这次创建 testFile 的拷贝失败了,系统给出的出错信息时文件大小超出了限制。在 Linux 系统下一个 block 的默认大小是 512 bytes。所以上面的 ulimit 的含义就是限制 shell 所能创建的文件最大值为 512 x 100 = 51200 bytes。小于 323669 bytes。所以创建文件失败。符合我们的期望。这个样例说明了怎样使用 ulimit 来控制 shell 所能创建的最大文件。
使用 ulimit 限制程序所能创建的 socket 数量
考虑一个现实中的实际需求。
对于一个 C/S 模型中的 server 程序来说,它会为多个 client 程序请求创建多个 socket port给与响应。
假设恰好有大量的 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 后堆栈大小
从上面的信息能够看出,我们已经成功的将线程的堆栈大小改为 512KB 了。这样在总内存使用限制不变的情况下,我们能够通过本小节介绍的方法来添加能够创建的线程数。从而达到改善程序的多线程性能。
总结
综上所述,linux 系统中的 ulimit 指令,对资源限制和系统性能优化提供了一条便捷的途径。
从用户的 shell 启动脚本。应用程序启动脚本。以及直接在控制台,都能够通过该指令限制系统资源的使用,包含所创建的内核文件的大小、进程数据块的大小、Shell 进程创建文件的大小、内存锁住的大小、常驻内存集的大小、打开文件描写叙述符的数量、分配堆栈的最大大小、CPU 时间、单个用户的最大线程数、Shell 进程所能使用的最大虚拟内存。等等方面。本文中的演示样例很直观的说明了 ulimit 的使用及其产生的效果。显而易见。ulimit 对我们在 Linux 平台的应用和开发工作是很有用的。