Linux下的resource limits(ulimit)
在Linux下,可以对进程使用的资源做一些限制,比如,可以使用的内存、可以使用的线程、最大能打开的文件数等等,这些也就是我们常说的rlimit
,在bash里,可以非常方便的用ulimit
这个内置的命令查看和修改这些限制,那么到底这些限制有那些,是怎么来的呢?
首先,在C编程环境下,系统提供了三个接口:int getrlimit(int resource, struct rlimit *rlim);
、int setrlimit(int resource, const struct rlimit *rlim);
、int prlimit(pid_t pid, int resource, const struct rlimit *new_limit, struct rlimit *old_limit);
分别用来获取当前进程的限制、设置当前进程的限制以及根据Pid设置对应进程的限制。
那么具体有哪些限制,也就是接口中的resource
参数有哪些,可以参考man里的信息,这里大致翻译一下:
RLIMIT_AS
进程最大可使用的虚拟内存空间,以bytes计算。这个限制主要影响brk
、mmap
、mremap
这几个系统调用,当达到限制时,这些系统调用就会返回ENOMEM
提示无内存可分配了。RLIMIT_CORE
coredump文件最大的大小,如果为0,则不会产生coredump,当是非0值时,如果coredump超过这个值会被truncated到这个大小RLIMIT_CPU
CPU使用时间限制,单位秒。当进程CPU时间达到软限制时,系统会给进程发送一个SIGXCPU
信号,默认行为是将进程终止,但是进程可以选择捕获这个信号并作出相应选择,如果继续执行达到了硬限制则会发送SIGKILL
信号终止进程RLIMIT_DATA
进程数据段的最大值,主要是堆空间,影响brk
和sbrk
两个系统调用,达到限制时返回ENOMEM
RLIMIT_FSIZE
进程可建立的文件的最大长度。如果超出这一限制时,系统会发送SIGXFSZ
信号,默认行为是将进程终止,但是进程可以选择捕获这个信号,这时对应的调用返回EFBIG
。RLIMIT_LOCKS (Early Linux 2.4 only)
进程可以进程可创建的锁数量,包括flock
和fcntl
两个调用RLIMIT_MEMLOCK
进程可锁定在内存中的最大数据量,单位bytes。RLIMIT_MSGQUEUE (Since Linux 2.6.8)
进程可为POSIX消息队列分配的最大字节数RLIMIT_NICE (since Linux 2.6.12, but see BUGS below)
进程可通过setpriority
和nice
调用设置的最大nice值RLIMIT_NOFILE
进程可打开的最大文件数,影响open
、pipe
、dup
等调用,达到限制时会返回EMFILE
错误RLIMIT_NPROC
实际运行进程的用户所能运行的最大进程数(在linux里更准确的说法是线程),如果达到这个限制,fork
调用返回EAGAIN
RLIMIT_RSS
进程最大驻留内存页数,单位内存页个RLIMIT_RTPRIO (Since Linux 2.6.12, but see BUGS)
进程最大可设置的实时调度优先级RLIMIT_RTTIME (Since Linux 2.6.25)
实时调度情况下进程最大占用的时间片,单位微秒RLIMIT_SIGPENDING (Since Linux 2.6.8)
实际运行进程的用户所能拥有的最大挂起信号数量RLIMIT_STACK
进程栈空间的最大大小,单位字节,如果达到了这个限制,系统会发送SIGSEGV
信号
从编程角度可设置的东西很多,而且很复杂,很多其实都用不到,需要注意的是,设置这些需要CAP_SYS_RESOURCE
权限。
有几个注意的点:
- 通过
fork
创建的子进程,是继承父进程的配置的。而execve
调用则会保留原进程的配置。 - Linux 2.6.24之后,进程的限制信息可以通过
/proc/{pid}/limits
查看。
说完了这么多,其实有个很关键的问题没有弄明白,默认值是怎么设置的?bash里ulimt -a显示的所有限制,默认值是多少?
如果稍微搜索一下,大概率会出现一个配置文件,位于/etc/security/limits.conf
,这个文件可以针对用户设置默认值,然而,是谁负责读取这个文件内容的呢?
实际上只有PAM会有可能读取这个配置,准确的说是pam_limits.so
读取的,具体还需要看PAM的配置,举个比较简单的例子,有些时候,我们可能会用sudo -u xxxx command
来切换用户执行某些命令,如果按上面所说的子进程继承父进程的相关配置的话,是不是执行的进程配置和当前session一样了?实际上并不是。
以CentOS 7为例,在我们执行sudo -u
的时候,是会调用到PAM的配置的,具体的配置在/etc/pam.d/sudo
文件:
#%PAM-1.0
auth include system-auth
account include system-auth
password include system-auth
session optional pam_keyinit.so revoke
session required pam_limits.so
可以看到,在执行sudo的时候,创建session过程中,必须要依赖pam_limits.so
,这时就会读取/etc/security/limits.conf
相关配置,设置默认的limits配置。
所以在很多场景下,特别是需要切换到另一个用户的session启动进程的时候,需要主要limits相关配置,避免因为用户配置不一致导致的问题。