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计算。这个限制主要影响brkmmapmremap这几个系统调用,当达到限制时,这些系统调用就会返回ENOMEM提示无内存可分配了。

  • RLIMIT_CORE
    coredump文件最大的大小,如果为0,则不会产生coredump,当是非0值时,如果coredump超过这个值会被truncated到这个大小

  • RLIMIT_CPU
    CPU使用时间限制,单位秒。当进程CPU时间达到软限制时,系统会给进程发送一个SIGXCPU信号,默认行为是将进程终止,但是进程可以选择捕获这个信号并作出相应选择,如果继续执行达到了硬限制则会发送SIGKILL信号终止进程

  • RLIMIT_DATA
    进程数据段的最大值,主要是堆空间,影响brksbrk两个系统调用,达到限制时返回ENOMEM

  • RLIMIT_FSIZE
    进程可建立的文件的最大长度。如果超出这一限制时,系统会发送SIGXFSZ信号,默认行为是将进程终止,但是进程可以选择捕获这个信号,这时对应的调用返回EFBIG

  • RLIMIT_LOCKS (Early Linux 2.4 only)
    进程可以进程可创建的锁数量,包括flockfcntl两个调用

  • RLIMIT_MEMLOCK
    进程可锁定在内存中的最大数据量,单位bytes。

  • RLIMIT_MSGQUEUE (Since Linux 2.6.8)
    进程可为POSIX消息队列分配的最大字节数

  • RLIMIT_NICE (since Linux 2.6.12, but see BUGS below)
    进程可通过setprioritynice调用设置的最大nice值

  • RLIMIT_NOFILE
    进程可打开的最大文件数,影响openpipedup等调用,达到限制时会返回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相关配置,避免因为用户配置不一致导致的问题。