Letysite.ru

IT Новости с интернет пространства
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Linux сигналы процессов

Часть 1. Основы работы с сигналами

Серия контента:

Этот контент является частью # из серии # статей: Работа с сигналами в Linux

Этот контент является частью серии: Работа с сигналами в Linux

Следите за выходом новых статей этой серии.

В современных операционных системах существует понятие межпроцессного взаимодействия (Inter-Process Communication – IPC) – это набор способов обмена данными между процессами и/или потоками. Одним из таких способов обмена служат сигналы. Концепцию сигналов поддерживает большинство операционных систем, но, например, Windows, не имеет их полноценной поддержки для использования в качестве одного из способов IPC – в подобных операционных системах сигналы лишь реализованы в стандартной библиотеке языка C.

Концепция сигналов

Сигналы способны в случайное время (асинхронно) прерывать процесс для обработки какого-либо события. Процесс может быть прерван сигналом по инициативе другого процесса или ядра. Ядро использует сигналы для извещения процессов о различных событиях, например о завершении дочернего процесса.

Сигналы имеют определенный жизненный цикл. Вначале сигнал создается – высылается процессом или генерируется ядром. Затем сигнал ожидает доставки в процесс-приемник. Принято считать, что период ожидания сигнала равен промежутку времени между созданием сигнала и действием, которое этот сигнал высылает. В конце жизненного цикла сигнала происходит его перехват (прием) процессом и выполнение связанных с сигналом действий.

Простые сигналы и надежные сигналы

Существует деление сигналов на простые и надежные.

Изначально были разработаны и использовались простые (ненадежные) сигналы. По своему принципу работы они похожи на канонический механизм обработки аппаратных прерываний в процессоре. Если процесс хочет особым образом обрабатывать некий сигнал, то он сообщает ядру об этом, указывая специальную функцию – обработчик сигнала. При доставке сигнала процессу ядро как можно скорее вызывает обработчик сигнала, прерывая работу процесса. По завершении работы обработчика выполнение процесса продолжается с того места, где он был прерван.

Вместо написания своей функции-обработчика можно просто указать ядру, что сигнал вызывает действие, принятое для него по умолчанию, или что сигнал банально игнорируется.

Вся эта концепция работы с сигналами выглядит достаточно хорошо, пока сигнал не приходит процессу в то время, когда он уже занят обработкой другого сигнала. Здесь и проявляются проблемы – повторно вызванный обработчик сигнала может испортить те разделяемые ресурсы (общие структуры данных и переменные), которые он использует. Кроме того, в случае прихода большого количества сигналов стек процесса может неограниченно увеличиваться, что может привести к сбоям в работе программы.

В процессе решения проблемы были разработаны надежные сигналы, которые стандартизованы в POSIX и используются по сей день. Далее в статье рассматриваются именно надежные сигналы.

Сигналы и системные вызовы

Сигнал может прийти к процессу в тот момент, когда процесс находится внутри какого-нибудь системного вызова, например, ожидает ввода данных в read(). В этом случае развитие событий может пойти по следующему пути: приложение не пытается перехватить сигнал и прерывается ядром (например, приход сигнала SIGTERM) – тогда терминал остается в нестандартной конфигурации, что может затруднить работу пользователя. Конечно, можно перехватить сигнал, очистить терминал в обработчике сигнала, а затем выйти, но достаточно сложно написать такой обработчик сигнала, который бы знал, что делала программа в момент прерывания, чтобы решить, следует ли выполнять очистку терминала, или нет.

На сегодняшний момент существует реализация сигналов, которая свободна от этих недостатков. Решение состоит в том, что в обработчике сигнала следует лишь установить флаг, обозначающий, что сигнал получен, а затем нужно обеспечить возвращение из системного вызова с кодом ошибки, обозначающим прерывание вызова сигналом. Далее программа должна проверить флаг, установленный обработчиком сигнала, и выполнить соответствующие действия, например, очистить терминал и завершиться.

Современная реализация сигналов заставляет медленные системные вызовы возвращать код ошибки EINTR, когда они прерываются приходящим сигналом. Быстрые системные вызовы должны завершаться перед тем, как сигнал будет доставлен. Медленный системный вызов – системный вызов, требующий неопределенного количества времени для своего завершения, например read(), wait(), write(). Все системные вызовы, зависящие от непредсказуемых ресурсов, таких как действия человека, сетевые данные и т.п., являются медленными. Естественно, все остальные системные вызовы – быстрые.

Обычно программа обрабатывает код ошибки EINTR и, если не произошло ничего фатального, перезапускает системный вызов. Большинство Unix-like операционных систем в настоящее время делает это по умолчанию – нужно лишь обработать сигнал в обработчике, а системный вызов будет перезапущен автоматически. В Linux по умолчанию системные вызовы не перезапускаются, но для каждого сигнала процесс может установить флаг, указывающий системе о необходимости перезапуска медленных системных вызовов, прерванных этим сигналом.

Посылка сигналов

Посылка сигналов от одного процесса к другому обычно осуществляется при помощи системного вызова kill(). Его первый параметр – PID процесса, которому посылается сигнал; второй параметр – номер сигнала. Если мы хотим послать сигнал SIGTERM процессу с PID 6666, то используем системный вызов kill() так:

