为什么 setrlimit(RLIMIT_NPROC) 在 运行 作为 root 时不起作用,但在 运行 作为普通用户时工作正常?

Why setrlimit(RLIMIT_NPROC) doesn't work when run as root but works fine when run as a normal user?

我编写了以下 C 程序来限制该程序可以创建的最大进程数(在 Linux 中)。本程序使用了setrlimit(),预计本程序最多可以创建4个进程。

// nproc.c
#include <stdio.h>
#include <unistd.h>
#include <sys/resource.h>

int main()
{
    struct rlimit rlim;
    rlim.rlim_cur = rlim.rlim_max = 4;
    setrlimit(RLIMIT_NPROC, &rlim);
    for (int i = 0; i < 3; ++i) printf("%d\n", fork());
    sleep(1);
    return 0;
}

当我以普通用户身份编译并运行这个程序时,它给出了以下输出:

$ ./nproc
-1
-1
-1

-1 表示 fork() 失败并且 rlimit 正在正常工作以限制程序可以创建的最大进程数。但是当我 运行 这个程序作为 root 时,它给出以下输出:

$ sudo ./nproc
25926
25927
25928
0
0
25929
0
25930
25931
0
0
0
25932
0

我们可以看到所有的fork()都成功了,rlimit没有正常工作。问题出在哪里?

以下建议代码:

  1. 干净地编译
  2. 无法执行所需的功能(?为什么?)
  3. 包含所有需要的头文件
  4. 只有 'parent' 尝试创建子进程
  5. 注意:OP 和提议的程序都在不等待子进程完成的情况下退出。 IE。主程序应该为每个启动的子进程调用 wait()wait_pid()
  6. 注意:对 sleep(1) 的调用使输出保持井井有条。但是,在此期间 sleep 子进程完成并退出,因此实际上在任何时候都只有 1 个子进程 运行ning,所以即使对 setrlimit() 的调用已成功,那个 'fork()` 循环可能 运行 永远。

现在,建议的代码:

#include <stdio.h>
#include <stdlib.h>

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

#include <sys/types.h>
#include <unistd.h>

int main( void )
{
    struct rlimit rlim;
    rlim.rlim_cur = rlim.rlim_max = 4;

    if( getrlimit(RLIMIT_NPROC, &rlim) == -1 )
    {
        perror( "getrlimit failed" );
        exit( EXIT_FAILURE );
    }

    if( setrlimit(RLIMIT_NPROC, &rlim) == -1 )
    {
        perror( "setrlimit failed" );
        exit( EXIT_FAILURE );
    }

    for (int i = 0; i < 4; ++i) 
    {
        pid_t pid = fork();
        switch( pid )
        {
            case -1:
            perror( "fork failed" );
            exit( EXIT_FAILURE );
            break;

            case 0:
            printf( "child pid: %d\n", getpid() );
            exit( EXIT_SUCCESS );
            break;

            default:
            printf( "parent pid: %d\n", getpid() );
            break;
        }
        sleep(1);
    }
    return 0;
}

a 运行 的程序结果:

fork failed: Resource temporarily unavailable

这表示调用 setrlimit()

时出现问题

来自手册页:

RLIMIT_NPROC
          This is a limit on the number of extant process (or,  more  pre‐
          cisely  on  Linux,  threads) for the real user ID of the calling
          process.  So long as the current number of  processes  belonging
          to  this process's real user ID is greater than or equal to this
          limit, fork(2) fails with the error EAGAIN.

          The RLIMIT_NPROC limit is not enforced for processes  that  have
          either the CAP_SYS_ADMIN or the CAP_SYS_RESOURCE capability.

所以,对 setrlimit() 的调用限制了线程的数量,而不是子进程的数量

但是,如果我们在调用 getrlimit() 之后立即添加几个打印语句,并在调用 setrlimit() 之后再次添加,结果是:

    if( getrlimit(RLIMIT_NPROC, &rlim) == -1 )
    {
        perror( "getrlimit failed" );
        exit( EXIT_FAILURE );
    }

    printf( "soft limit: %d\n", (int)rlim.rlim_cur );
    printf( "hard limit: %d\n\n", (int)rlim.rlim_max );

    if( setrlimit(RLIMIT_NPROC, &rlim) == -1 )
    {
        perror( "setrlimit failed" );
        exit( EXIT_FAILURE );
    }


    if( getrlimit(RLIMIT_NPROC, &rlim) == -1 )
    {
        perror( "getrlimit failed" );
        exit( EXIT_FAILURE );
    }

    printf( "soft limit: %d\n", (int)rlim.rlim_cur );
    printf( "hard limit: %d\n\n", (int)rlim.rlim_max );

那么结果是:

soft limit: 27393
hard limit: 27393

soft limit: 27393
hard limit: 27393

parent pid: 5516
child pid: 5517
parent pid: 5516
child pid: 5518
parent pid: 5516
child pid: 5519
parent pid: 5516
child pid: 5520

这表明调用:setrlimit() 实际上并未更改子进程的限制

注:我运行宁ubuntulinux18.04