ulimit功能简述
假设有这样一种情况,当一台 Linux 主机上同时登陆了 10 个人,在系统资源无限制的情况下,这 10 个用户同时打开了 500 个文档,而假设每个文档的大小有 10M,这时系统的内存资源就会受到巨大的挑战。而实际应用的环境要比这种假设复杂的多,例如在一个嵌入式开发环境中,各方面的资源都是非常紧缺的,对于开启文件描述符的数量,分配堆栈的大小,CPU 时间,虚拟内存大小,等等,都有非常严格的要求。资源的合理限制和分配。
不仅仅是保证系统可用性的必要条件,也与系统上软件运行的性能有着密不可分的联系。这时,ulimit可以起到很大的作用,它是一种简单并且有效的实现资源限制的方式。ulimit用于限制shell启动进程所占用的资源,支持以下各种类型的限制:所创建的内核文件的大小、进程数据块的大小、Shell进程创建文件的大小、内存锁住的大小、常驻内存集的大小、打开文件描述符的数量、分配堆栈的最大大小、CPU 时间、单个用户的最大线程数、Shell 进程所能使用的最大虚拟内存。同时,它支持硬资源和软资源的限制。
作为临时限制,ulimit 可以作用于通过使用其命令登录的 shell 会话,在会话终止时便结束限制,并不影响于其他 shell 会话。而对于长期的固定限制,ulimit 命令语句又可以被添加到由登录 shell 读取的文件中,作用于特定的 shell 用户。
如何使用ulimit
ulimit通过一些参数选项来管理不同种类的系统资源。在本节,我们将讲解这些参数的使用。
ulimit命令的格式为:
$ 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。
参数名 | 对应选项 | 含义 |
---|---|---|
-a | 显示当前系统所有的limit资源信息,ulimit -a | |
core file size | -c | 内核core文件的大小限制,单位为blocks |
file size | -f | 进程可以创建文件的最大值,单位为blocks |
data seg size | -d | 进程最大数据段的大小,单位为KB |
max memory size | -m | 进程可使用内存大小,单位为KB |
open files | -n | 用户可使用的文件描述符的最大数量 |
stack size | -s | 最大堆栈大小,单位为KB |
pipe size | -p | 管道缓冲区大小,单位为KB |
max user processes | -u | 用户最大可用进程数 |
virtual memory | -v | 进程最大可用虚拟内存,单位为KB |
cpu time | -t | 最大CPU占用时间,单位为秒 |
max locked memory | -l | 最大加锁内存大小,单位为KB |
ulimit使用实例
用户进程的有效范围
ulimit 作为对资源使用限制的一种工作,是有其作用范围的。那么,它限制的对象是单个用户,单个进程,还是整个系统呢?事实上,ulimit 限制的是当前 shell 进程以及其派生的子进程。举例来说,如果用户同时运行了两个 shell 终端进程,只在其中一个环境中执行了 ulimit -s 100,则该 shell 进程里创建文件的大小收到相应的限制,而同时另一个 shell 终端包括其上运行的子程序都不会受其影响。
$ 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
$ 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
那么,是否有针对某个具体用户的资源加以限制的方法呢?答案是有的,临时生效(不限制打开文件大小限制):
$ ulimit -f unlimited
或通过修改系统的/etc/security/limits.conf配置文件。该文件不仅能限制指定用户的资源使用,还能限制指定组的资源使用。该文件的每一行都是对限定的一个描述,格式如下:
#格式:<domain> <type> <item> <value>
#domain:表示用户或组的名字,还可以使用*作为通配符,表示所有用户。
#type:表示限制类型,soft表示软资源限制,设置后可以超过软资源限制,但是不能超过硬资源限制;hard表示硬资源限制,一旦设置不能超过限制。
#item:表示需要限定的资源名称,常用的有nofile(用户可用文件描述符最大数量)、CPU(占用CPU时间)、statck(最大堆栈大小)、noproc(用户最大可用进程数)等。
#value:表示限制资源的具体值。
#举例:
* soft noproc 65535
* hard noproc 65535
* soft nofile 65535
* hard nofile 65535
通过添加对应的一行描述,则可以产生相应的限制。例如:
* 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命令的情况:
$ ll shell1 -l
-rw-r--r--. 1 root root 227690 4月 23 09:16 shell1
大家可以看到此时的 ls 命令运行正常。下面设置 ulimit:
$ ulimit -d 1000 -m 1000 -v 1000
这里再温习一下前面章节里介绍过的这三个选项的含义:
-d:设置数据段的最大值。单位:KB。
-m:设置可以使用的常驻内存的最大值。单位:KB。
-v:设置虚拟内存的最大值。单位:KB。
通过上面的 ulimit 设置我们已经把当前 shell 所能使用的最大内存限制在 1000KB 以下。接下来我们看看这时运行 ls 命令会得到什么样的结果:
$ 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这个选项,它是用于限制一个进程所能打开的文件描述符的最大值。如下所示(查看某个进程当前打开的文件描述符信息):
$ 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命令可以帮你查看:
$ 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
在这个文件后加上:
* soft nofile 1024000
* hard nofile 1024000
root soft nofile 1024000
root hard nofile 1024000
ulimit使用注意事项
修改完配置文件后要退出SSH终端,重新登录会话才能生效。
除了limits.conf
还有一个/etc/security/limits.d
目录,系统会默认优先读取并使用这个目录下的配置文件,再读取limits.conf
的配置,如果有重复的配置,以/etc/security/limits.d
目录下的配置文件为准。
正在运行的应用不会读取新添加的ulimit资源限制,在应用重启后资源限制才生效。
ulimit修改可使用文件描述符最大数量
不能超过1048576(1024*1024),如果要提高整个数量需要修改内核参数/etc/sys/fs/nr_open
。
/etc/security/limits.conf
件中的配置对system services不生效,只对通过PAM登录的用户生效,也就是说使用systemd管理的
服务进程是不受该配置文件影响。
systemd管理服务的修改方法
假设nginx服务由systemd进行管理,如果需要修改其资源限制,有以下两种方法:
-
使用
ps aux|grep nginx
获取nginx进程的id# ps aux |grep nginx root 2441 0.0 0.0 50176 5952 ? Ss 18:30 0:00 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
-
执行
cat /proc/nginx_id/limits
查看当前生效的资源限制# cat /proc/2441/limits Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 261206 261206 processes Max open files 65536 65536 files Max locked memory 65536 65536 bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 261206 261206 signals Max msgqueue size 819200 819200 bytes Max nice priority 0 0 Max realtime priority 0 0 Max realtime timeout unlimited unlimited us
全局修改
对于systemd管理的服务,可以通过修改全局配置/etc/systemd/system.conf或/etc/systemd/user.conf(对于所有的服务生效)
具体修改哪个全局配置文件取决systemd服务本身运行在系统实例还是用户实例(通常情况下运行在系统实例)。
# ps aux |grep systemd
root 1 0.5 0.0 158336 10176 ? Ss Mar30 7:54 /lib/systemd/systemd --system --deserialize 20
如上所示代表运行在系统实例状态,如果要以用户实例运行,把–system 换成–user即可。不过通常情况下都是系统实例状态。
#在配置文件中添加如下内容
DefaultLimitNOFILE=655360
DefaultLimitNPROC=655360
修改配置后生效的方法有两种:
- 重启系统
- 执行
systemctl daemon-reexec
使用daemon-reload无效原因
使用 systemctl daemon-reload
不会刷新全局配置/etc/systemd/system.conf,引用serverfault.com对systemd-system.conf的说明:
不会刷新, daemon-reload 将重载所有单元文件,而不是 systemd 本身的配置。然而, systemctl daemon-reexec
将重新执行systemd,并使它在流程中消化其新配置。
来自 systemctl 手册页:
daemon-reexec
Reexecute the systemd manager. This will serialize the manager
state, reexecute the process and deserialize the state again. This
command is of little use except for debugging and package upgrades.
Sometimes, it might be helpful as a heavy-weight daemon-reload.
While the daemon is being reexecuted, all sockets systemd listening
on behalf of user configuration will stay accessible.
当手册页说daemon-reexec用于包升级时,它在很大程度上意味着这个命令将执行任何新的二进制文件,并重新处理它的配置。但是,我们用来升级systemd的RPM已经包含了这样做的脚本,所以在正常升级的情况下通常不需要它。
Or you can reboot. Either will do.
换句话说,daemon-reexec
将重新执行systemd管理程序,重新读取系统配置文件,而daemon-reload
只会去读service部分的配置,不包含全局配置/systemd/system.conf
,相当于重量级的daemon-reload
.
单个服务修改
如果只修改单个服务的资源限制,以nginx为例,需编辑/usr/lib/systemd/system/nginx.service
:
找到[Service段],添加如下配置
[Service]
LimitNOFILE=655360
LimitNPROC=655360
运行如下命令,配置立即生效
# systemctl daemon-reload
# systemctl restart nginx.service