linux rlimit资源限制


一、简介

每个进程在运行时系统不会无限制的允许单个进程不断的消耗系统资源,因此都会设置资源限制。Linux系统中使用 resource limit 来表示,每个进程都可以设置不同的资源限制,当前进程和其以后fork的子进程会遵循此限制,而其他进程不受当前进程条件的影响。


二、ulimit命令

通过该命令,我们可以设置当前shell环境中的一些资源限制值,这些设置会影响到该shell和由该shell中运行的其余命令,对于不相关的其他shell进程不受影响。

# ulimit -a
-t: time(cpu-seconds)     unlimited
-f: file(blocks)          unlimited
-c: coredump(blocks)      0
-d: data(KiB)             unlimited
-s: stack(KiB)            8192
-l: lockedmem(KiB)        65536
-n: nofiles(descriptors)  32768
-p: processes             53498
-i: sigpending            53498
-q: msgqueue(bytes)       819200
-e: maxnice               40
-r: maxrtprio             0
-m: resident-set(KiB)     unlimited
-v: address-space(KiB)    unlimited
-x: filelocks             unlimited

比如可以使用 ulimit -c 1 来使当前shell环境下的进程产生core文件,并且core文件大小限制在1个block。


三、rlimit系统调用

#include <sys/time.h>
#include <sys/resource.h>

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);


1. getrlimit()/setrlimit()

getrlimit() 和 setrlimit() 系统调用分别获取和设置资源限制。每个资源都有一个关联的软限制和硬限制,由 rlimit 结构定义:

struct rlimit {
   rlim_t rlim_cur;  /* Soft limit */
   rlim_t rlim_max;  /* Hard limit (ceiling for rlim_cur) */
};

软限制是内核对相应资源强制执行的值。硬限制充当软限制的上限:非特权进程只能将其软限制设置为 0 到硬限制范围内的值,并(不可逆地)降低其硬限制。 特权进程(在 Linux 下:具有 CAP_SYS_RESOURCE 功能的进程)可以对任一限制值进行任意更改。

值 RLIM_INFINITY 表示对资源没有限制(无论是在 getrlimit() 返回的结构中还是在传递给 setrlimit() 的结构中)。

资源参数必须是以下之一:

(1) RLIMIT_AS

这是进程虚拟内存(地址空间)的最大大小。该限制以字节为单位指定,并向下舍入到系统页面大小。此限制会影响对 brk(2)、mmap(2) 和 mremap(2) 的调用,超过此限制后,这些调用会失败并出现错误 ENOMEM。

(2) RLIMIT_CORE

这是进程可以转储的核心文件(请参阅 core(5))的最大大小(以字节为单位)。 当 0 时,不创建核心转储文件。 当非零时,较大的转储将被截断至此大小。

(3) RLIMIT_CPU

这是进程可以消耗的 CPU 时间量的限制(以秒为单位)。当进程达到软限制时,会向其发送 SIGXCPU 信号。该信号的默认操作是终止进程。但是,信号可以被捕获,并且处理程序可以将控制权返回给主程序。 如果进程继续消耗CPU时间,则会每秒发送一次 SIGXCPU,直到达到硬限制,此时发送 SIGKILL。(后一点描述了 Linux 行为。实现方式因处理达到软限制后继续消耗 CPU 时间的进程的方式而异。需要捕获此信号的便携式应用程序应在首次收到 SIGXCPU 时执行有序终止。)

(4) RLIMIT_DATA

这是进程数据段(已初始化数据、未初始化数据和堆)的最大大小。 该限制以字节为单位指定,并向下舍入到系统页面大小。 此限制会影响对 brk(2)、sbrk(2) 和 mmap(2) 的调用,这些调用在遇到此资源的软限制时会失败并出现错误 ENOMEM。

(5) RLIMIT_FSIZE