Вместо положительного значения PID можно передать вызову равное по модулю, но отрицательное значение. Тогда сигнал будет послан всем процессам из группы с номером, равным модулю переданного PID’а. Если PID равен 0, то сигнал посылается всем процессам из группы, к которой относится и текущий процесс. Эти возможности используются в основном оболочками для управления заданиями.

Если в качестве PID передать в системный вызов kill() значение -1, то сигнал будет послан всем процессам за исключением init’а. Такая возможность применяется для завершения работы системы.

Более подробная информация по системному вызову kill() приведена в man 2 kill.

Послать сигнал самому себе процесс может при помощи системного вызова raise(), который принимает один параметр – номер сигнала. Пример:

Естественно, каждый из рассмотренных системных вызовов возвращает ноль в случае успешного выполнения и ненулевое значение, если произошла какая-нибудь ошибка.

Перехват сигналов

Все программы, подчиняющиеся стандарту POSIX, регистрируют свои обработчики сигналов при помощи системного вызова sigaction(). Этот системный вызов имеет три параметра: первый – int signum – номер перехватываемого сигнала. Второй – struct sigaction * act – указатель на структуру, описывающую правила установки обработчика. Третий параметр – struct sigaction * oact – принимает уже установленные правила обработчика сигнала. Либо второй, либо третий (но не оба сразу!) параметр можно установить в NULL при необходимости.

Структура struct sigaction имеет следующее описание:

sa_handler – указатель на обработчик сигнала, причем обработчик должен быть объявлен следующим образом:

где единственный параметр – номер сигнала, который попал в обработчик. sa_handler также может быть равным SIG_IGN – сигнал игнорируется процессом, и SIG_DFL – сигнал вызывает действие по умолчанию, например прерывание процесса.

sa_mask – набор сигналов, которые должны блокироваться при вызове заданного в этой же структуре обработчика сигнала. Как их устанавливать, рассматривается далее.

Параметр sa_flags позволяет процессу модифицировать поведение сигнала. Параметр может принимать всего четыре значения, которые, впрочем, можно объединять при помощи битовой операции «ИЛИ»:

  1. SA_NOCLDSTOP – отсылать сигнал SIGCHLD только в случае прерывания дочернего процесса. Приостановка дочернего процесса не вызывает посылки сигнала.
  2. SA_NODEFER – эмуляция простых (ненадежных) сигналов.
  3. SA_RESTHAND – после прихода сигнала его обработчик сбрасывается в SIG_DFL.
  4. SA_RESTART – перезапуск системного вызова после возврата из обработчика сигнала. Если флаг не установлен, то системный вызов возвращает ошибку EINTR.

Как обычно, при успешном выполнении sigaction() возвращается 0, а в случае ошибки – отрицательное значение.

Маска сигналов процесса

Добавление сигналов в структуру sigset_t sa_mask, ее очистка и т.п. осуществляются при помощи набора функций sigemptyset(), sigfillset(), sigaddset(), sigdelset(). Первые две функции принимают один параметр – указатель на структуру sigset_t. Эти функции очищают и заполняют всеми возможными сигналами структуру sigset_t соответственно.

Последние две функции, соответственно, добавляют и удаляют один определенный сигнал из структуры и имеют по два параметра. Их первый параметр – указатель на структуру sigset_t, а второй – номер сигнала.

Все рассмотренные выше функции возвращают 0 при успешном завершении и число, не равное нулю, – при ошибке.

Кроме того, существует еще одна функция, проверяющая, находится ли указанный сигнал в указанном наборе – sigismember(). Ее параметры совпадают с параметрами sigaddset(). Функция возвращает 1, если сигнал находится в наборе, 0 – если не находится, и отрицательное число – при возникшей ошибке.

Помимо всего прочего, мы можем задать список сигналов, доставка которых процессу будет заблокирована. Это выполняется при помощи функции sigprocmask(int how, const sigset_t * set, sigset_t * oldset).

Первый ее параметр описывает то, что должно выполняться:

  1. SIG_BLOCK – сигналы из набора set блокируются;
  2. SIG_UNBLOCK – сигналы из набора set разблокируются;
  3. SIG_SETMASK – сигналы из набора set блокируются, остальные разблокируются.

Второй параметр является указателем на тот самый набор, сигналы из которого блокируются/разблокируются. Если он равен NULL, то значение первого параметра игнорируется системным вызовом.

Третий параметр – указатель на уже используемую маску сигналов; его можно поставить в NULL, если эти данные не нужны.

Для получения списка ожидающих сигналов можно использовать функцию sigpending(), которая принимает единственный параметр – указатель на структуру sigset_t, куда будет записан набор ожидающих сигналов.

Принципы написания обработчиков сигналов

Одно из самых главных правил написания обработчиков сигналов – обработчик должен быть реентерабельным, т.е. он должен допускать свой повторный вызов, когда процесс уже находится в обработчике. Нужно заботиться о том, чтобы обработчик сигналов не использовал глобальные структуры данных или медленные системные вызовы. Если избежать этого, увы, невозможно, то стоит позаботиться о защите от повторного вызова обработчика во время работы со структурой данных или с системным вызовом. Добиться этого можно, заблокировав на время доставку сигнала, обработчик которого сейчас работает, при помощи системного вызова sigprocmask(). Например, мы имеем обработчик сигнала SIGCHLD, выполняем в нем блокировку так:

