Почему setrlimit (RLIMIT_NPROC) не работает при запуске от имени пользователя root, но отлично работает при запуске от имени обычного пользователя? - PullRequest
0 голосов
/ 24 февраля 2019

Я написал следующую программу на 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 голосов
/ 24 февраля 2019

следующий предложенный код:

  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

...