GETRLIMIT(2)

ИМЯ
getrlimit, setrlimit, prlimit - считывает/устанавливает ограничения использования ресурсов

ОБЗОР
#include <sys/time.h>
#include <sys/resource.h>

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

Требования макроса тестирования свойств для glibc (см. feature_test_macros(7)):

prlimit(): _GNU_SOURCE

ОПИСАНИЕ
Системные вызовы getrlimit() и setrlimit(), соответственно, получают и устанавливают ограничения использования ресурсов. Каждому ресурсу назначается мягкое и жёсткое ограничение, определяемое структурой rlimit:

struct rlimit {
    rlim_t rlim_cur;  /* мягкое ограничение */
    rlim_t rlim_max;  /* жёсткое ограничение (максимум для rlim_cur) */
};
Мягким ограничением является значение, принудительно устанавливаемое ядром для соответствующего ресурса. Жёсткое ограничение работает как максимальное значение для мягкого ограничения: непривилегированные процессы могут определять только свои мягкие ограничения в диапазоне от 0 до жёсткого ограничения, то есть однозначно меньше жёсткого ограничения. Привилегированные процессы (в Linux: имеющие мандат CAP_SYS_RESOURCE) могут устанавливать произвольные значения в любых пределах.

Значение RLIM_INFINITY означает отсутствие ограничений для ресурса (в структуре, возвращаемой getrlimit() и в структуре, передаваемой в setrlimit()).

Значение resource должно быть одним из:

RLIMIT_AS
    Максимальный размер виртуальной памяти (адресного пространства) процесса в байтах. Учитывается в вызовах brk(2), mmap(2) и mremap(2), которые завершатся с ошибкой ENOMEM, если будет превышено это ограничение. Также завершится с ошибкой автоматическое расширение стека (и будет сгенерирован сигнал SIGSEGV, по которому завершится процесс, если не было создано с помощью sigaltstack(2) альтернативного стека). Так как значение имеет тип long, на машинах с 32-битным long максимальное значение ограничения будет около 2 ГиБ, или этот ресурс не ограничивается.

RLIMIT_CORE
    Максимальный размер файла core (смотрите core(5)). Если значение равно 0, то файлы core не создаются. Если значение больше нуля, то создаваемые дампы обрезаются до этого размера.

RLIMIT_CPU
    Время выполнения на ЦП в секундах. Когда процесс достигает своего мягкого ограничения, то ему отправляется сигнал SIGX CPU. Действием по умолчанию для этого сигнала является завершение процесса. Однако, этот сигнал может быть перехвачен, и обработчик может передать управление в основную программу. Если процесс продолжает потреблять процессорное время, то ему будет отправляться SIGXCPU раз в секунду до тех пор, пока не будет достигнуто жёсткое ограничение, и тогда процессу будет послан сигнал SIGKILL. (Последний пункт описывает поведение Linux. В разных реализациях действия над потребляющими процессорное время после прохождения мягкого ограничения процессами различаются. Переносимые приложения, где требуется перехват сигнала, должны выполнять корректное завершение процесса после первого получения SIGXCPU.)

RLIMIT_DATA
    Максимальный размер сегмента данных процесса (инициализированные данные, неинициализированные данные, куча). Это ограничение учитывается в вызовах brk(2) и sbrk(2), которые завершатся с ошибкой ENOMEM при достижении мягкого ограничения этого ресурса.

RLIMIT_FSIZE
    Максимальный размер файлов, создаваемых процессом. Попытки расширить файл сверх этого ограничения приведёт к доставке сигнала SIGXFSZ. По умолчанию по этому сигналу процесс завершается, но процесс может перехватить этот сигнал и в этом случае выполнявшийся системный вызов (например, write(2), truncate(2)) завершится с ошибкой EFBIG.

RLIMIT_LOCKS (только в ранних версиях Linux 2.4)
    Ограничение на общее количество блокировок flock(2) и аренд fcntl(2), которое может установить процесс.

