Текст книги "Операционная система UNIX"
Автор книги: Андрей Робачевский
Жанр:
ОС и Сети
сообщить о нарушении
Текущая страница: 14 (всего у книги 39 страниц)
Стандарт POSIX. 1 определил новый набор функций управления сигналами. основанный на интерфейсе 4.2BSD UNIX и лишенный рассмотренных выше недостатков.
Модель сигналов, предложенная POSIX, основана на понятии набора сигналов (signal set), описываемого переменной типа sigset_t
. Каждый бит этой переменной отвечает за один сигнал. Во многих системах тип sigset_t
имеет длину 32 бита, ограничивая количество возможных сигналов числом 32.
Следующие функции позволяют управлять наборами сигналов:
#include
int sigempyset(sigset_t *set);
int siufillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(sigset_t *set, int signo);
В отличие от функции signal(3C), изменяющей диспозицию сигналов, данные функции позволяют модифицировать структуру данных sigset_t
, определенную процессом. Для управления непосредственно сигналами используются дополнительные функции, которые мы рассмотрим позже.
Функция sigemptyset(3C) инициализирует набор, очищая все биты. Если процесс вызывает sigfillset(3C), то набор будет включать все сигналы, известные системе. Функции sigaddset(3C) и sigdelset(3C) позволяют добавлять или удалять сигналы набора. Функция sigismember(3C) позволяет проверить, входит ли указанный параметром signo
сигнал в набор.
Вместо функции signal(3C) стандарт POSIX. 1 определяет функцию sigaction(2), позволяющую установить диспозицию сигналов, узнать ее текущее значение или сделать и то и другое одновременно. Функция имеет следующее определение:
#include
int sigaction (int sig, const struct sigaction *act,
struct sigaction *oact);
Вся необходимая для управлением сигналами информация передается через указатель на структуру sigaction
, имеющую следующие поля:
void (*sa_handler)() | Обработчик сигнала sig |
void (*sa_sigaction)(int, siginfo_t*, void*) | Обработчик сигнала sig при установленном флаге SA_SIGINFO |
sigset_t sa_mask | Маска сигналов |
int sa_flags | Флаги |
Поле sa_handler
определяет действие, которое необходимо предпринять при получении сигналов, и может принимать значения SIG_IGN
, SIG_DFL
или адреса функции-обработчика. Если значение sa_handler
или sa_sigaction
не равны NULL
, то в поле sa_mask
передается набор сигналов, которые будут добавлены к маске сигналов перед вызовом обработчика. Каждый процесс имеет установленную маску сигналов, определяющую сигналы, доставка которых должна быть заблокирована. Если определенный бит маски установлен, соответствующий ему сигнал будет заблокирован. После возврата из функции-обработчика значение маски возвращается к исходному значению. Заметим, что сигнал, для которого установлена функция-обработчик, также будет заблокирован перед ее вызовом. Такой подход гарантирует, что во время обработки, последующее поступление определенных сигналов будет приостановлено до завершения функции. Как правило, UNIX не поддерживает очередей сигналов, и это значит, что блокировка нескольких однотипных сигналов в конечном итоге вызовет доставку лишь одного.
Поле sa_flags
определяет флаги, модифицирующие доставку сигнала. Оно может принимать следующие значения:
SA_ONSTACK | Если определена функция-обработчик сигнала, и с помощью функции sigaltstack(2) задан альтернативный стек для функции-обработчика, то при обработке сигнала будет использоваться этот стек. Если флаг не установлен, будет использоваться обычный стек процесса. |
SA_RESETHAND * | Если определена функция-обработчик, то диспозиция сигнала будет изменена на SIG_DFL , и сигнал не будет блокироваться при запуске обработчика. Если флаг не установлен, диспозиция сигнала остается неизменной. |
SA_NODEFER * | Если определена функция-обработчик, то сигнал блокируется на время обработки только в том случае, если он явно указан в поле sa_mask . Если флаг не установлен, в процессе обработки данный сигнал автоматически блокируется. |
SA_RESTART | Если определена функция-обработчик, ряд системных вызовов, выполнение которых было прервано полученным сигналом, будут автоматически перезапущены после обработки сигнала.[25]25
К таким системным вызовам, в частности, относятся read(2) и write(2) для медленных устройств, таких как терминалы, а также ioctl(2), fcntl(2), wait(2) и waitpid(2). [Закрыть] Если флаг не установлен, системный вызов возвратит ошибку EINTR . |
SA_SIGINFO * | Если диспозиция указывает на перехват сигнала, вызывается функция, адресованная полем sa_sigaction . Если флаг не установлен, вызывается обработчик sa_handler . |
SA_NOCLDWAIT * | Если указанный аргументом sig сигнал равен SIGCHLD , при завершении потомки не будут переходить в состояние зомби. Если процесс в дальнейшем вызовет функции wait(2), wait3(2), waitid(2) или waitpid(2), их выполнение будет блокировано до завершения работы всех потомков данного процесса. |
SA_NOCLDSTOP * | Если указанный аргументом sig сигнал равен SIGCHLD , указанный сигнал не будет отправляться процессу при завершении или останове любого из его потомков. |
*Данные флаги не определены для UNIX BSD.
В системах UNIX BSD 4.x структура sigaction
имеет следующий вид:
struct sigaction {
void (*sa_handler)();
sigset_t sa_mask;
int sa_flags;
};
где функция-обработчик определена следующим образом:
void handler(int signo, int code, struct sigcontext *scp);
В первом аргументе signo
содержится номер сигнала, code
определяет дополнительную информацию о причине поступления сигнала, a scp
указывает на контекст процесса.
Для UNIX System V реализована следующая возможность получения более полной информации о сигнале. Если установлен флаг SA_SIGINFO
, то при получении сигнала sig
будет вызван обработчик, адресованный полем sa_sigaction
. Помимо номера сигнала, обычно передаваемого обработчику сигнала, ему будет переданы указатель на структуру siginfo_t
, содержащую информацию о причинах получения сигнала, а также указатель на структуру ucontext_t
, содержащую контекст процесса.
Структура siginfo_t
определена в файле
int si_signo | Номер сигнала |
int si_errno | Номер ошибки |
int si_code | Причина отправления сигнала |
В поле si_signo
хранится номер сигнала. Поле si_code
имеет следующий смысл: если его значение меньше или равно нулю, значит сигнал был отправлен прикладным процессом, в этом случае структура siginfo_t
содержит также следующие поля:
pid_t si_pid | Идентификатор процесса PID |
uid_t si_uid | Идентификатор пользователя UID |
которые адресуют процесс, пославший сигнал; если значение si_code
больше нуля, то оно указывает на причину отправления сигнала. Список возможных значений si_code
для некоторых сигналов, соответствующих полю si_signo
, приведен в табл. 2.19
Таблица 2.19. Значения поля si_code структуры siginfo_t для некоторых сигналов
si_signo | si_code | |
---|---|---|
SIGILL | Попытка выполнения недопустимой инструкции | |
ILL_ILLOPC | Недопустимый код операции (opcode) | |
ILL_ILLOPN | Недопустимый операнд | |
ILL_ADR | Недопустимый режим адресации | |
ILL_ILLTRP | Недопустимая ловушка (trap) | |
ILL_PRVOPC | Привилегированный код операции | |
ILL_PRVREG | Привилегированный регистр | |
ILL_COPROC | Ошибка сопроцессора | |
ILL_BADSTK | Ошибка внутреннего стека | |
SIGFPE | Особая ситуация операции с плавающей точкой | |
FPE_INTDIV | Целочисленное деление на ноль | |
FPE_INTOVF | Целочисленное переполнение | |
FPE_FLTDIV | Деление на ноль с плавающей точкой | |
FPE_FLTOVF | Переполнение с плавающей точкой | |
FPE_FLTUND | Потеря точности с плавающей точкой (underflow) | |
FPE_FLTRES | Неоднозначный результат операции с плавающей точкой | |
FPE_FLTINV | Недопустимая операция с плавающей точкой | |
FPE_FLTSUB | Индекс вне диапазона | |
SIGSEGV | Нарушение сегментации | |
SEGV_MAPPER | Адрес не отображается на объект | |
SEGV_ACCERR | Недостаточно прав на отображаемый объект | |
SIGBUS | Ошибка адресации | |
BUS_ADRALN | Недопустимое выравнивание адреса | |
BUS_ADRERR | Несуществующий физический адрес | |
BUS_OBJERR | Аппаратная ошибка, связанная с объектом | |
SIGTRAP | Ловушка | |
TRAP_BRKPT | Процессом достигнута точка останова | |
TRAP_TRACE | Ловушка трассирования процесса | |
SIGCHLD | Завершение выполнения дочернего процесса | |
CLD_EXITED | Дочерний процесс завершил выполнение | |
CLD_KILLED | Дочерний процесс был «убит» | |
CLD_DUMPED | Ненормальное завершение дочернего процесса | |
CLD_TRAPPED | Трассируемый дочерний процесс находится в ловушке | |
CLD_STOPPED | Выполнение дочернего процесса было остановлено | |
CLD_CONTINUED | Выполнение остановленного дочернего процесса было продолжено | |
SIGPOLL | Событие на опрашиваемом устройстве | |
POLL_IN | Поступили данные для ввода | |
POLL_OUT | Свободны буферы данных | |
POLL_MSG | Сообщение ожидает ввода | |
POLL_ERR | Ошибка ввода/вывода | |
POLL_PRI | Высокоприоритетные данные ожидают ввода | |
POLL_HUP | Устройство отключено |
Уже отмечалось, что при получении сигнала от пользовательского процесса структура siginfo_t
содержит дополнительные поля (табл. 2.20).
Таблица 2.20. Дополнительные поля структуры siginfo_t
si_signo | ||
---|---|---|
SIGILL SIGFPE | caddr_t si_addr | Адрес недопустимой инструкции |
SIGSEGV SIGBUS | caddr_t si_addr | Адрес недопустимой области памяти |
SIGCHLD | pid_t si_pid | Идентификатор дочернего процесса |
int si_status | Код возврата сигнала | |
SIGPOLL | long si_band | Ошибка канала (для модулей STREAMS) |
Установить маску сигналов или получить текущую маску можно с помощью функции sigprocmask(2):
#include
int sigprocmask(int how, sigset_t *set, sigset_t *oset);
Маска сигналов изменяется в соответствии с аргументом how
, который может принимать следующие значения:
SIG_BLOCK | Результирующая маска получится путем объединения текущей маски и набора set |
SIG_UNBLOCK | Сигналы набора set будут удалены из текущей маски |
SIG_SETMASK | Текущая маска будет заменена на набор set |
Если указатель set
равен NULL
, то аргумент how игнорируется. Если аргумент oset
не равен NULL
, то в набор, адресованный этим аргументом, помещается текущая маска сигналов.
Функция sigpending(2) используется для получения набора заблокированных сигналов, ожидающих доставки:
#include
int sigpending(int how, sigset_t *set, sigset_t *oset);
Список сигналов, ожидающих доставки, возвращается в наборе, адресованном аргументом set
.
Системный вызов sigsuspend(2) замещает текущую маску набором, адресованным аргументом set
, и приостанавливает выполнение процесса до получения сигналов, диспозиция которых установлена либо на завершение выполнения процесса, либо на вызов функции-обработчика сигнала.
#include
int sigsuspend(const sigset_t *set);
При получении сигнала, завершающего выполнение процесса, возврата из функции sigsuspend(2) не происходит. Если же диспозиция полученного сигнала установлена на вызов функции-обработчика, возврат из sisuspend(2) происходит сразу после завершения обработки сигнала. При этом восстанавливается маска, существовавшая до вызова sigsuspend(2).
Заметим, что в BSD UNIX вызов signal(3) является упрощенным интерфейсом к более общей функции sigaction(2), в то время как в ветви System V signal(3) подразумевает использование старой семантики ненадежных сигналов.
В заключение для иллюстрации изложенных соображений, приведем версию функции signal(), позволяющую использовать надежные сигналы. Похожая реализация используется в BSD UNIX. С помощью этой «надежной» версии мы повторим пример, рассмотренный нами выше, в измененном виде.
#include
#include
#include
#include
#include
/* Вариант «надежной» функции signal() */
void (*mysignal(int signo, void (*hndlr)(int)))(int) {
struct sigaction act, oact;
/* Установим маску сигналов */
act.sa_handler = hndlr;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (signo != SIGALRM)
act.sa_flags = SA_RESTART;
/* Установим диспозицию */
if (sigaction(signo, &act, &oact) < 0)
return SIG_ERR;
return(oact.sa_handler);
}
/* Функция-обработчик сигнала */
static void sig_hndlr(int signo) {
/* Эта часть кода нам уже не нужна
mysignal(SIGINT, sig_hndlr);
*/
printf(«Получен сигнал SIGINTn»);
}
main() {
/* Установим диспозицию */
mysignal(SIGINT, sig_hndlr);
mysignal(SIGUSR2, SIG_IGN);
/* Бесконечный цикл */
while (1)
pause();
}
Заметим, что при использовании надежных сигналов, не нужно восстанавливать диспозицию в функции-обработчике при получении сигнала.
Группы и сеансыПосле создания процесса ему присваивается уникальный идентификатор, возвращаемый системным вызовом fork(2) родительскому процессу. Дополнительно ядро назначает процессу идентификатор группы процессов (process group ID). Группа процессов включает один или более процессов и существует, пока в системе присутствует хотя бы один процесс этой группы. Временной интервал, начинающийся с создания группы и заканчивающийся, когда последний процесс ее покинет, называется временем жизни группы. Последний процесс может либо завершить свое выполнение, либо перейти в другую группу.
Многие системные вызовы могут быть применены как к единичному процессу, так и ко всем процессам группы. Например, системный вызов kill(2) может отправить сигнал как одному процессу, так и всем процессам указанной группы. Точно так же функция waitpid(2) позволяет родительскому процессу ожидать завершения конкретного процесса или любого процесса группы.
Каждый процесс, помимо этого, является членом сеанса (session), являющегося набором одной нескольких групп процессов. Понятие сеанса было введено в UNIX для логического объединения процессов, а точнее, групп процессов, созданных в результате регистрации и последующей работы пользователя в системе. Таким образом, термин "сеанс работы" в системе тесно связан с понятием сеанса, описывающего набор процессов, которые порождены пользователем за время пребывания в системе.
Процесс имеет возможность определить идентификатор собственной группы процессов или группы процесса, который является членом того же сеанса. Для этого используются два системных вызова: getpgrp(2) и getpgid(2):
#include
#include
pid_t getpgrp(void);
pid_t getpgid(pid_t pid);
Аргумент pid
, который передается функции getpgid(2), адресует процесс, идентификатор группы которого требуется узнать. Если этот процесс не принадлежит к тому же сеансу, что и процесс, сделавший системный вызов, функция возвращает ошибку.
Системный вызов setpgid(2) позволяет процессу стать членом существующей группы или создать новую группу.
#include
#include
int setpgid(pid_t pid, pid_t pgid);
Функция устанавливает идентификатор группы процесса pid
равным pgid
. Процесс имеет возможность установить идентификатор группы для себя и для своих потомков (дочерних процессов). Однако процесс не может изменить идентификатор группы для дочернего процесса, который выполнил системный вызов exec(2), запускающий на выполнение другую программу.
Если значения обоих аргументов равны, то создается новая группа с идентификатором pgid
, а процесс становится лидером (group leader) этой группы. Поскольку именно таким образом создаются новые группы, их идентификаторы гарантированно уникальны. Заметим, что группа не удаляется при завершении ее лидера, пока в нее входит хотя бы один процесс.
Идентификатор сеанса можно узнать с помощью функции getsid(2):
#include
#include
pid_t getsid(pid_t pid);
Как и в случае с группой, идентификатор pid
должен адресовать процесс, являющийся членом того же сеанса, что и процесс, вызвавший getsid(2). Заметим, что эти ограничения не распространяются на процессы, имеющие привилегии суперпользователя.
Вызов функции setsid(2) приводит к созданию нового сеанса:
#include
#include
pid_t setsid(void);
Новый сеанс создается лишь при условии, что процесс не является лидером какого-либо сеанса. В случае успеха процесс становится лидером сеанса и лидером новой группы.
Понятия группы и сеанса тесно связаны с терминалом или, точнее, с драйвером терминала. Каждый сеанс может иметь один ассоциированный терминал, который называется управляющим терминалом (controlling terminal), а группы, созданные в данном сеансе, наследуют этот управляющий терминал. Наличие управляющего терминала позволяет ядру контролировать стандартный ввод/вывод процессов, а также дает возможность отправить сигнал всем процессам ассоциированной с терминалом группы, например, при его отключении. Типичным примером является регистрация и работа пользователя в системе. При входе в систему терминал пользователя становится управляющим для лидера сеанса (в данном случае для командного интерпретатора shell) и всех процессов, порожденных лидером (в данном случае для всех процессов, которые запускает пользователь из командной строки интерпретатора). При выходе пользователя из системы shell завершает свою работу и таким образом отключается от управляющего терминала, что вызывает отправление сигнала SIGHUP
всем незавершенным процессам текущей группы. Это гарантирует, что после завершения работы пользователя в системе не останется запущенных им процессов.[26]26
Тем не менее в системе будут продолжать выполняться процессы, запущенные в фоновом режиме. Это утверждение также не справедливо для демонов – процессов, являющихся членами сеанса, не имеющего управляющего терминала. Система не имеет возможности автоматического отправления сигнала SIGHUP
таким процессам при выходе пользователя, и они будут продолжать выполняться даже после завершения пользователем работы в UNIX. Для «превращения» процесса в демона, он должен воспользоваться функцией setsid(2) и создать новый сеанс, лидером которого он автоматически окажется и который не будет ассоциирован с управляющим терминалом. Эти вопросы будут более подробно обсуждены при иллюстрации программы-демона далее в этой главе.
[Закрыть]
Как было показано, для каждого управляющего терминала существует сеанс, включающий одну или несколько групп процессов. Одна из этих групп является текущей (foreground group), а остальные фоновыми (background group).[27]27
Наличие текущей и фоновых групп процессов в сеансе работы пользователя зависит от возможности командного интерпретатора управлять заданиями (job control). При отсутствии этой возможности все процессы будут выполняться в той же группе, что и shell.
[Закрыть] Сигналы SIGINT
и SIGQUIT
, которые генерируются драйвером терминала, посылаются всем процессам текущей группы. Попытка процессов фоновых групп осуществить доступ к управляющему терминалу, как правило, вызывает отправление им сигналов SIGSTP
, SIGTTIN
или SIGTTOU
.
Рассмотрим следующие команды:
$ find / -name foo &
$ cat | sort
При этом происходит чтение ввода пользователя с клавиатуры (cat(1) и сортировка введенных данных (sort(1)). Если интерпретатор поддерживает управление заданиями, оба процесса, созданные для программ cat(1) и sort(1), будут помещены в отдельную группу. Это подтверждается выводом команды ps(1):
$ ps -efj | egrep «PID|andy»
UID PID PPID PGID SID С STIME TTY TIME CMD
andy 2436 2407 2435 2407 1 15:51:30 tty01 0:00 sort
andy 2431 2407 2431 2407 0 15:51:25 tty01 0:00 find / -name foo
andy 2407 2405 2407 2407 0 15:31:09 tty01 0:00 -sh
andy 2435 2407 2435 2407 0 15:51:30 tty01 0:00 cat
Все четыре процесса (sh, find, cat и sort) имеют один и тот же идентификатор сеанса, связанного с управляющим терминалом tty01. Процессы cat(1) и sort(1) принадлежат одной группе, идентификатор которой (2435) отличен от идентификатора группы командного интерпретатора (2407). То же самое можно сказать и о процессе find(1), который является лидером отдельной группы (2431). Можно также заметить, что процессы sh(1), find(1) и cat(1) являются лидерами групп, a еще sh(1) и лидером сеанса.
Хотя команда ps(1) не указывает, какие группы являются фоновыми, а какая текущей, синтаксис команд позволяет утверждать, что командный интерпретатор помещает cat(1) и sort(1) в текущую группу. Это, во-первых, позволяет процессу cat(1) читать данные со стандартного потока ввода, связанного с терминалом tty01. Во-вторых, пользователь имеет возможность завершить выполнение обоих процессов путем нажатия клавиши <Del> (или <Ctrl>+<C>), что вызовет генерацию сигнала SIGINT
. Получение процессами этого сигнала вызовет завершение их выполнения (действие по умолчанию), если, конечно, процесс не установил игнорирование SIGINT
. На рис. 2.13. представлена схема взаимодействия управляющего терминала, сеанса и групп процессов для приведенного выше примера. Более детально взаимосвязь между терминалом и процессами рассмотрена в следующей главе.
Рис. 2.13. Связь между управляющим терминалом, сеансом и группами
Если командный интерпретатор не поддерживает управление заданиями, оба процесса станут членами той же группы, что и сам shell. В этом случае командный интерпретатор должен позаботиться об игнорировании сигналов SIGINT
и SIGQUIT
, чтобы допустимые действия пользователя (такие как нажатие клавиши <Del> или <Ctrl>+<C>) не привели к завершению выполнения shell и выходу из системы.
UNIX является многозадачной системой. Это значит, что несколько процессов конкурируют между собой при доступе к различным ресурсам. Для «справедливого» распределения разделяемых ресурсов, таких как память, дисковое пространство и т.п., каждому процессу установлен набор ограничений. Эти ограничения не носят общесистемного характера, как, например, максимальное число процессов или областей, а устанавливаются для каждого процесса отдельно. Для получения информации о текущих ограничениях и их изменения предназначены системные вызовы getrlimit(2) и setrlimit(2):
#include
#include
int getrlimit{int resource, struct rlimit *rlp);
int setrlimit(int resource, const struct rlimit *rlp);
Аргумент resource
определяет вид ресурса, для которого мы хотим узнать или изменить ограничения процесса. Структура rlimit
состоит из двух полей:
rlim_t rlim_cur;
rlim_t rlim_max;
определяющих, соответственно, изменяемое (soft) и жесткое (hard) ограничение. Первое определяет текущее ограничение процесса на данный ресурс, а второе – максимальный возможный предел потребления ресурса. Например, изменяемое ограничение на число открытых процессом файлов может составлять 64, в то время как жесткое ограничение равно 1024.
Любой процесс может изменить значение текущего ограничения вплоть до максимально возможного предела. Жесткое ограничение может быть изменено в сторону увеличения предела потребления ресурса только процессом с привилегиями суперпользователя. Обычные процессы могут только уменьшить значение жесткого ограничения. Обычно ограничения устанавливаются при инициализации системы и затем наследуются порожденными процессами (хотя в дальнейшем могут быть изменены).
Вообще говоря, максимальный возможный предел потребления ресурса может иметь бесконечное значение. Для этого необходимо установить значение rlim_max
равным RLIM_INFINITY
. В этом случае физические ограничения системы (например, объем памяти и дискового пространства) будут определять реальный предел использования того или иного ресурса.
Различные ограничения и связанные с ними типы ресурсов приведены в табл. 2.21.
Таблица 2.21. Ограничения процесса (значения аргумента resource)
RLIMIT_CORE | Максимальный размер создаваемого файла core, содержащего образ памяти процесса. Если предел установлен равным 0, файл core создаваться не будет. | После создания файла core запись в этот файл будет остановлена при достижении предельного размера. |
RLIMIT_CPU | Максимальное время использования процессора в секундах. | При превышении предела процессу отправляется сигнал SIGXCPU . |
RLIMIT_DATA | Максимальный размер сегмента данных процесса в байтах, т.е. максимальное значение смещения брейк-адреса. | При достижении этого предела последующие вызовы функции brk(2) завершатся с ошибкой ENOMEM . |
RLIMIT_FSIZE | Максимальный размер файла, который может создать процесс. Если значение этого предела равно 0, процесс не может создавать файлы. | При достижении этого предела процессу отправляется сигнал SIGXFSZ . Если сигнал перехватывается или игнорируется процессом, последующие попытки увеличить размер файла закончатся с ошибкой EFBIG . |
RLIMIT_NOFILE | Максимальное количество назначенных файловых дескрипторов процесса. | При достижении этого предела, последующие попытки получить новый файловый дескриптор закончатся с ошибкой EMFlLE . |
RLIMIT_STACK | Максимальный размер стека процесса. | При попытке расширить стек за установленный предел отправляется сигнал SIGSEGV . Если процесс перехватывает или игнорирует сигнал и не использует альтернативный стек с помощью функции sigaltstack(2), диспозиция сигнала устанавливается на действие по умолчанию перед отправкой процессу. |
RLIMIT_VMEM | Максимальный размер отображаемой памяти процесса в байтах. (Предел определен в версиях System V.) | При достижении этого предела последующие вызовы brk(2) или mmap(2) завершатся с ошибкой ENOMEM . |
RLIMIT_NPROC | Максимальное число процессов с одним реальным UID. Определяет максимальное число процессов, которые может запустить пользователь. (Предел определен в версиях BSD UNIX.) | При достижении этого предела, последующие вызовы fork(2) для порождения нового процесса завершатся с ошибкой EAGAIN . |
RLIMIT_RSS | Максимальный размер в байтах резидентной части процесса (RSS – Resident Set Size). Определяет максимальное количество физической памяти, предоставляемой процессу. (Предел определен в версиях BSD UNIX.) | Если система ощущает недостаток памяти, ядро освободит память за счет процессов, превысивших свой RSS. |
RLIMIT_MEMLOCK | Максимальный физической памяти (физических страниц) в байтах, который процесс может заблокировать с помощью системного вызова mlock(2). (Предел определен в версиях BSD UNIX.) | При превышении предела системный вызов mlock(2) завершится с ошибкой EAGAIN . |
В заключение приведем пример программы, выводящий на экран установленные ограничения для процесса:
#include
#include
/* Процедура вывода на экран текущего и максимального
пределов потребления ресурса resource */
void disp_limit(int resource, char *rname) {
struct rlimit rim;
getrlimit(resource, &rlm);
printf("%-13s ", rname);
/* Значение изменяемого ограничения */
if (rlm.rlim_curr == RLIM_INFINITY)
printf("infinite ");
else
printf("%101d ", rlm.rlim_cur);
/* Значение жесткого ограничения */
if (rlm.rlim_max == RLIM_INFINITY)
printf(«infinite n»);
else
printf(«%10ldn», rlm.rlim_max);
}
main() {
disp_limit(RLIMIT_CORE, «RLIMIT_CORE»);
disp_limit(RLIMIT_CPU, «RLIMIT_CPU»);
disp_limit(RLIMIT_DATA, «RLIMIT_DATA»);
disp_limit(RLIMIT_FSIZE, «RLIMIT_FSIZE»);
disp_limit(RLIMIT_NOFILE, «RLIMIT_NOFILE»);
disp_limit(RLIMIT_STACK, «RLIMIT_STACK»);
/* BSD */
#ifdef RLIMIT_NPROC
disp_limit(RLIMIT_NPROC, «RLIMIT_NPROC»);
#endif
/* BSD */
#ifdef RLIMIT_RSS
disp_limit(RLIMIT_RSS, «RLIMIT_RSS»);
#endif
/* BSD */
#ifdef RLIMIT_MEMLOCK
disp_limit(RLIMIT_MEMLOCK, «RLIMIT_MEMLOCK»);
#endif
/* System V */
#ifdef RLIMIT_VMEM
disp_limit(RLIMIT_VMEM, «RLIMIT_VMEM»);
#endif
}
Запуск программы под управлением операционной системы Solaris 2.5 даст следующие результаты:
$ а.out
RLIMIT_CORE infinite infinite
RLIMIT_CPU infinite infinite
RLIMIT_DATA 2147479552 2147479552
RLIMIT_FSIZE infinite infinite
RLIMIT_NOFILE 64 1024
RLIMIT_STACK 8388608 2147479552
RLIMIT_VMEM infinite infinite