为什么以root用户身份运行时setrlimit(RLIMIT_NPROC)不起作用,而以普通用户身份运行时,为什么工作得很好?

时间:2019-02-24 06:33:55

标签: c linux kernel setrlimit

我编写了以下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 个答案:

答案 0 :(得分:0)

以下建议的代码:

  1. 干净地编译
  2. 无法执行所需的功能(为什么?)
  3. 包含所有必需的头文件
  4. 只有“父级”尝试创建子进程
  5. 注意:OP和拟议的程序都退出而无需等待子进程完成。即主程序应该为每个启动的子进程调用wait()wait_pid()
  6. 注意:对sleep(1)的调用使输出保持美观和井井有条。但是,在该sleep子进程完成并退出的过程中,实际上在任何时候都只有一个子进程在运行,因此即使对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;
}

该程序的运行导致:

fork failed: Resource temporarily unavailable

表示调用setrlimit()

时出现问题

在MAN页面:

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()的调用实际上并未更改子进程的限制

注意:我正在运行ubuntu linux 18.04