prlimit(2) считывает/устанавливает ограничения

Other Alias

getrlimit, setrlimit

ОБЗОР

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