系统性能一直是一个受关注的话题,如何通过最简单的设置来实现最有效的性能调优,如何在有限资源的条件下保证程序的运作,ulimit
是我们在处理这些问题时,经常使用的一种简单手段。
ulimit 是一种 linux 系统的内建功能,它具有一套参数集,用于为由它生成的 shell
进程及其子进程的资源使用设置限制。本文将在后面的章节中详细说明 ulimit
的功能,使用以及它的影响,
并以具体的例子来详细地阐述它在限制资源使用方面的影响。
ulimit
功能简述
假设有这样一种情况,当一台 Linux 主机上同时登陆了 10 个人,在系统资源无限制的情况下,这 10
个用户同时打开了 500 个文档,而假设每个文档的大小有
10M,这时系统的内存资源就会受到巨大的挑战。
而实际应用的环境要比这种假设复杂的多,例如在一个嵌入式开发环境中,各方面的资源都是非常紧缺的,对于开启文件描述符的数量,分配堆栈的大小,CPU
时间,虚拟内存大小,等等,
都有非常严格的要求。资源的合理限制和分配,不仅仅是保证系统可用性的必要条件,也与系统上软件运行的性能有着密不可分的联系。这时,ulimit
可以起到很大的作用,它是一种简单并且有效的实现资源限制的方式。
ulimit 用于限制 shell
启动进程所占用的资源,支持以下各种类型的限制:所创建的内核文件的大小、进程数据块的大小、Shell
进程创建文件的大小、内存锁住的大小、常驻内存集的大小、
打开文件描述符的数量、分配堆栈的最大大小、CPU 时间、单个用户的最大线程数、Shell
进程所能使用的最大虚拟内存。同时,它支持硬资源和软资源的限制。
作为临时限制,ulimit 可以作用于通过使用其命令登录的 shell
会话,在会话终止时便结束限制,并不影响于其他 shell 会话。而对于长期的固定限制,
ulimit 命令语句又可以被添加到由登录 shell
读取的文件中,作用于特定的 shell 用户。
linux下默认是不产生core文件的,要用ulimit -c unlimited放开
ulimit
通过一些参数选项来管理不同种类的系统资源。
ulimit 命令的格式为:ulimit [options] [limit]
表 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 终端包括其上运行的子程序都不会受其影响:
那么,是否有针对某个具体用户的资源加以限制的方法呢?答案是有的,方法是通过修改系统的 /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 所使用的内存进行限制。
限制前
[root@localhost lianxi]# echo "testwwww"
>test
[root@localhost lianxi]# ll test
-rw-r--r--. 1 root root 9 May
10 10:21 test
限制后
[root@localhost lianxi]# ulimit -d 1000 -m 1000 -v
1000
[root@localhost lianxi]# ll test
Segmentation
fault
以上是在centos6.5-64位上运行的,与其它版本的报错信息可能不一致
通过上面的 ulimit 设置我们已经把当前 shell 所能使用的最大内存限制在 1000KB 以下
从上面的结果可以看到,此时 ls
运行失败。根据系统给出的错误信息我们可以看出是由于调用 libc 库时内存分配失败而导致的 ls 出错。那么我们来看一下这个 libc
库文件到底有多大:
从上面的信息可以看出,这个 libc 库文件的大小是 1.5MB。而我们用 ulimit 所设置的内存使用上限是 1000KB,小于
1.5MB,这也就充分证明了 ulimit 所起到的限制 shell 内存使用的功能。
使用 ulimit 限制 shell 创建的文件的大小
接下来向读者展示如何使用 -f 选项来对 shell
所能创建的文件大小进行限制。
限制前 可以创建任何大小的文件
[root@localhost lianxi]# cp
win2k3_r2_ent_sp1_cd2.iso test1
[root@localhost lianxi]# ll
total
273980
-rw-r--r--. 1 root root 9 May 10 10:21 test
-rw-r--r--. 1
root root 140273664 May 10 10:38 test1
-rw-r--r--. 1 root root 140273664 Jul
31 2006 win2k3_r2_ent_sp1_cd2.iso
限制后
[root@localhost lianxi]# ulimit -f
100000
[root@localhost lianxi]# cp win2k3_r2_ent_sp1_cd2.iso test3
File
size limit exceeded
使用 ulimit 限制程序所能创建的 socket 数量
考虑一个现实中的实际需求。对于一个 C/S 模型中的
server 程序来说,它会为多个 client 程序请求创建多个 socket 端口给与响应。如果恰好有大量的 client 同时向 server
发出请求,
那么此时 server 就会需要创建大量的 socket 连接。但在一个系统当中,往往需要限制单个 server 程序所能使用的最大
socket 数,以供其他的 server 程序所使用。
那么我们如何来做到这一点呢?答案是我们可以通过 ulimit
来实现!细心的读者可能会发现,通过前面章节的介绍似乎没有限制 socket 使用的 ulimit 选项。是的,
ulimit
并没有哪个选项直接说是用来限制 socket 的数量的。但是,我们有 -n 这个选项,它是用于限制一个进程所能打开的文件描述符的最大值。在 Linux
下一切资源皆文件,
普通文件是文件,磁盘打印机是文件,socket 当然也是文件。在 Linux 下创建一个新的 socket
连接,实际上就是创建一个新的文件描述符。
776 ? 00:00:00 rsyslogd
[root@localhost fd]# cd
/proc/776/fd
[root@localhost fd]# ll
total 0
lrwx------. 1 root root 64
May 10 10:53 0 -> socket:[10173]
l-wx------. 1 root root 64 May 10 10:53 1
-> /var/log/secure
l-wx------. 1 root root 64 May 10 10:53 2 ->
/var/log/maillog
lr-x------. 1 root root 64 May 10 10:53 3 ->
/proc/kmsg
l-wx------. 1 root root 64 May 10 10:53 4 ->
/var/log/messages
l-wx------. 1 root root 64 May 10 10:53 5 ->
/var/log/cron
840 ? 00:00:00 sshd
[root@localhost fd]# cd
/proc/840/fd
[root@localhost fd]# ll
total 0
lrwx------. 1 root root 64
May 10 10:53 0 -> /dev/null
lrwx------. 1 root root 64 May 10 10:53 1
-> /dev/null
lrwx------. 1 root root 64 May 10 10:53 2 ->
/dev/null
lrwx------. 1 root root 64 May 10 10:53 3 ->
socket:[10348]
lrwx------. 1 root root 64 May 10 10:53 4 ->
socket:[10352]
因此,我们可以通过使用 ulimit -n 来限制程序所能打开的最大文件描述符数量,从而达到限制 socket 创建的数量。
使用 ulimit 限制 shell
多线程程序堆栈的大小(增加可用线程数量)
在最后一个例子中,向大家介绍如何使用 -s(单位
KB)来对线程的堆栈大小进行限制,从而减少整个多线程程序的内存使用,增加可用线程的数量。这个例子取自于一个真实的案例。
我们所遇到的问题是系统对我们的多线程程序有如下的限制:
#
ulimit -v 200000
根据本文前面的介绍,这意味着我们的程序最多只能使用不到 200MB
的虚拟内存。由于我们的程序是一个多线程程序,程序在运行时会根据需要创建新的线程,
这势必会增加总的内存需求量。一开始我们对堆栈大小的限制是 1024
(本例子中使用 1232 来说明):
# ulimit -s 1232
当我们的程序启动后,通过 pmap 来查看其内存使用情况,可以看到多个占用
1232KB 的数据段,这些就是程序所创建的线程所使用的堆栈:
# pmap -x pid
每当一个新的线程被创建时都需要新分配一段大小为
1232KB 的内存空间,而我们总的虚拟内存限制是
200MB,所以如果我们需要创建更多的线程,那么一个可以改进的方法就是减少每个线程的固定堆栈大小,
这可以通过 ulimit -s 来实现:
#
ulimit -s 512
我们将堆栈大小设置为 512KB,这时再通过 pmap 查看一下我们的设置是否起作用:
# pmap -x
pid
从上面的信息可以看出,我们已经成功的将线程的堆栈大小改为 512KB
了,这样在总内存使用限制不变的情况下,我们可以通过本小节介绍的方法来增加可以创建的线程数,从而达到改善程序的多线程性能。
综上所述,linux 系统中的 ulimit 指令,对资源限制和系统性能优化提供了一条便捷的途径。从用户的 shell
启动脚本,应用程序启动脚本,以及直接在控制台,
都可以通过该指令限制系统资源的使用,包括所创建的内核文件的大小、进程数据块的大小、Shell
进程创建文件的大小、内存锁住的大小、常驻内存集的大小、打开文件描述符的数量、
分配堆栈的最大大小、CPU 时间、单个用户的最大线程数、Shell
进程所能使用的最大虚拟内存,等等方面。本文中的示例非常直观的说明了 ulimit 的使用及其产生的效果,
显而易见,ulimit 对我们在 Linux
平台的应用和开发工作是非常实用的。
840 ? 00:00:00 sshd
[root@localhost fd]# pmap -x 840
[root@localhost ~]# ulimit -a 显示所有已设值