这是进程可以创建的文件的最大大小(以字节为单位)。尝试将文件扩展至超出此限制会导致发送 SIGXFSZ 信号。 默认情况下,此信号终止进程,但进程可以捕获此信号,在这种情况下,相关系统调用(例如 write(2)、truncate(2))会失败并出现错误 EFBIG。

(6) RLIMIT_MEMLOCK

这是可以锁定到 RAM 中的最大内存字节数。 该限制实际上向下舍入到最接近的系统页面大小的倍数。 此限制会影响 mlock(2)、mlockall(2) 和 mmap(2) MAP_LOCKED 操作。 它还会影响 shmctl(2) SHM_LOCK 操作,它设置共享内存段中可能被真实用户 ID 锁定的共享内存段中总字节数的最大值(请参阅 shmget(2))。 调用过程。 shmctl(2) SHM_LOCK 锁与由 mlock(2)、mlockall(2) 和 mmap(2) MAP_LOCKED 建立的每进程内存锁分开计算; 进程可以在这两个类别中锁定最多达到此限制的字节。

在 2.6.9 之前的 Linux 内核中,此限制控制特权进程可以锁定的内存量。 从 Linux 2.6.9 开始,特权进程可以锁定的内存量没有限制,并且此限制控制非特权进程可以锁定的内存量。

(7) RLIMIT_MSGQUEUE

这是对 POSIX 消息队列分配的字节数的限制。 mq_open(3) 强制执行此限制。 用户创建的每个消息队列都会根据以下公式针对此限制进行计数(直到被删除):
从 Linux 3.5 开始:

bytes = attr.mq_maxmsg * sizeof(struct msg_msg) + min(attr.mq_maxmsg, MQ_PRIO_MAX) * sizeof(struct posix_msg_tree_node)+ attr.mq_maxmsg * attr.mq_msgsize;

其中 attr 是指定为 mq_open(3) 的第四个参数的 mq_attr 结构,msg_msg 和 posix_msg_tree_node 结构是内核内部结构。

公式中的“overhead”加数考虑了实现所需的开销字节,并确保用户不能创建无限数量的零长度消息(尽管如此,每个消息都会消耗一些系统内存用于簿记开销)。

(8) RLIMIT_NICE

这指定了使用 setpriority(2) 或 nice(2) 可以将进程的 nice 值提高到的上限。 nice 值的实际上限计算为 20 - rlim_cur。 因此,该限制的有用范围是从 1(对应于nice值 19)到 40(对应于nice值 -20)。 这种不寻常的范围选择是必要的,因为负数通常具有特殊含义,因此不能指定为资源限制值。 例如,RLIM_INFINITY 通常与 -1 相同。 有关 Nice 值的更多详细信息,请参阅 sched(7)。

(9) RLIMIT_NOFILE

这指定一个比该进程可以打开的最大文件描述符数大一的值。 尝试(open(2)、pipe(2)、dup(2) 等)超过此限制会产生错误 EMFILE。
从 Linux 4.5 开始,此限制还定义了非特权进程(没有 CAP_SYS_RESOURCE 功能的进程)可以通过 UNIX 域套接字传递到其他进程的文件描述符的最大数量。 此限制适用于 sendmsg(2) 系统调用。 有关更多详细信息,请参阅 unix(7)。

(10) RLIMIT_NPROC

这是对调用进程的真实用户 ID 的现有进程(或者更准确地说,在 Linux 上,线程)数量的限制。 只要属于该进程的真实用户 ID 的当前进程数大于或等于此限制,fork(2) 就会失败并出现错误 EAGAIN。
对于具有 CAP_SYS_ADMIN 或 CAP_SYS_RESOURCE 功能的进程,不强制执行 RLIMIT_NPROC 限制。

(11) RLIMIT_RTPRIO

这指定了可以使用 sched_setscheduler(2) 和 sched_setparam(2) 为此进程设置的实时优先级的上限。

(12) RLIMIT_RTTIME

