进程资源限制
每个进程都需要进行资源限制,避免把系统搞垮(比如对CPU的使用,硬盘空间的占用等等)。基于这个目的,Linux内核在每个进程的进程描述符中还应该包含资源限制的数据结构,Linux使用了一个数组成员,该数组成员的包含关系为current->signal->rlim,数组的定义如下所示:
struct rlimit rlim[RLIM_NLIMITS];
其中,rlimit结构的定义为:
struct rlimit { __kernel_ulong_t rlim_cur; __kernel_ulong_t rlim_max; };
__kernel_ulong_t等于无符号长整形。RLIM_NLIMITS的大小为16,也就是说,目前对进程资源的限制有16种,分别如下所示:
- RLIMIT_AS
进程空间的最大值,单位是字节。当进程使用malloc()或者相关函数扩大自己的地址空间时,就会检查这个值。 - RLIMIT_CORE
最大核心转储文件大小,单位是字节。当进程被中止时,内核会检查这个值,然后进程的当前目录下创建一个core文件。(最常见的情况就是,我们的程序有bug而崩溃的时候,会在该目录下产生一个core文件。)当然了,如果这个值的大小为0,是不会产生core文件的。 - RLIMIT_CPU
进程占用CPU的最大时间,单位是秒(S)。如果超过这个时间,内核会发送一个SIGXCPU信号,如果进程还是没有终止,再发送SIGKILL信号。 - RLIMIT_DATA
最大堆大小,单位是字节。内核在扩大进程的堆空间之前,检查这个值。 - RLIMIT_FSIZE
最大文件大小,单位是字节。如果进程尝试扩大文件超过这个值,内核发送一个SIGXFSZ信号。 - RLIMIT_LOCKS
文件锁的最大数量(目前不强制)。 - RLIMIT_MEMLOCK
非交换内存的最大值,单位是字节。当内核调用mlock()或mlockall()系统调用尝试给一个页帧加锁时会检查该值。 - RLIMIT_MSGQUEUE
POSIX消息队列的最大字节数。 - RLIMIT_NOFILE
打开的文件描述符最大数量。当新打开一个文件或复制文件描述符时,内核都会检查这个值。 - RLIMIT_NPROC
用户可以拥有的最大进程数量。 - RLIMIT_RSS
进程可以拥有的页帧数量,也就是物理帧的数量(目前不强制)。 - RLIMIT_SIGPENDING
进程挂起信号的最大数量。 - RLIMIT_STACK
最大栈空间,单位是字节。在扩展进程的用户态栈时,内核会检查这个值。 - RLIMIT_NICE
优先级的完美值。进程可通过setpriority()或nice()设置。 - RLIMIT_RTPRIO
最大实时优先级。进程可通过sched_setscheduler和sched_setparam设置。 - RLIMIT_RTTIME
实时任务的timeout,单位是uS。
结构体成员rlim_cur表示对当前进程的资源限制。比如current->signal->rlim[RLIMIT_CPU].rlim_cur
是指当前正在运行的进程的CPU时间限制。
成员rlim_max表示资源限制允许的最大值。可以通过getrlimit()和setrlimit()系统调用进行设置,用户可以增加rlim_max的值到rlim_max。但是,超级用户(更准确地讲,具有CAP_SYS_RESOURCE能力的用户)可以增加rlim_max的值,或者将rlim_cur设为超过rlim_max的值。
这就是为什么当我们的程序崩溃时,却发现没有core文件,这是因为系统默认是关闭的。所以需要调用命令
ulimit -c unlimited // 设置core文件大小为不限制大小
然后才能看到core文件的原因。
但是,我们查看源码的时候会发现,大部分的资源限制都被设为RLIM_INFINITY(0xffffffff),这意味对资源没有用户限制(当然了,本身还要受到硬件的限制:比如可用的RAM,硬盘实际空间等等)。这是因为我们想要保留软件设置的自由度,如果代码中直接定义对硬件资源的限制,软件操作的空间就会变小。
通过上面的方法,系统管理员可以更改对资源的限制。当用户登陆到系统时,内核创建一个超级用户拥有的进程,通过它调用setrlimit()减小rlim_max和rlim_cur的值;然后执行login shell,成为用户态进程(实际就是进程init)。用户新创建的进程继承它父进程的rlim数组内容,所以,用用也不能覆盖掉由超级用户赋值的限制值。