RLIMIT_MEMLOCK
    Максимальное количество байт памяти, которое может быть заблокировано в ОЗУ. В целях эффективности это ограничение округляется в меньшую сторону до ближайшего значения, кратного размеру системной страницы. Это ограничение учитывается в mlock(2) и mlockall(2) и в mmap(2) при операции MAP_LOCKED. Начиная с Linux 2.6.9, оно также учитывается в shmctl(2) при операции SHM_LOCK, где определяет максимальное количество байт всех общих сегментов памяти (см. shmget(2)), которые могут быть заблокированы вызывающим процессом с реальным идентификатором пользователя. Блокировки по операции SHM_LOCK у shmctl(2) учитываются отдельно от попроцессных блокировок памяти, устанавливаемых mlock(2), mlockall(2) и mmap(2) операцией MAP_LOCKED; процесс может заблокировать пространство до этого значения заданного ограничения байт в каждой из этих двух категорий. В ядрах Linux до версии 2.6.9 этим ограничением контролировалось количество памяти, которое можно было блокировать привилегированному процессу. Начиная с Linux 2.6.9 это ограничение снято и теперь это ограничение управляет количеством памяти, которое может блокировать непривилегированный процесс.

RLIMIT_MSGQUEUE (начиная с Linux 2.6.8)
    Ограничение на количество байт, которое может выделяться для очередей сообщений POSIX для вызывающего процесса с реальным идентификатором пользователя. Это ограничение учитывается в mq_open(3). Каждая очередь сообщений, которую создаёт пользователь, учитывается (пока не будет удалена) в формуле:
        Начиная с Linux 3.5:
            bytes = attr.mq_maxmsg * sizeof(struct msg_msg) +
                    min(attr.mq_maxmsg, MQ_PRIO_MAX) *
                          sizeof(struct posix_msg_tree_node)+
                                    /* издержки */
                    attr.mq_maxmsg * attr.mq_msgsize;
                                    /* данные из сообщения */
        Linux 3.4 и старее:
            bytes = attr.mq_maxmsg * sizeof(struct msg_msg *) +
                                    /* издержки */
                    attr.mq_maxmsg * attr.mq_msgsize;
                                    /* данные из сообщения */
    где attr — структура mq_attr, указанная в четвёртом аргументе mq_open(3), а msg_msg и posix_msg_tree_node — внутренние структуры ядра. Дополнение «издержки» (overhead) в формуле нужно учитывает байты расходов требуемые в реализации на то, чтобы пользователь не смог создать бесконечное количество сообщений нулевой длины (для таких сообщений, тем не менее, потребляется системная память для учёта использования системных ресурсов).

RLIMIT_NICE (начиная с Linux 2.6.12, см. ДЕФЕКТЫ далее)
    Определяет максимум, до которого может быть увеличено значение nice с помощью setpriority(2) или nice(2). Действительный максимум значения nice высчитывается по формуле: 20 - rlim_cur. (Так пришлось поступить из-за того, что отрицательные числа нельзя указывать в значениях ограничений ресурсов, так как они, обычно, имеют специальное предназначение. Например, RLIM_INFINITY, обычно равно -1.)

RLIMIT_NOFILE
    Определяет значение, на 1 больше максимального количества дескрипторов файлов, которое может открыть этот процесс. Попытки (open(2), pipe(2), dup(2) и т.п.) превысить это ограничение приведут к ошибке EMFILE. (Раньше это ограничение в BSD называлось RLIMIT_OFILE.)

RLIMIT_NPROC
    Максимальное количество процессов (или, более точно для Linux, нитей), которое может создать вызывающий процесс с реальным идентификатором пользователя. При превышении этого ограничения fork(2) завершается с ошибкой EAGAIN. Данное ограничение не действует на процессы с мандатом CAP_SYS_ADMIN или CAP_SYS_RESOURCE.

RLIMIT_RSS
    Максимальное ограничение (в байтах) размера постоянного присутствия процесса (числа виртуальных страниц, постоянно присутствующих в ОЗУ). Это ограничение учитывается только начиная с версии Linux 2.4.x, x < 30, и только в вызовах madvise(2) со значением MADV_WILLNEED.