这是对在实时调度策略下调度的进程在不进行阻塞系统调用的情况下可能消耗的 CPU 时间量的限制(以微秒为单位)。 出于此限制的目的,每次进程进行阻塞系统调用时,其消耗的 CPU 时间计数都会重置为零。 如果进程继续尝试使用 CPU 但被抢占、其时间片到期或调用 sched_yield(2),则 CPU 时间计数不会重置。

达到软限制后,会向进程发送 SIGXCPU 信号。 如果进程捕获或忽略该信号并继续消耗CPU时间,那么 SIGXCPU 将每秒生成一次,直到达到硬限制,此时向进程发送 SIGKILL 信号。

此限制的预期用途是阻止失控的实时进程锁定系统。

有关实时调度策略的更多详细信息,请参阅 sched(7)

(13) RLIMIT_SIGPENDING

这是对可以为调用进程的真实用户 ID 排队的信号数量的限制。 为了检查此限制,标准信号和实时信号都会被计数。 但是,该限制仅针对 sigqueue(3) 强制执行; 始终可以使用kill(2) 对尚未排队到进程的任何信号的一个实例进行排队。

(14) RLIMIT_STACK

这是进程堆栈的最大大小(以字节为单位)。 达到此限制后,会生成 SIGSEGV 信号。 要处理此信号,进程必须使用备用信号堆栈 (sigaltstack(2))。
此限制还决定了进程的命令行参数和环境变量使用的空间量; 有关详细信息,请参阅 execve(2)。


2. prlimit()

Linux 特定的 prlimit() 系统调用组合并扩展了 setrlimit() 和 getrlimit() 的功能。 它可用于设置和获取任意进程的资源限制。

资源参数与 setrlimit() 和 getrlimit() 具有相同的含义。

如果 new_limit 参数不为 NULL,则它指向的 rlimit 结构用于为资源的软限制和硬限制设置新值。 如果 old_limit 参数不为 NULL,则成功调用 prlimit() 会将先前的资源软限制和硬限制放置在 old_limit 指向的 rlimit 结构中。

pid 参数指定调用要操作的进程的 ID。 如果 pid 为 0,则用于调用进程。 要设置或获取除自身以外的进程的资源,调用者必须在要更改资源限制的进程的用户命名空间中具有 CAP_SYS_RESOURCE 能力,或者目标进程的真实、有效和保存的设置用户 ID 必须与调用者的真实用户ID相匹配,并且目标进程的真实、有效、保存的设置组ID必须与调用者的真实组ID相匹配。

成功返回0,失败返回-1和设置errno.


3. RLIM_INFINITY

如果读取和设置的rlimit中的值为 RLIM_INFINITY 宏,那么表示是无限制,unlimited。


4. 例子

#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>

#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)

int main(int argc, char *argv[])
{
    struct rlimit old, new;
    struct rlimit *newp;
    pid_t pid;

    if (!(argc == 2 || argc == 4)) {
        fprintf(stderr, "Usage: %s <pid> [<new-soft-limit> <new-hard-limit>]\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    pid = atoi(argv[1]); /* PID of target process */

    newp = NULL;
    if (argc == 4) {
        new.rlim_cur = atoi(argv[2]);
        new.rlim_max = atoi(argv[3]);
        newp = &new;
    }

    /* Set CPU time limit of target process; retrieve and display
    previous limit */

    if (prlimit(pid, RLIMIT_CPU, newp, &old) == -1)
        errExit("prlimit-1");
    printf("Previous limits: soft=%lld; hard=%lld\n", (long long) old.rlim_cur, (long long) old.rlim_max);

    /* Retrieve and display new CPU time limit */

    if (prlimit(pid, RLIMIT_CPU, NULL, &old) == -1)
        errExit("prlimit-2");
    printf("New limits: soft=%lld; hard=%lld\n", (long long) old.rlim_cur, (long long) old.rlim_max);

    exit(EXIT_SUCCESS);
}

 


参考:
man getrlimit

 

posted on 2024-03-06 10:42  Hello-World3  阅读(11)  评论(0编辑  收藏  举报

导航