Unix/Linux编程:进程资源限制

文章目录

  • 进程资源限制
  • 获取设置进程默认可以打开的最大文件描述符数
    • sysconf
  • getdtablesize、getrlimit

进程资源限制

每个进程都用一组资源限制值,它们可以用来限制进程能够消耗的各种系统资源。

  • 如在执行任意一个程序之前如果不想让它消耗太多资源系统,则可以设置该进程的资源限制。
  • 使用 shell的内置命令 ulimit 可以设置 shell 的资源限制(在 C shell 中是 limit)。shell创建用来执行用户命令的进程会继承这些限制

从2.6.24开始,Linux特有的proc/PID/limits 文件可以用来查看任意进程的所有资源限制。这个文件由相应进程的真实用户 ID 所拥有,并且只有进程 ID 为用户 ID 的进程(或特权进程)才能够读取这个文件。

getrlimit()和 setrlimit()系统调用允许一个进程可以读取和修改自己的资源限制

NAME
       getrlimit, setrlimit, prlimit - get/set resource limits

SYNOPSIS
       #include 
       #include 

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

RETURN VALUE
       On success, these system calls return 0.  On error, -1 is returned, and errno is set appropriately.

resource参数标识出了需要读取或者修改的资源限制。

rlim参数用来返回限制值(getrlimit())或指定新的资源限制值((setrlimit()),它是一个指向包含两个字段的结构的指针。

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

这两个字段对应于一种资源的两个关联限制:软限制(rlim_cur)和硬限制(rlim_max)。(rlim_t 数据类型是一个整数类型。基于这个原因,在打印 rlim_t 值时,最好转换成 long long 并使用%lld printf()修饰符。)

  • 软限制规定了进程能够消耗的资源数量(内核强加给相应资源的限制值)。一个进程可以将软限制调整为从 0 到硬限制之间的值。
  • 对于大多数资源来讲,硬限制的唯一作用是为软限制设定了上限。
    • 特权(CAP_SYS_RESOURCE)进程能够增大和缩小硬限制(它必须大于等于软限制值)
    • 非授权调用进程只可以将其软限制指定为0~硬限制范围中的某个值,同时能不可逆转地降低其硬限制。
  • 在 getrlimit()和 setrlimit()调用中,rlim_cur 和 rlim_max 取值为 RLIM_INFINITY 表示没有限制(不限制资源的使用)

在大多数情况下,特权进程和非特权进程在使用资源时都会受到限制。通过 fork()创建的子进程会继承这些限制并且在 exec()调用之间不得到保持。

resource有如下取值:

  • RLIMIT_AS

    • 进程虚拟内存限制大小(字节数),即进程总的可用存储空间的最大长度
    • 试图(brk()、sbrk()、mmap()、mremap()以及 shmat())超出这个限制会得到 ENOMEM 错误。
    • 在实践中,程序中会超出这个限制的最常见的地方是在调用 malloc 包中的函数时,因为它们会使用 sbrk()和mmap()。当碰到这个限制时,栈增长操作也会失败,进而会出现下面 RLIMIT_STACK 限制中列出的情况。
  • RLIMIT_CORE

    • 核心文件大小(字节数),即core文件的最大字节数
    • 当达到这个限制时,核心 dump 文件就不会再产生了
    • 如果值为0标识阻止创建core文件
      • 这种做法有时候是比较有用的,因为核心 dump 文件可能会变得非常大,而最终用户通常又不知道如何处理这些文件。
      • 另一个禁用核心 dump文件的原因是安全性——防止程序占用的内存中的内容输出到磁盘上。
      • 如果 RLIMIT_FSIZE限制值低于这个限制值,那么核心 dump 文件的最大大小会被限制为 RLIMIT_FSIZE 字节。
  • RLIMIT_CPU

    • 进程最多使用的 CPU 时间(包括系统模式和用户模式)。
    • 当超过此软限制时,向该进程发送SIGXCPU信号(SIGXCPU 信号的默认动作是终止一个进程并输出一个核心 dump。此外,也可以捕获这个信号并将控制返回给主程序。)。
    • 不同的 UNIX 实现对进程处理完 SIGXCPU 信号之后继续消耗 CPU 时间这种情况的处理方式不同。大多数会每隔固定时间间隔向进程发送一个 SIGXCPU 信号。
    • 在达到软限制值之后,Linux 内核会在进程每消耗一秒钟的 CPU 时间后向其发送一个 SIGXCPU 信号。当进程持续执行直至达到硬 CPU 限制时,内核会向其发送一个 SIGKILL 信号,该信号总是会终止进程。
  • RLIMIT_DATA

    • 数据段的最大字节长度。这是初始化数据段、非初始化数据段、堆的总和
    • 试图(sbrk()和 brk())访问这个限制之外的数据段会得到ENOMEM 的错误。
    • 与 RLIMIT_AS 一样,程序中会超出这个限制的最常见的地方是在调用malloc 包中的函数时。
  • RLIMIT_FSIZE

    • 文件大小(字节数),即可以创建的文件的最大字节长度
    • 当超过此软限制时,向该进程发送SIGXFSZ信号。并且系统调用(如 write()或truncate())会返回EFBIG错误。
    • SIGXFSZ信号的默认动作是终止进程并产生一个核心dump。此外,也可以捕获这个信号并将控制返回给主程序。不管怎样,后续视图扩充该文件的操作都会得到同样的信号和错误。
  • RLIMIT_MEMLOCK

    • 一个进程最多能够将多少字节的虚拟内存锁进物理内存以防止内存被交换出去
    • 这个限制会影响 mlock()和 mlockall()系统调用以及 mmap()和 shmctl()系统调用的加锁参数
    • 如果在调用 mlockall()时指定了 MCL_FUTURE 标记,那么 RLIMIT_MEMLOCK 限制也会导致后续的 brk()、sbrk()、mmap()和 mremap()调用失败
  • RLIMIT_MSGQUEUE

    • 能够为调用进程的真实用户 ID 的 POSIX 消息队列分配的最大字节数。
    • RLIMIT_MSGQUEUE 限制只会影响调用进程。这个用户下的其他进程不会受到影响,因为它们也会设置这个限制或继承这个限制。
  • RLIMIT_NICE

    • 规定了使用 sched_setscheduler()和 nice()能够为进程设置的最大 nice 值。
    • 这个最大值是通过公式 20 – rlim_cur 计算得来的,其中 rlim_cur 是当前的 RLIMIT_NICE 软资源限制
  • RLIMIT_NOFILE

    • 一个进程能够分配的最大文件描述符数量加 1
    • 试图(如 open()、pipe()、socket()、accept()、shm_open()、dup()、dup2()、fcntl(F_DUPFD)和 epoll_create())分配的文件描述符数量超出这个限制时会失败。
      • 在大多数情况,失败的错误是 EMFILE
      • 在 dup2(fd, newfd)调用中,失败的错误是 EBADF,
      • 在 fcntl(fd, F_DUPFD, newfd)调用中当 newfd 大于或等于这个限制时,失败的错误是 EINVAL。
    • 更改此限制将影响到sysconf函数在参数_SC_OPEN_MAX中返回的值
    • 在 Linux 上可以通过使用 readdir()扫描/proc/PID/fd 目录下的内容来检查一个进程当前打开的文件描述符,这个目录包含了进程当前打开的每个文件描述符的符号链接。
    • 从 2.6.25 的版本开始,这个限制由 Linux 特有的/proc/sys/fs/nr_open 文件定义。这个文件中的默认值是 1048576,超级用户可以修改这个值。试图将软或硬 RLIMIT_NOFILE 限制设置为一个大于最大值的值会产生 EPERM 错误。
    • 还存在一个系统级别的限制,它规定了系统中所有进程能够打开的文件数量,通过 Linux 特有的/proc/sys/fs/file-max 文件能够获取和修改这个限制。
      • 只有特权(CAP_SYS_ADMIN)进程才能够超出 file-max 的限制。
      • 在非特权进程中,当系统调用碰到 file-max 限制时会返回 ENFILE 错误
  • RLIMIT_NPROC

    • 规定了调用进程的真实用户 ID 下最多能够创建的进程数量。
    • 试图(fork()、vfork()和 clone())超出这个限制会得到 EAGAIN 错误
    • RLIMIT_NPROC 限制只影响调用进程。这个用户下的其他进程不会受到影响,除非它们也设置或继承了这个限制。这个限制不适用于特权(CAP_SYS_ADMIN 和 CAP_SYS_RESOURCE)进程。
    • Linux 还提供了系统层面的限制来规定所有用户能够创建的进程数量。在 Linux 2.4以及之后的版本中,可以使用 Linux 特有的/proc/sys/kernel/threads-max 文件来获取和修改这个限制
    • 准确地说,RLIMIT_NPROC 资源限制和 threads-max 文件实际上限制的是所能创建的线程数量,而不是进程的数量
    • 更改此限制将影响到sysconf函数在参数_SC_CHILD_MAX中返回的值
    • 不存在一种统一的方法能够在不同系统中找出某个特定用户 ID 已经创建的进程数。
  • RLIMIT_RSS

    • 进程驻留集中的最大页面数,即当前位于物理内存中的虚拟内存页面总数。
    • Linux 提供了这个限制,但当前并没有起任何作用
    • 如果可用的物理存储器非常少,则内核将从进程处取同超过RSS的部分
  • RLIMIT_RTPRIO

    • 规定了使用 sched_ setscheduler()和 sched_setparam()能够为进程设置的最高实时优先级(自 Linux 2.6.12 起)
  • RLIMIT_RTTIME

    • 规定了一个进程在实时调度策略中不睡眠(即执行一个阻塞系统调用)的情况下最大能消耗的 CPU 秒数(微秒;自 Linux 2.6.25 起)
    • 如果进程达到了软限制,那么内核会向进程发送一个 SIGXCPU 信号,之后进程每消耗一秒的 CPU 时间都会收到一个SIGXCPU 信号。在达到硬限制时,内核会向进程发送一个 SIGKILL 信号。
  • RLIMIT_SBSIZE

    • 在任一给定时刻,一个用户可以占用的套接字缓冲区的最大长度(字节)
  • RLIMIT_SIGPENDING
    - 一个进程可排队的信号最大数量制(Linux 特有的,自 Linux 2.6.8 起)
    - 试图(sigqueue())超出这个限制会得到 EAGAIN错误。
    - RLIMIT_SIGPENDING 只影响调用进程。这个用户下的其他进程不会受到影响,除非它们也设置或继承了这个限制

  • RLIMIT_STACK

    • 栈段的大小(字节数)
    • 试图扩展栈大小以至于超出这个限制会导致内核向该进程发送一个 SIGSEGV 信号。
    • 由于栈空间已经被用光了,因此捕获这个信号的唯一方式是建立另外一个备用的信号栈,
  • RLIMIT_VMEN

    • 这时RLIMIT_AS的同义词

看个例子:下面调用了setrlimit()来设置一个用户能够创建的进程数量的软限制和硬限制(RLIMIT_NPROC)同时使用了 printRlimit()来输出变更之前和之后的资源限制,最后根据资源限制创建了尽可能多的进程

#include 
#include 
#include 
#include 
#include 
int                     /* Print 'msg' followed by limits for 'resource' */
printRlimit(const char *msg, int resource)
{
    struct rlimit rlim;

    if (getrlimit(resource, &rlim) == -1)
        return -1;

    printf("%s soft=", msg);
    if (rlim.rlim_cur == RLIM_INFINITY)
        printf("infinite");
#ifdef RLIM_SAVED_CUR           /* Not defined on some implementations */
    else if (rlim.rlim_cur == RLIM_SAVED_CUR)
        printf("unrepresentable");
#endif
    else
        printf("%lld", (long long) rlim.rlim_cur);

    printf("; hard=");
    if (rlim.rlim_max == RLIM_INFINITY)
        printf("infinite\n");
#ifdef RLIM_SAVED_MAX           /* Not defined on some implementations */
    else if (rlim.rlim_max == RLIM_SAVED_MAX)
        printf("unrepresentable");
#endif
    else
        printf("%lld\n", (long long) rlim.rlim_max);

    return 0;
}
int main(int argc, char *argv[])
{
    struct rlimit rl;
    int j;
    pid_t childPid;

    if (argc < 2 || argc > 3 || strcmp(argv[1], "--help") == 0){
        printf("%s soft-limit [hard-limit]\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    printRlimit("Initial maximum process limits: ", RLIMIT_NPROC);

    /* Set new process limits (hard == soft if not specified) */

    rl.rlim_cur = (argv[1][0] == 'i') ? RLIM_INFINITY :  atoi(argv[1]);  // "soft-limit"
                               
    rl.rlim_max = (argc == 2) ? rl.rlim_cur :
                (argv[2][0] == 'i') ? RLIM_INFINITY : atoi(argv[2]);  // "hard-limit"
                                 
    if (setrlimit(RLIMIT_NPROC, &rl) == -1){
        perror("setrlimit");
        exit(EXIT_FAILURE);
	}
	
    printRlimit("New maximum process limits:     ", RLIMIT_NPROC);

    /* Create as many children as possible */

    for (j = 1; ; j++) {
        switch (childPid = fork()) {
        case -1:
        		 perror("fork  error  ");
        		 exit(EXIT_FAILURE);
        case 0:
        		_exit(EXIT_SUCCESS);            /* Child */
        default:        /* Parent: display message about each new child
                           and let the resulting zombies accumulate */
            printf("Child %d (PID=%ld) started\n", j, (long) childPid);
            break;
        }
    }
}

现象与预期不符合,待研究
在这里插入图片描述

获取设置进程默认可以打开的最大文件描述符数

sysconf

获取进程默认可以打开的最大文件描述符数

#include 
#include 
#include 
#include 

#ifdef OPEN_MAX
static long openmax = OPEN_MAX
#else
static long openmax = 0;
#endif

/*
  if OPEN_MAX is indeterminate, this might be inadequate
*/
#define OPEN_MAX_GUESS 256

long open_max(void)
{
    if(openmax == 0){
        errno = 0;
        if((openmax = sysconf(_SC_OPEN_MAX)) < 0){

            if(errno == 0)
                openmax = OPEN_MAX_GUESS; //it's indeterminate,return -1 and not change errno
            else
                perror("sysconf error for _SC_OPEN_MAX");//_SC_OPEN_MAX is invalid,errno is set EINVAL
        }
    }
    return(openmax);
}


int main(void){
    printf("open max:%ld\n",open_max());
}

在这里插入图片描述

getdtablesize、getrlimit

获取进程默认可以打开的最大文件描述符数

#include 
#include 
#include 
#include 
#include 

long get_open_max(void){
    struct rlimit rl;
    const char *myname = "acl_open_limit";
    int   rlim_cur = -1;

    if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
        rlim_cur = getdtablesize();
        printf("%s(%d): getrlimit error: %s, use: %d",
               myname, __LINE__, strerror(errno), rlim_cur);
        return rlim_cur;
    }

    return rl.rlim_cur;   // 软限制
}

int main(void){
    printf("open max:%ld\n" , get_open_max());
}

在这里插入图片描述

  • getdtablesize()函数等效于带有RLIMIT_NOFILE选项的getrlimit()。

  • 保留此功能是出于历史原因。它是Single UNIX Specification,版本2中的Legacy Feature的一部分,但已被撤回,不作为Single UNIX Specification,版本3的一部分得到支持。新应用程序应使用getrlimit()而不是getdtablesize()。

  • 如果有必要在为Single UNIX规范版本3编写的应用程序中继续使用此功能,请在包含任何标准系统头之前定义功能测试宏_UNIX03_WITHDRAWN。该宏公开了在单一UNIX规范版本3中删除的所有接口和符号。

设置进程默认可以打开的最大文件描述符数

#include 
#include 
#include 
#include 
#include 
int acl_set_limit(int limit){
    const char *myname = "acl_open_limit";
    int   rlim_cur = -1;
    struct rlimit rl;

    if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
        rlim_cur = getdtablesize();
        printf("%s(%d): getrlimit error: %s, use: %d",
                     myname, __LINE__, strerror(errno), rlim_cur);
        return rlim_cur;
    }

    if (rl.rlim_max <= 0){
        rl.rlim_max = 204800;
    }
        
    rlim_cur = (int) rl.rlim_cur;

    if (limit > 0) {
        if (limit > (int) rl.rlim_max){
            rl.rlim_cur = rl.rlim_max;
        }else{
            rl.rlim_cur = limit;
        }
            
        if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {  // 修改的是软限制而不是硬限制
            printf("%s(%d): setrlimit error: %s, limit: %d,"
                         " curr: %d", myname, __LINE__,
                   strerror(errno), limit, rlim_cur);
            return rlim_cur;
        }
        else{
            return (int) rl.rlim_cur;
        }
            
    } else if (rl.rlim_max > rl.rlim_cur) {
        rlim_cur = (int) rl.rlim_cur;
        rl.rlim_cur = rl.rlim_max;
        if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
            printf("%s(%d): setrlimit error: %s,"
                         " cur: %d, max: %d", myname, __LINE__,
                         strerror(errno), (int) rl.rlim_cur,
                         (int) rl.rlim_max);
            return rlim_cur;
        }

        return (int) rl.rlim_cur;
    } else{
        return (int) rl.rlim_cur;
    }
}