RLIMIT_RTPRIO (начиная с Linux 2.6.12, смотрите ДЕФЕКТЫ)
    Определяет максимум для приоритета реального времени, который можно установить для процесса с помощью sched_setscheduler(2) и sched_setparam(2).

RLIMIT_RTTIME (начиная с Linux 2.6.25)
    Определяет ограничение (в микросекундах) на количество времени ЦП, которое процесс может быть запланирован выполняться в условиях реального времени без выполнения блокирующего системного вызова. Для работы ограничения, всякий раз когда процесс делает блокирующий системный вызов счётчик использованного времени ЦП сбрасывается в ноль. Счётчик времени ЦП не сбрасывается, если процесс продолжает пытаться использовать ЦП, но был вытеснен, его выделенное время на исполнение истекло или он вызвал sched_yield(2). При достижении мягкого ограничения процессу посылается сигнал SIGXCPU. Если процесс перехватил сигнал, проигнорировал его и продолжает потреблять время ЦП, то раз в секунду будет генерироваться сигнал SIGXCPU до тех пор, пока не будет достигнуто жёсткое ограничение, и процессу не будет послан сигнал SIGKILL. Это ограничение предназначено для предотвращения блокировки системы вышедшими из под контроля процессами реального времени.

RLIMIT_SIGPENDING (начиная с Linux 2.6.8)
    Определяет ограничение на количество сигналов, которые могут быть поставлены в очередь вызывающего процесса с реальным пользовательским идентификатором. При проверке ограничения учитываются обычные сигналы и сигналы реального времени. Однако ограничение учитывается только в sigqueue(3); всегда возможно использовать kill(2) для постановки в очередь любого сигнала, которого ещё нет в очереди процесса.

RLIMIT_STACK
    Максимальный размер стека процесса в байтах. При достижении этого ограничения генерируется сигнал SIGSEGV. Для обработки этого сигнала процесс должен использовать альтернативный стек сигналов (sigaltstack(2)). Начиная с Linux 2.6.23, это ограничение также определяет количество места, используемого для аргументов командной строки процесса и его переменных окружения; подробней об этом смотрите в execve(2).

  prlimit()
Системный вызов prlimit(), который есть только в Linux объединяет и расширяет функции setrlimit() и getrlimit(). Он может использоваться для задания и получения ограничений ресурсов произвольного процесса. Аргумент resource имеет тот же смысл что и в setrlimit() и getrlimit(). Если значение аргумента new_limit не равно NULL, то структура rlimit, на которую он указывает, используется для задания новых значений мягкий и жёстких ограничений для resource. Если значение аргумента old_limit не равно NULL, то успешный вызов prlimit() помещает текущие значения мягких и жёстких ограничений для resource в структуру rlimit, на которую указывает old_limit. В аргументе pid задаётся идентификатор процесса с которым работает вызов. Если pid равно 0, то вызов применяется к вызывающему процессу. Для установки и получения ресурсов не своего процесса, вызывающий должен иметь мандат CAP_SYS_RESOURCE в пользовательском пространстве имён процесса, ограничения ресурсов которого изменяются или реальный, эффективный и сохранённый идентификатор пользователя процесса назначения должен совпадать с реальным идентификатором пользователя вызывающего и реальный, эффективный и сохранённый идентификатор группы процесса назначения должны совпадать с реальным идентификатором группы вызывающего.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении возвращается 0. В случае ошибки возвращается -1, а errno устанавливается в соответствующее значение.

ОШИБКИ

EFAULT
    Аргумент-указатель указывает за пределы доступного адресного пространства.

EINVAL
    Указано некорректное значение resource; или для setrlimit() или prlimit(): rlim->rlim_cur больше чем rlim->rlim_max.

EPERM
    Непривилегированный процесс пытался увеличить жёсткое ограничение; для этого требуется мандат CAP_SYS_RESOURCE.

