【问题标题】:Why setrlimit(RLIMIT_NPROC) doesn't work when run as root but works fine when run as a normal user?为什么 setrlimit(RLIMIT_NPROC) 在以 root 身份运行时不起作用,但在以普通用户身份运行时工作正常?
【发布时间】:2019-07-17 20:45:17
【问题描述】:

我编写了以下 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 没有正常工作。问题出在哪里?

【问题讨论】:

  • 您不能为 root 设置 nproc 限制。根是根。
  • root 拥有“适当的特权”并且可以改变大多数规则。
  • 关于:for (int i = 0; i &lt; 3; ++i) printf("%d\n", fork()); 这有父进程和子进程(甚至是错误条件)生成进程。不是你真正想要的。

标签: c linux kernel setrlimit


【解决方案1】:

以下建议的代码:

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

【讨论】:

    猜你喜欢
    • 2011-10-03
    • 2020-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 2017-04-07
    • 1970-01-01
    相关资源
    最近更新 更多