下面程序可以查询当前系统支持的所有资源当前的软限制与硬限制

#include "apue.h"
#include 

#define	doit(name)	pr_limits(#name, name)

static void	pr_limits(char *, int);

int
main(void)
{
#ifdef	RLIMIT_AS
	doit(RLIMIT_AS);
#endif

	doit(RLIMIT_CORE);
	doit(RLIMIT_CPU);
	doit(RLIMIT_DATA);
	doit(RLIMIT_FSIZE);

#ifdef	RLIMIT_MEMLOCK
	doit(RLIMIT_MEMLOCK);
#endif

#ifdef RLIMIT_MSGQUEUE
	doit(RLIMIT_MSGQUEUE);
#endif

#ifdef RLIMIT_NICE
	doit(RLIMIT_NICE);
#endif

	doit(RLIMIT_NOFILE);

#ifdef	RLIMIT_NPROC
	doit(RLIMIT_NPROC);
#endif

#ifdef RLIMIT_NPTS
	doit(RLIMIT_NPTS);
#endif

#ifdef	RLIMIT_RSS
	doit(RLIMIT_RSS);
#endif

#ifdef	RLIMIT_SBSIZE
	doit(RLIMIT_SBSIZE);
#endif

#ifdef RLIMIT_SIGPENDING
	doit(RLIMIT_SIGPENDING);
#endif

	doit(RLIMIT_STACK);

#ifdef RLIMIT_SWAP
	doit(RLIMIT_SWAP);
#endif

#ifdef	RLIMIT_VMEM
	doit(RLIMIT_VMEM);
#endif

	exit(0);
}

static void
pr_limits(char *name, int resource)
{
	struct rlimit		limit;
	unsigned long long	lim;

	if (getrlimit(resource, &limit) < 0)
		err_sys("getrlimit error for %s", name);
	printf("%-14s  ", name);
	if (limit.rlim_cur == RLIM_INFINITY) {
		printf("(infinite)  ");
	} else {
		lim = limit.rlim_cur;
		printf("%10lld  ", lim);
	}
	if (limit.rlim_max == RLIM_INFINITY) {
		printf("(infinite)");
	} else {
		lim = limit.rlim_max;
		printf("%10lld", lim);
	}
	putchar((int)'\n');
}

https://www.yingsoo.com/news/servers/60515.html

你可能感兴趣的:(Unix/Linux编程,linux,unix,运维)