EPERM
    Вызывающий пытался увеличить жёсткое ограничение RLIMIT_NOFILE, превышая максимум, заданный в /proc/sys/fs/nr_open (смотрите proc(5)).

EPERM
    Вызывающий процесс не имеет прав для назначения ограничений процессу, указанному в pid.

ESRCH
    Не удалось найти процесс с идентификатором, указанном в pid.

ВЕРСИИ
Системный вызов prlimit() появился в Linux 2.6.36. Поддержка в glibc доступна начиная с версии 2.13.

АТРИБУТЫ
Описание терминов данного раздела смотрите в attributes(7).
Интерфейс | Атрибут | Значение
getrlimit(), setrlimit(), prlimit() | Безвредность в нитях | MT-Safe

СООТВЕТСТВИЕ СТАНДАРТАМ
getrlimit(), setrlimit(): POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD.
prlimit(): только в Linux. Ограничение RLIMIT_MEMLOCK и RLIMIT_NPROC появились из BSD и их нет в POSIX.1; они есть в BSD и Linux, но реализации несколько различны. Ограничение RLIMIT_RSS появилось из BSD и его нет в POSIX.1; тем не менее оно есть в большинстве реализаций. Ограничения RLIMIT_MSGQUEUE, RLIMIT_NICE, RLIMIT_RTPRIO, RLIMIT_RTTIME и RLIMIT_SIGPENDING есть только в Linux.

ЗАМЕЧАНИЯ
Дочерний процесс, созданный fork(2), наследует ограничения ресурсов родителя. Ограничения ресурсов сохраняются при execve(2). Уменьшение мягкого ограничения ресурса ниже текущего потребления процесса будет выполнено (но в дальнейшем процесс не сможет увеличить потребление ресурса). Ограничения ресурсов интерпретатора командной строки можно устанавливать с помощью встроенной команды ulimit (limit в csh(1)). Ограничения ресурсов интерпретатора наследуются дочерними процессами, которые он создаёт при выполнении команд. Начиная с Linux 2.6.24, ограничения ресурсов любого процесса можно узнать с помощью /proc/[pid]/limits; смотрите proc(5). В старых системах была функция vlimit() с подобным setrlimit() назначением. Для обратной совместимости в glibc также есть функция vlimit(). Во всех новых приложениях должен быть использован setrlimit().

  Отличия между библиотекой C и ABI ядра
Начиная с версии 2.13, обёрточные функции glibc getrlimit() и setrlimit() больше не вызывают соответствующие системные вызовы, вместо этого вызывается prlimit() по причинам, описанным в разделе ДЕФЕКТЫ. Обёрточная функция в glibc называется prlimit(); нижележащий системный вызов называется prlimit64().

ДЕФЕКТЫ
В старых ядрах Linux сигналы SIGXCPU и SIGKILL, посылаемые когда у процесса обнаруживается достижение мягкого и жёсткого ограничения RLIMIT_CPU, доставляются на одну секунду (ЦП) позднее чем это должно быть. Это исправлено в ядре версии 2.6.8. В ядрах 2.6.x до версии 2.6.17, ограничение RLIMIT_CPU равное 0, неправильно воспринималось как «без ограничения» (подобно RLIM_INFINITY). Начиная с Linux 2.6.17, установка ограничения в 0 действует, но реально обрабатывается как ограничение в 1 секунду. Из-за дефекта ядра RLIMIT_RTPRIO не работает в версии 2.6.12; это исправлено в ядре 2.6.13. В ядре 2.6.12 было несоответствие в единицу между диапазонами приоритетов, возвращаемых getpriority(2) и RLIMIT_NICE. Это приводило к тому, что реальный максимум значения nice вычислялся как 19 - rlim_cur. Исправлено в ядре 2.6.13. Начиная с Linux 2.6.12, если процесс имеет мягкое ограничение RLIMIT_CPU и установлен обработчик для SIGXCPU, то, помимо вызова обработчика сигнала, ядро увеличивает мягкое ограничение на одну секунду. Такое поведение повторяется, если процесс продолжает потреблять процессорное время, и происходит это до тех пор, пока не будет достигнуто жёсткое ограничение, после чего процесс будет завершён. В других реализациях мягкое ограничение RLIMIT_CPU не меняется подобным образом, и поведение Linux, вероятно, нестандартно; переносимые приложения не должны полагаться на данную специфику Linux. Ограничение Linux RLIMIT_RTTIME демонстрирует такое же поведение, при исчерпании мягкого ограничения. В ядрах до 2.4.22 не определялась ошибка EINVAL в setrlimit(), если значение rlim->rlim_cur было больше rlim->rlim_max.

  Представление «больших» значений ограничений ресурсов на 32-битных платформах
