概述
系统性能一直是一个受关注的话题,如何通过最简单的设置来实现最有效的性能调优,如何在有限资源的条件下保证程序的运作,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 用户。
如何使用ulimit
ulimit通过一些参数选项来管理不同种类的系统资源。在本节,我们将讲解这些参数的使用。
ulimit命令的格式为:
1 |
$ ulimit [options] [limit] |
[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;限制最大可加锁内存大小为32Kbytes。
-m: 最大内存大小,以Kbytes为单位。如,ulimit -m unlimited;对最大内存不进行限制。
-n: 可以打开最大文件描述符的数量。如,ulimit -n 128;限制最大可以使用128个文件描述符。
-p: 管道缓冲区的大小,以Kbytes为单位。如,ulimit -p 512;限制管道缓冲区的大小为512Kbytes。
-s: 线程栈大小,以Kbytes为单位。如,如,ulimit -s 512;限制线程栈的大小为512Kbytes。
-t: 最大的CPU占用时间,以秒为单位。 如,ulimit -t unlimited;对最大的CPU占用时间不进行限制。
-u: 用户最大可用的进程数。如,ulimit -u 64;限制用户最多可以使用64个进程。
-v: 进程最大可用的虚拟内存,如,以Kbytes为单位。如,ulimit -v 200000;限制最大可用的虚拟内存为200000Kbytes。
ulimit使用实例
一、用户进程的有效范围
ulimit 作为对资源使用限制的一种工作,是有其作用范围的。那么,它限制的对象是单个用户,单个进程,还是整个系统呢?事实上,ulimit 限制的是当前 shell 进程以及其派生的子进程。举例来说,如果用户同时运行了两个 shell 终端进程,只在其中一个环境中执行了 ulimit -s 100,则该 shell 进程里创建文件的大小收到相应的限制,而同时另一个 shell 终端包括其上运行的子程序都不会受其影响。
Shell 1
1 2 3 4 5 6 7 |
$ ll -h newfile -rw-r--r--. 1 root root 223K 4月 23 09:16 newfile $ ulimit -f 100 $ cat newfile > shell1 File size limit exceeded (core dumped) $ ll -h shell1 -rw-r--r--. 1 root root 100K 4月 23 09:20 shell1 |
Shell 2
1 2 3 4 5 |
$ cat newfile > shell2 $ ll -d shell2 -rw-r--r--. 1 root root 227690 4月 23 09:23 shell2 $ ll -h shell2 -rw-r--r--. 1 root root 223K 4月 23 09:23 shell2 |
那么,是否有针对某个具体用户的资源加以限制的方法呢?答案是有的,临时生效(不限制打开文件大小限制):
1 |
$ ulimit -f unlimited |
或通过修改系统的/etc/security/limits.conf配置文件。该文件不仅能限制指定用户的资源使用,还能限制指定组的资源使用。该文件的每一行都是对限定的一个描述,格式如下:
1 |
<domain> <type> <item> <value> |
domain 表示用户或者组的名字,还可以使用 * 作为通配符。Type 可以有两个值,soft 和 hard。Item 则表示需要限定的资源,可以有很多候选值,如 stack,cpu,nofile 等等,分别表示最大的堆栈大小,占用的 cpu 时间,以及打开的文件数。通过添加对应的一行描述,则可以产生相应的限制。例如:
1 |
* hard noflle 100 |
该行配置语句限定了任意用户所能创建的最大文件数是 100。现在已经可以对进程和用户分别做资源限制了,看似已经足够了,其实不然。很多应用需要对整个系统的资源使用做一个总的限制,这时候我们需要修改/proc下的配置文件。/proc目录下包含了很多系统当前状态的参数,例如/proc/sys/kernel/pid_max,/proc/sys/net/ipv4/ip_local_port_range等等,从文件的名字大致可以猜出所限制的资源种类。由于该目录下涉及的文件众多,在此不一一介绍。有兴趣的读者可打开其中的相关文件查阅说明。
二、使用ulimit限制shell的内存使用
在这一小节里向读者展示如何使用-d,-m 和-v 选项来对shell所使用的内存进行限制。首先我们来看一下不设置ulimit限制时调用ls命令的情况:
1 2 |
$ ll shell1 -l -rw-r--r--. 1 root root 227690 4月 23 09:16 shell1 |
大家可以看到此时的 ls 命令运行正常。下面设置 ulimit:
1 |
$ ulimit -d 1000 -m 1000 -v 1000 |
这里再温习一下前面章节里介绍过的这三个选项的含义:
-d:设置数据段的最大值。单位:KB。
-m:设置可以使用的常驻内存的最大值。单位:KB。
-v:设置虚拟内存的最大值。单位:KB。
通过上面的 ulimit 设置我们已经把当前 shell 所能使用的最大内存限制在 1000KB 以下。接下来我们看看这时运行 ls 命令会得到什么样的结果:
1 2 |
$ ll shell1 -l Segmentation fault (core dumped) |
三、使用ulimit限制程序所能创建的socket数量
考虑一个现实中的实际需求。对于一个 C/S 模型中的 server 程序来说,它会为多个 client 程序请求创建多个 socket 端口给与响应。如果恰好有大量的 client 同时向 server 发出请求,那么此时 server 就会需要创建大量的 socket连接。但是在 Linux 下一切资源皆文件,普通文件是文件,磁盘打印机是文件,socket 当然也是文件。在 Linux 下创建一个新的socket 连接,实际上就是创建一个新的文件描述符。而Linux对单进程能打开的文件描述符是有限制的,默认单进程能打开的最大文件数量为1024,。ulimit 并没有哪个选项直接说是用来限制socket的数量的。但是,我们有-n这个选项,它是用于限制一个进程所能打开的文件描述符的最大值。如下所示(查看某个进程当前打开的文件描述符信息):
1 2 3 4 5 6 7 8 9 10 11 |
$ ll /proc/36766/fd 总用量 0 lr-x------. 1 root root 64 4月 23 09:31 0 -> /dev/null l-wx------. 1 root root 64 4月 23 09:31 1 -> /mydata/localhost.localdomain.err lrwx------. 1 root root 64 4月 23 09:31 10 -> /mydata/ib_logfile1 lrwx------. 1 root root 64 4月 23 09:31 11 -> socket:[115703] lrwx------. 1 root root 64 4月 23 09:31 12 -> /tmp/ibLxLFBt (deleted) l-wx------. 1 root root 64 4月 23 09:31 13 -> /mydata/mysql-bin.000001 lrwx------. 1 root root 64 4月 23 09:31 14 -> socket:[115704] lrwx------. 1 root root 64 4月 23 09:31 15 -> /mydata/mysql/host.MYI ....................... |
因此,我们可以通过使用ulimit -n来限制但进程所能打开的最大文件描述符数量,默认单进程打开的文件描述符为1024,就是代表单个进程只能同时最多只能维持1024甚至更少(因为有其它文件的句柄被打开)。如果开启4个进程维持用户链接,那么整个应用能够同时维持的连接数不会超过4*1024个,也就是说最多只能支持4×1024个用户在线。可以增大这个设置以便服务能够维持更多的TCP连接,从而达到限制socket创建的数量。
如果单个进程打开的文件句柄数量超过了系统定义的值,就会提到“too many files open”的错误提示。如何知道当前进程打开了多少个文件句柄呢?通过lsof命令可以帮你查看:
1 2 3 |
$ lsof -n |awk '{print $2}'|sort|uniq -c |sort -nr| head -n 2 126 7015 93 1831 |
上面说明了,7015进程打开了126个文件描述符,你可以通过ps命令看看7015这个进程是什么服务(这里都是以我的举例说明的,你在实验时要根据自己的进程进行查看,相信你有这个意识)。
修改单进程所能打开的最大文件数
1)ulimit -n 102400
这只是在当前终端有效,退出之后,open files又变为默认值。
2)将ulimit -n 102400写到/etc/profile中,这样每次登录终端时,都会自动执行/etc/profile。
3)令修改open files的数值永久生效,则必须修改配置文件:/etc/security/limits.conf在这个文件后加上:
1 2 3 4 |
* soft nofile 1024000 * hard nofile 1024000 root soft nofile 1024000 root hard nofile 1024000 |