Читать еще:  Sticky bit linux что это

Кроме всего перечисленного выше, считается, что обработчик должен быть максимально простым – в идеале он должен выставлять некий флаг и завершаться, а все остальное должна выполнять основная часть программы.

Заключение

Приведенный материал является базовым для понимания концепции сигналов. В принципе, его должно хватить, чтобы вы уже начали использовать работу с сигналами в своих программах.

В завершение цикла будет рассказано о том, как получить (и отправить) дополнительные данные о сигнале, если вам не хватает обычной информации о том, что сигнал откуда-то пришел в ваш процесс.

3.3.2. Сигналы

Механизм сигналов — это средство, позволяющее сообщать процессам о некоторых событиях в системе, а процессу-получателю — должным образом на эти сообщения реагировать. Послать сигнал может сам процесс (например, при попытке деления на ноль), ядро (при сбое оборудования), пользователь или другой процесс (требуя прервать выполнение задачи).

Всего в Linux 63 сигнала, обозначаемых своими номерами или символическими именами. Имена всех сигналов начинаются с SIG, и эту приставку часто опускают: так, сигнал, требующий прекратить выполнение процесса, называется SIGKILL, или KILL, или сигнал 9.

Получив сигнал, процесс может: игнорировать его; вызвать для обработки установленную по умолчанию функцию; вызвать собственный обработчик (перехватить сигнал). Некоторые сигналы (например, KILL) перехватить или игнорировать невозможно.

Пользователь может послать сигнал процессу с идентификатором PID командой

где — это номер или символическое имя.

Несколько часто встречающихся сигналов перечислены в таблице 3.1. Полный список можно получить по команде kill -l (list).

Сигналы Linux Таблица 3.1

Некоторые сигналы посылаются по нажатии комбинации клавиш. Так, Ctrl+C посылает сигнал INT, a Ctrl+ (обратный слэш) — сигнал QUIT. Получает эти сигналы тот процесс, который сейчас занимает консоль — например, ожидает вашего ввода.

Команда kill носит такое убийственное название потому, что чаще всего используется для принудительного завершения процессов, вышедших из-под контроля, забирающих много ресурсов или просто повисших. По умолчанию она посылает сигнал TERM. Он отличается от сигнала KILL тем, что приказывает процессу завершиться аккуратно, закрыв открытые им файлы, удалив временные и т.п. Сигнал же KILL действует на процесс как выстрел в голову.

Понятно, что для того, чтобы прервать выполнение процесса, нужно быть его хозяином или иметь привилегии суперпользователя.

Данный текст является ознакомительным фрагментом.

Читать книгу целиком

Похожие главы из других книг:

Глава 10 Сигналы

Глава 10 Сигналы Данная глава освещает все подробности сигналов, важную, но сложную часть GNU/Linux

12.3. Доступные сигналы

12.3. Доступные сигналы Linux предоставляет в распоряжение процессов сравнительно немного сигналов, и все они собраны в табл. 12.1.Таблица 12.1. Сигналы Сигнал Описание Действие по умолчанию SIGABRT Доставляется вызовом abort(). Прервать, сбросить дамп SIGALRM Истек срок действия

Сигналы

Сигналы Сигналы являются способом передачи от одного процесса другому или от ядра операционной системы какому-либо процессу уведомления о возникновении определенного события. Сигналы можно рассматривать как простейшую форму межпроцессного взаимодействия. В то же

Надежные сигналы

Надежные сигналы Стандарт POSIX. 1 определил новый набор функций управления сигналами. основанный на интерфейсе 4.2BSD UNIX и лишенный рассмотренных выше недостатков.Модель сигналов, предложенная POSIX, основана на понятии набора сигналов (signal set), описываемого переменной типа

Сигналы

Сигналы В некотором смысле сигналы обеспечивают простейшую форму межпроцессного взаимодействия, позволяя уведомлять процесс или группу процессов о наступлении некоторого события. Мы уже рассмотрели в предыдущих главах сигналы с точки зрения пользователя и

7.2 СИГНАЛЫ

7.2 СИГНАЛЫ Сигналы сообщают процессам о возникновении асинхронных событий. Посылка сигналов производится процессами — друг другу, с помощью функции kill, — или ядром. В версии V (вторая редакция) системы UNIX существуют 19 различных сигналов, которые можно классифицировать

5.8.2. Сигналы

5.8.2. Сигналы Демон syslogd реагирует на следующие сигналы: SYGTERM, SIGINT, SIGQUIT, SIGHUP, SIGUSR1, SIGCHLD. Реакция демона на сигналы описана в табл. 5.8.Реакция демона на сигналы Таблица 5.8 Сигнал Реакция SIGTERM Завершает работу демона SIGINT, SIGQUIT Завершает работу демона, если выключена отладка

3.3.2. Сигналы