В обёрточных функциях glibc getrlimit() и setrlimit() используется 64-битный тип данных rlim_t, даже на 32-битных платформах. Однако в системных вызовах getrlimit() и setrlimit() тип данных rlim_t приводится к unsigned long (32-битному). Кроме этого, в ядрах Linux версии до 2.6.36 ограничители ресурсов на 32-битных платформах представлялись как unsigned long. Однако 32-битный тип данных недостаточно велик. Для этого больше подходит RLIMIT_FSIZE, который определяет максимальный размер на который можно увеличить файл; чтобы его можно было использовать, данное ограничение должно быть представлено типом, соразмерным с типом, используемым для представления файловых смещений — 64-битным off_t (предполагается, что программа компилируется в параметром _FILE_OFFSET_BITS=64). Если программа пытается задать ограничение ресурса значением, большим чем можно представить 32-битным unsigned long, то, чтобы обойти это ограничение ядра, обёрточная функция glibc setrlimit() просто преобразует значение ограничения в RLIM_INFINITY. Иначе говоря, запрашиваемое назначение ограничения ресурса просто игнорируется. Данная проблема касается Linux 2.6.36 с двумя принципиальными отличиями:

*   добавлено новое представление ограничений ресурсов в ядре — 64 бита используется даже на 32-битных платформах;

*   добавлен системный вызов prlimit(), в котором для аргументов ограничений ресурсов используются 64-битные значения.

Начиная с версии 2.13, в glibc для обхода ограничений системных вызовов getrlimit() и setrlimit() для реализации обёрточных функций setrlimit() и getrlimit() используется вызов prlimit().

ПРИМЕР
Представленная ниже программа показывает использование prlimit().

#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
#define errExit(msg) 	do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)
int
main(int argc, char *argv[])
{
    struct rlimit old, new;
    struct rlimit *newp;
    pid_t pid;
    if (!(argc == 2 || argc == 4)) {
        fprintf(stderr, "Использование: %s <pid> [<новое-мягкое-ограничение> "
                "<новое-жёсткое-ограничение>]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    pid = atoi(argv[1]);        /* PID процесса назначения */
    newp = NULL;
    if (argc == 4) {
        new.rlim_cur = atoi(argv[2]);
        new.rlim_max = atoi(argv[3]);
        newp = &new;
    }
    /* Установить ограничение на время ЦП процесса назначения;
       получить и показать предыдущее ограничение */
    if (prlimit(pid, RLIMIT_CPU, newp, &old) == -1)
        errExit("prlimit-1");
    printf("Previous limits: soft=%lld; hard=%lld\n",
            (long long) old.rlim_cur, (long long) old.rlim_max);
    /* Получить и показать новое ограничение времени ЦП */
    if (prlimit(pid, RLIMIT_CPU, NULL, &old) == -1)
        errExit("prlimit-2");
    printf("Новые ограничения: мягкое=%lld; жёсткое=%lld\n",
            (long long) old.rlim_cur, (long long) old.rlim_max);
    exit(EXIT_FAILURE);
}

СМОТРИТЕ ТАКЖЕ
prlimit(1), dup(2), fcntl(2), fork(2), getrusage(2), mlock(2), mmap(2), open(2), quotactl(2), sbrk(2), shmctl(2), malloc(3), sigqueue(3), ulimit(3), core(5), capabilities(7), cgroups(7), signal(7)