3.3.2. Сигналы Механизм сигналов — это средство, позволяющее сообщать процессам о некоторых событиях в системе, а процессу-получателю — должным образом на эти сообщения реагировать. Послать сигнал может сам процесс (например, при попытке деления на ноль), ядро (при сбое

27.3.10. Сигналы и сокеты

27.3.10. Сигналы и сокеты С сокетами связаны три сигнала:? SIGIO — сокет готов к вводу/выводу. Сигнал посылается процессу, который связан с сокетом;? SIGURG — сокет получил экспресс-данные (мы их использовать не будем, поэтому особо останавливаться на них нет смысла);? SIGPIPE — запись

7.2.6.2. Сигналы

7.2.6.2. Сигналы Самый простой и грубый способ сообщения между двумя процессами на одной машине заключается в том, что один из них отправляет другому какой-либо сигнал (signal). Сигналы в операционной системе Unix представляют собой форму программного прерывания. Каждый сигнал

7.2.6.2. Сигналы

7.2.6.2. Сигналы Самый простой и грубый способ сообщения между двумя процессами на одной машине заключается в том, что один из них отправляет другому какой-либо сигнал (signal). Сигналы в операционной системе Unix представляют собой форму программного прерывания. Каждый сигнал

3.3. Сигналы

3.3. Сигналы Сигналы — это механизм связи между процессами в Linux. Данная тема очень обширна, поэтому здесь мы рассмотрим лишь наиболее важные сигналы и методики управления процессами.Сигнал представляет собой специальное сообщение, посылаемое процессу. Сигналы являются

Linux сигналы процессов

В таблицах далее есть столбец «Действие», в котором указан обработчик по умолчанию для каждого сигнала:

Term Действие по умолчанию — завершение процесса. Ign Действие по умолчанию — игнорирование сигнала. Core Действие по умолчанию — завершение процесса и вывод дампа в файл (смотрите core(5)). Stop Действие по умолчанию — остановка процесса. Cont Действие по умолчанию — продолжение работы процесса, если он в данный момент остановлен.

Процесс может изменить обработчик сигнала с помощью sigaction(2) или signal(2) (менее переносим; дополнительную информацию смотрите в signal(2)). Используя данные системные вызовы процесс может выбрать одно из следующих действий при получении сигнала: выполнить действие по умолчанию, игнорировать сигнал, поймать сигнал обработчиком сигнала — функцией, задаваемой программистом, которая автоматически вызывается при получении сигнала (по умолчанию обработчик сигнала использует обычный стек процесса. Возможно сделать так, чтобы обработчик сигнала использовал альтернативный стек; как это делается и когда это может быть полезно смотрите в sigaltstack(2)).

Реакция на сигналы является атрибутом процесса: в многонитиевом приложении реакция на определённый сигнал одинакова для всех нитей.

Потомок, созданный с помощью fork(2), наследует реакцию на сигналы от своего родителя. При execve(2) реакция на сигналы устанавливается в значение по умолчанию; реакция на игнорируемые сигналы не изменяется.

Отправка сигнала

Ожидание сигнала для обработки

Синхронный приём сигнала

Сигнальная маска и ожидающие сигналы

В каждой нити процесса имеется независимая сигнальная маска, определяющая набор сигналов, которые нить, в данный момент, блокирует. Нить может управлять сигнальной маской с помощью pthread_sigmask(3). В обычном однонитиевом приложении для работы с сигнальной маской можно использовать вызов sigprocmask(2).

Потомок, создаваемый с помощью fork(2), наследует копию родительской маски сигналов; маска сигналов сохраняется при вызове execve(2).

Сигнал может быть сгенерирован (а значит и стать ожидающим) как для всего процесса (например, при отправке с помощью kill(2)) так и для отдельной нити (например, некоторые сигналы, такие как SIGSEGV и SIGFPE, сгенерированные в следствии выполнения определённой инструкции на машинном языке в самой нити, или сигналы, направленные определённой нити с помощью pthread_kill(3)). Направленный процессу сигнал может быть доставлен в любую из нитей, у которых сигнал не заблокирован. Если имеется несколько таких нитей, то ядро выбирает произвольную нить, которой и доставит сигнал.

Нить может получить набор сигналов, которые находятся в состоянии ожидания с помощью вызова sigpending(2). Этот набор будет состоять из объединения набора ожидающих сигналов, направленных процессу, и набора ожидающих сигналов для вызвавшей нити.

Потомок, созданный с помощью fork(2), первоначально имеет пустой набор ожидающих сигналов; набор ожидающих сигналов сохраняется при вызове execve(2).

Стандартные сигналы

Сначала рассмотрим сигналы, описанные в стандарте POSIX.1-1990.

Сигналы SIGKILL и SIGSTOP нельзя поймать, заблокировать или проигнорировать.

Далее приведены сигналы, не входящие в POSIX.1-1990, но описанные в SUSv2 и POSIX.1-2001.

В Linux до версии 2.2 включительно поведением по умолчанию для сигналов SIGSYS, SIGXCPU, SIGXFSZ и SIGBUS (на всех архитектурах кроме SPARC и MIPS) было завершение процесса без создания дампа (в некоторых системах UNIX действием по умолчанию для SIGXCPU и SIGXFSZ является завершение процесса без создания дампа). Linux версии 2.4 соответствует требованиям POSIX.1-2001 для этих сигналов и завершает процесс с созданием дампа.

Читать еще:  Ошибка сети код 2

Некоторые другие сигналы.

Сигнал с номером 29 на alpha соответствует SIGINFO / SIGPWR, а на sparc соответствует SIGLOST.

Сигнал SIGEMT не определён в POSIX.1-2001, но, тем не менее, появляется почти во всех системах UNIX, где действием по умолчанию для него является завершение процесса с созданием дампа.

Сигнал SIGPWR (не определён в POSIX.1-2001) по умолчанию, обычно, игнорируется (в других системах UNIX).

Для сигнала SIGIO (не определён в POSIX.1-2001) в других системах UNIX действием по умолчанию является игнорирование.

Если определён сигнал SIGUNUSED, то он является синонимом SIGSYS для большинства архитектур.

Сигналы реального времени

Ядро Linux поддерживает 33 таких сигнала, начиная с номера 32 до номера 64. Однако внутри реализации нитей POSIX в glibc используется два (для NPTL) или три (для LinuxThreads) сигнала реального времени (смотрите pthreads(7)), а значение SIGRTMIN корректируется должным образом (до 34 или 35). Так как диапазон доступных сигналов реального времени различается в зависимости от реализации нитей в glibc (и это может происходить во время выполнения при смене ядра и glibc), и, более того, диапазон сигналов реального времени различен в разных системах UNIX, то программы никогда не должны задавать сигналы реального времени по номерам, а вместо этого всегда должны записывать их в виде SIGRTMIN+n и выполнять проверку (во время выполнения), что SIGRTMIN+n не превышает SIGRTMAX.

В отличие от стандартных сигналов, сигналы реального времени не имеют предопределенного назначения: весь набор сигналов реального времени приложения могут использовать так, как им нужно.

Действием по умолчанию для необработанных сигналов реального времени является завершение процесса (terminate).

Сигналы реального времени отличаются от обычных в следующем:

1. В очередь можно добавлять несколько экземпляров одного сигнала реального времени. В случае со стандартными сигналами, если доставляется несколько экземпляров сигнала, в то время как этот тип сигнала в данный момент заблокирован, то только один экземпляр будет добавлен в очередь. 2. Если сигнал отправляется с помощью sigqueue(3), то с сигналом может быть отправлено некоторое значение (целочисленное, либо указатель). Если принимающий процесс устанавливает обработчик для сигнала, используя флаг SA_SIGINFO и вызов sigaction(2), то он может получить это значение через поле si_value структуры siginfo_t, переданной обработчику в виде второго аргумента. Кроме этого, поля si_pid и si_uid данной структуры можно использовать для получения идентификатора процесса и реального идентификатора пользователя, отправившего сигнал. 3. Сигналы реального времени доставляются точно в порядке поступления. Несколько сигналов одного типа доставляются в порядке, определяемых их отправлением. Если процессу отправлено несколько разных сигналов реального времени, то порядок их доставки начинается с сигнала с наименьшим номером (то есть сигналы с наименьшим номером имеют наивысший приоритет). Порядок же для стандартных сигналов в такой ситуации не определён.

Если процессу передан и стандартный сигнал, и сигнал реального времени, то в POSIX однозначно не определено, какой из них будет доставлен первым. В Linux, как и во многих других реализациях в таких случаях, отдан приоритет стандартным сигналам.

В соответствии с POSIX, реализация должна позволять ставить в очередь процесса как минимум _POSIX_SIGQUEUE_MAX (32) сигнала. Однако в Linux это делается по-другому. В ядрах до версии 2.6.7 включительно, Linux накладывает общесистемный лимит на количество сигналов режима реального времени в очереди для всех процессов. Этот лимит может быть получен и изменён (если есть права) через файл /proc/sys/kernel/rtsig-max. Текущее количество сигналов режима реального времени в очереди можно получить из файла /proc/sys/kernel/rtsig-nr. В Linux 2.6.8 данные интерфейсы /proc были заменены на ограничение ресурса RLIMIT_SIGPENDING, которое устанавливает ограничение на очередь сигналов на каждого пользователя отдельно; дополнительную информацию можно найти в setrlimit(2).

Для дополнительных сигналов или сигналов реального времени требуется расширение структуры набора сигналов (sigset_t) с 32 до 64 бит. В связи с этим, различные системные вызовы заменены на новые системные вызов, поддерживающие набор сигналов большего размера. Вот соответствие старых и новых системных вызовов:

Linux сигналы процессов

Сухая формулировка говорит нам что процесс это — совокупность программного кода и данных, загруженных в память ЭВМ. На первый взгляд процесс — это запущенная программа (приложение) или команда. Но это не совсем так. Некоторые приложения могут создавать несколько процессов одновременно.

Код процесса не обязательно должен выполняться в текущий момент времени, так как процесс может находиться в состоянии спящего. В этом случае выполнение кода такого процесса приостановлено. Существует всего 3 состояния, в которых может находиться процесс:

Работающий процесс — в данный момент код процесса выполняется.

Спящий процесс — в данный момент код процесса не выполняется в ожидании какого либо события (нажатия клавиши на клавиатуре, поступление данных из сети и т.д.)

Процесс-зомби — сам процесс уже не существует, его код и данные выгружены из оперативной памяти, но запись в таблице процессов остается по тем или иным причинам.

Каждому процессу в системе назначаются числовые идентификаторы (личные номера) в диапазоне от 1 до 65535 (PID — Process Identifier — идентификатор процесса) и идентификаторы родительского процесса (PPID — Parent Process Identifier — идентификатор родительского процесса). PID является именем процесса, по которому мы можем адресовать процесс в операционной системе при использовании различных средств просмотра и управления процессами. PPID определяет родственные отношения между процессами, которые в значительной степени определяют его свойства и возможности. Другие параметры, которые необходимы для работы программы, называют «окружение процесса». Одним из таких параметров является управляющий терминал — имя терминального устройства, на которое процесс выводит информацию и с которого информацию получает. Управляющий терминал имеют далеко не все процессы. Процессы, не привязанные к какому-то конкретному терминалу называются «демонами» (daemons). Такие процессы, будучи запущенными пользователем, не завершают свою работу по окончании сеанса, а продолжают работать, так как они не связаны никак с текущим сеансом и не могут быть автоматически завершены. Как правило, с помощью демонов реализуются серверные службы, так например сервер печати реализован процессом-демоном cupsd, а сервер журналирования — syslogd.

Для просмотра списка процессов в Linux существует команда ps. Формат команды следующий:

ps [PID] [options] — просмотр списка процессов. Без параметров ps показывает все процессы, которы были запущены в течение текущей сессии, за исключением демонов. Options может принимать одно из следующих значений или их комбинации:

-а или -e — показать все процессы

-f — полный листинг

-w — показать полные строки описания процессов. Если они превосходят
длину экрана, то перенести описание на следующую строку.

Это далеко не все параметры команды ps. Остальные параметры Вы можете узнать, просто набрав man ps.

Пример1:

[gserg@WEBMEDIA gserg]$ ps

PID TTY TIME CMD

3126 pts/2 00:00:00 bash

3158 pts/2 00:00:00 ps

Пример2:

[gserg@WEBMEDIA gserg]$ ps 3126

PID TTY STAT TIME COMMAND

3126 pts/2 S 0:00 /bin/bash

Пример3:

[gserg@WEBMEDIA gserg]$ ps -ef

UID PID PPID C STIME TTY TIME CMD

root 1 0 0 10:01 ? 00:00:03 init [5]

root 2 1 0 10:01 ? 00:00:00 [keventd]

root 3 1 0 10:01 ? 00:00:00 [kapmd]

root 4 1 0 10:01 ? 00:00:00 [ksoftirqd_CPU0]

root 5 1 0 10:01 ? 00:00:24 [kswapd]

root 6 1 0 10:01 ? 00:00:00 [bdflush]

gserg 3126 3124 0 17:56 pts/2 00:00:00 /bin/bash

gserg 3160 3126 0 17:59 pts/2 00:00:00 ps -ef

Пример4:

[gserg@WEBMEDIA gserg]$ ps -efw

UID PID PPID C STIME TTY TIME CMD

root 1 0 0 10:01 ? 00:00:03 init [5]

root 2 1 0 10:01 ? 00:00:00 [keventd]

root 3 1 0 10:01 ? 00:00:00 [kapmd]

root 4 1 0 10:01 ? 00:00:00 [ksoftirqd_CPU0]

root 5 1 0 10:01 ? 00:00:24 [kswapd]

root 1130 1 0 10:02 ? 00:00:00 /usr/sbin/apmd -p 10 -w 5 -W -P /etc/sysconfig/apm-scripts/apmd_proxy

gserg 3172 3126 0 18:01 pts/2 00:00:00 ps -efw

Процессы в ОС Linux обладают теми же правами, которыми обладает пользователь, от чьего имени был запущен процесс.

На самом деле операционная система воспринимает работающего в ней пользователя как набор запущенных от его имени процессов. Ведь и сам сеанс пользователя открывается в командной оболочке (или оболочке Х) от имени пользователя. Поэтому когда мы говорим «права доступа пользователя к файлу» то подразумеваем «права доступа процессов, запущенных от имени пользователя к файлу».

Для определения имени пользователя, запустившего процесс, операционная система использует реальные идентификаторы пользователя и группы, назначаемые процессу. Но эти идентификаторы не являются решающими при определении прав доступа. Для этого у каждого процесса существует другая группа идентификаторов — эффективные.

Как правило, реальные и эффективные идентификаторы процессов одинаковые, но есть и исключения. Например, для работы утилиты passwd необходимо использовать идентификатор суперпользователя, так как только суперпользователь имеет права на запись в файлы паролей. В этом случае эффективные идентификаторы процесса будут отличаться от реальных. Возникает резонный вопрос — как это было реализовано?

У каждого файла есть набор специальных прав доступа — биты SUID и SGID. Эти биты позволяют при запуске программы присвоить ей эффективные идентификаторы владельца и группы-владельца соответственно и выполнять процесс с правами доступа другого пользователя. Так как файл passwd принадлежит пользователю root и у него установлен бит SUID, то при запуске процесс passwd будет обладать правами пользователя root.

Устанавливаются биты SGID и SUID командой chmod:

Читать еще:  Распознавание речи linux

chmod u+s filename — установка бита SUID

chmod g+s filename — установка бита SGID

Мы с вами рассмотрели понятие процесса, способы отображения процессов и права доступа. Но для комфортной работы в операционной системе этого, согласитесь, мало. Необходимо еще эффективно управлять процессами. А для реализации управления мы сначала рассмотри строение таблицы процессов:

Родителем всех процессов в системе является процесс init. Его PID всегда 1, PPID — 0. Всю таблицу процессов можно представить себе в виде дерева, в котором корнем будет процесс init. Этот процесс хоть и не является частью ядра, но выполняет в системе очень важную роль — определяет текущий уровень инициализации системы и следит чтобы были запущены программы, позволяющие пользователю общаться с компьютером (mingetty, X или другие).

Процессы, имена которых заключены в квадратные скобки, например «[keventd]» — это процессы ядра. Эти процессы управляют работой системы, а точнее такими ее частями, как менеджер памяти, планировщик времени процессора, менеджеры внешних устройств и так далее.

Остальные процессы являются пользовательскими, запущенными либо из командной строки, либо во время инициализации системы.

Жизнь каждого процесса представлена следующими фазами:

Создание процесса — на этом этапе создается полная копия того процесса, который создает новый. Например, вы запустили из интерпретатора на выполнение команду ls. Командный интерпретатор создает свою полную копию.

Загрузка кода процесса и подготовка к запуску — копия, созданная на первом этапе заменяется кодом задачи, которую необходимо выполнить и создается ее окружение — устанавливаются необходимые переменные и т.п.

Выполнение процесса

Состояние зомби — на этом этапе выполнение процесса закончилось, его код выгружается из памяти, окружение уничтожается, но запись в таблице процессов еще остается.

Умирание процесса — после всех завершающих стадий удаляется запись из таблицы процессов — процесс завершил свою работу.

Во время работы процесса, ядро контролирует его состояние, и в случае возникновения непредвиденной ситуации управляет процессом с помощью посылки ему сигнала. Сигнал — это простейший способ межпроцессорного (то есть между процессами) взаимодействия. Существует несколько типов сигналов. Для каждого из типов предусмотрено действие по умолчанию. Процесс может воспользоваться действием по умолчанию, или, если у него есть обработчик сигнала, то он может перехватить и обработать или игнорировать сигнал. Сигналы SIGKILL и SIGSTOP невозможно ни перехватить, ни игнорировать.

По умолчанию возможны несколько действий:

игнорировать — продолжать работу, несмотря на то, что получен сигнал.

завершить — завершить работу процесса.

завершить + core — завершить работу процесса и создать файл в текущем каталоге с именем core, содержащий образ памяти процесса (код и данные).

остановить — приостановить выполнение процесса, но не завершать его работу и не выгружать код из памяти.

Вот список всех сигналов, существующих в системе на сегодняшний день:

Механизм сигналов Linux

Механизм сигналов signal является простейшей формой межпроцессного взаимодействия и предназначен для внешнего управления процессами. Каждый сигнал имеет свой обработчик, определяющий поведение процесса при отсылке ему этого сигнала. Этот обработчик является некоторым набором инструкций программы (подпрограммой), которым передается управление при доставке сигнала процессу. Каждому процессу назначаются обработчики «по умолчанию», в большинстве случаев приводящие к завершению процесса.

В листинге ниже показано применение сигнала штатного прерывания процесса №2 (SIGINT), отсылаемого драйвером терминала всем процессам «переднего» фона при получении символа ^C (ETX), т. е. нажатии клавиш Ctrl+C о на этом терминале. Для процессов «заднего» фона сигнал может быть отослан явно, при помощи команды kill, предназначенной, несмотря на ее название (в большинстве случаев обработчик завершит процесс, отсюда и название), для отсылки сигналов.

Штатное прерывание процесса (^C, Intr, SIGINT)

$ stty -а

speed 38400 baud; rows 24; columns 80; line = 0;

intr ^C; quit = ^; erase ^?; kill = ^U; eof = ^D; eol= M-^7; eol2 = M-^7;

isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt

$ dd if=/dev/zero of=/dev/null

^C782349+0 записей получено

782349+0 записей отправлено

скопировано 400562688 байт (401 МВ), 0,531532 с, 754 МВ/с

$ dd if=/dev/zero of=/dev/null &

$ jobs -l

[1]+ 23418 Выполняется dd if=/dev/zero of=/dev/null &

$ kill -SIGINT 23418

$ 25318811+0 записей получено

25318810+0 записей отправлено

скопировано 12963230720 байт (13 GB), 17,1001 с, 758 МВ/с

[1]+ Прерывание dd lf=/dev/zero of=/dev/null

Нужно отметить, что настройки драйвера терминала позволяют как переопределить символ, получение которого приведет к выполнению действия intr (посылка сигнала SIGINT), так и совсем выключить отсылку подобных сигналов управления процессами при получении управляющих символов.

В листинге ниже показано аналогичное применение сигнала аварийного завершения процесса №3 (SIGQUIT), посылаемого процессам «переднего» фона при получении драйвером управляющего символа ^(FS), генерируемого терминалом при нажатии Ctrl + . Обработчик сигнала не только завершит процесс, но и попытается (если позволяют настройки) сохранить дамп памяти процесса в файл core для последующей отладки.

Аварийное прерывание процесса (^, quit, SlGQUIT)

$ dd if=/dev/zero of=/dev/null

^Выход (сделан дамп памяти)

$ dd if=/dev/zero of=/dev/null &

$ kill -SIGQUIT 23429

[1]+ Выход (подготовлен дамп ядра) dd if=/dev/zero of=/dev/null

Некоторые процессы (например, демоны, или графические приложения) не имеют управляющего терминала, поэтому не могут быть завершены интерактивно при помощи Ctrl+C или Ctrl+. В этом случае используется сигнал штатного завершения № 15 (SIGTERM), что проиллюстрировано в листинге ниже.

Штатное завершение процесса (SIGTERM)

$ dd if=/dev/zero of=/dev/null &

$ kill -SIGTERM 23444

[1]+ Завершено dd if=/dev/zero of=/dev/null

Для приостановки процесса, т. e. временного, исключения его из процедур распределения процессорного времени планировщиком, предназначен сигнал № 19 (SIGSTOP), а для возобновления процесса — сигнал № 18 (SIGCONT). В листинге ниже показано, что приостановленный (sTopped) процесс не потребляет процессорного времени.

Приостановка и возобновление процесса (SIGSTOP и SIGCONT)

$ pbzip2 big &
[1] 1647

$ top -b -n1 -p 1647
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1647 fitz 20 0 102m 33m 900 S 387 0.4 0:25.09 pbzip2

$ pkill -STOP pbzip2

$ ps f

PID TTY STAT TIME COMMAND
1640 pts/2 S 0:00 -bash
1647 pts/2 T 0:24 _ pbzip2 big
1651 pts/2 R+ 0:00 _ ps f
fitz@ubuntu:

$ jobs -l
[1]+ 1647 Остановлено (сигнал) pbzip2 big
fitz@ubuntu:

$ top -b -n1 -p 1647

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1647 fitz 20 0 102m 34m 900 T 0 0.4 0:42.90 pbzip2

$ kill -CONT 1647

$ top -b -nl -p 1647

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND

1647 fitz 20 0 102m 34m 900 S 370 0.4 0:51.82 pbzip2

Сигналы могут быть перехвачены, если процесс назначит собственный обработчик, а также проигнорированы, тогда при их доставке вообще никакой обработчик не вызывается. Исключение составляют некоторые «безусловные» «сигналы, такие как № 9 (SIGKILL) — безусловное завершение или № 19 (SIGSTOP) безусловная приостановка процесса.

Игнорирование и перехват сигналов показаны в листинге ниже на примере командного интерпретатора bash. Ни попытки «интерактивного» завершения при помощи сигналов SIGINT и SIGQUIT, ни явная посылка сигналов SIGINT и SIGTERM не приводят к завершению командного интерпретатора. К желаемому результату приводит только явная отсылка сигнала SIGKILL.

Игнорирование и перехват сигналов

$ ps f

PID TTY STAT TIME COMMAND

23025 pts/1 S 0:00 -bash
23771 pts/1 R+ 0:00 _- ps f

$ bash

$ ps f

PID TTY STAT TIME COMMAND

23025 pts/1 S 0:00 -bash
23636 pts/1 S 0:00 _ bash
23692 pts/1 R+ 0:00 _ ps f

$ ^C

$ ps f

PID TTY STAT TIME COMMAND

23025 pts/1 S 0:00 -bash
23636 pts/1 S 0:00 _ bash
23692 pts/1 R+ 0:00 _ ps f

$ kill -SIGINT 23636

$ ps f

PID TTY STAT TIME COMMAND

23025 pts/1 S 0:00 -bash

23636 pts/1 S 0:00 _ bash
23701 pts/1 R+ 0:00 _ ps f

$ kill -SIGKILL 23636

$ ps f

PID TTY STAT TIME COMMAND

23025 pts/1 S 0:00 -bash
23771 pts/1 R+ 0:00 _ ps f

Диспозицию сигналов, т. е. информацию об игнорировании (IGNORED), перехвате (CAUGHT), временной блокировке (BLOCKED) или ожидании доставки (PENDING) сигналов процессов можно получить при помощи команды ps(1), как показано в листинге ниже.

Диспозиция изображается битовой маской, где каждый N-Pi бит маски (нумерация от младших к старшим) соответствует сигналу N. Например, командный интерпретатор игнорирует сигналы, представленные (шестнадцатеричной) маской 0038400416, что в двоичном представлении составляет 11100001000000000001002 и указывает на игнорируемые 3-й, 15-й, 20-й, 21-й и 22-й сигналы т. е. SIGQUIT, SIGTERM, SIGTSTP, SIGTTIN и SIGTTOU.

Для перевода из шестнадцатеричного в двоичное представление .использован стековый калькулятор dc, которому было велено из входной i (input) системы счисления по основанию 16 в выходную о (output) систему счисления по основанию 2 напечатать p (print) число 00384004, а для просмотра имен сигналов по их номерам использована встроенная команда kill.

Диспозиция сигналов

$ ps s
UID PID PENDING BLOCKED IGNORED CAUGHT STAT TTY TIME COMMAND
1006 23025 00000000 00010000 00384004 4b813efb S pts/5 0:00 -bash
1006 23773 00000000 00000000 00000000 73d3fef9 R+ pts/5 0:00 ps s

$ dc -e 16i2o00384004p

1110000100000000000100
222128 15 87654321

$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SKILL 5) SIGTRAP
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SlGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
63) SIGRTMAX-164) SIGRTMAX

Ссылка на основную публикацию
Adblock
detector