- Команда kill
- Описание команды kill
- Синтаксис
- Опции
- Примеры использования команды kill
- Определить PID процесса
- Вывести список сигналов
- Отправка сигнала SIGTERM
- Отправка сигнала KILL (завершение процесса)
- Отправка сигнала нескольким процессам
- Практика работы с сигналами
- Функция обработчик сигналов
- Блокирование сигналов
- sigwait
- Посыл сигнала
- Пример использования сигналов
- Сигналы, группы, сеансы
- Сигнал, маска, обработчик
- Примеры использования сигналов
- Список сигналов
- Posix.1-1990
- Отправка сигнала
- Исторический способ обработки сигнала — signal
- Реакция на сигнал — sigaction
- Параметры для установки обработчика сигнала через sigaction()
- Данные, передаваемые в обработчик сигнала sa_sigaction()
- Блокирование сигналов
- Манипулирование маской
- Проверка наличия заблокированных сигналов
- Очистка заблокированных сигналов
- Управляющий терминал, сеанс, группы
- Управляющий терминал, сеанс, группы
- Группа процессов
- Сеанс
- Фоновая группа сеанса
- Управляющий терминал
Команда kill
Описание команды kill
Синтаксис
PID — это PID (числовой идентификатор) процесса или несколько PID процессов, если требуется послать сигнал сразу нескольким процессам.
По умолчанию команда kill шлет сигнал KILL (он также называется SIGKILL и имеет числовое значение 9 ).
Опции
- числовой номер сигнала — в таком случае будет выведено название сигнала;
- название сигнала — в таком случае будет выведено числовое значение сигнала.
Примеры использования команды kill
Определить PID процесса
Так как команда kill принимает на вход PID (идентификатор) процесса, то необходимо сначала узнать PID процесса, которому требуется отправить сигнал. Чтобы это сделать, можно использовать команду ps (вместо firefox укажите название процесса):
Вывести список сигналов
Выведем список всех доступных сигналов:
В результате получим список сигналов и их числовые значения:
Отправка сигнала SIGTERM
Пошлем сигнал SIGTERM процессу с PID 3012:
Отправка сигнала KILL (завершение процесса)
Пошлем сигнал KILL процессу с PID 3121, чтобы принудительно завершить процесс:
Или можно использовать числовое значение сигнала:
Отправка сигнала нескольким процессам
Выполним отправку сигнала KILL сразу нескольким процессам. Для этого необходимо перечислить их идентификаторы:
Источник
Практика работы с сигналами
Хочу запечатлеть небольшой опыт работы с сигналами в Linux. Ниже будут представлены примеры использования наиболее значимых конструкций в этой области. Постараюсь разложить все по отдельным полочкам, чтобы всегда было легко глянуть и вспомнить, что и как использовать.
Важные факты о сигналах:
- Сигналы в Linux играют роль некоего средства межпроцессного взаимодействия (а так же и межпоточного)
- Каждый процесс имеет маску сигналов (сигналов, получение которых он игнорирует)
- Каждая нить (thread), так же как и процесс, имеет свою маску сигналов
- При получении сигнала(если он не блокируется) процесс/нить прерывается, управление передается в функцию обработчик сигнала, и если эта функция не приводит к завершению процесса/нити, то управление передается в точку на которой процесс/нить была прервана
- Можно установить свою функцию обработчик сигнала, но только для процесса. Данный обработчик будет вызываться и для каждой нити порожденной из этого процесса
Я не буду углубляться в теорию сигналов, что откуда зачем и куда. Меня в первую очередь интересует сам механизм работы с ними. Поэтому в качестве используемых сигналов будут выступать SIGUSR1 и SIGUSR2, это два единственных сигнала отданных в полное распоряжение пользователя. А так же я постараюсь уделить больше внимание именно межпоточному взаимодействию сигналов.
Итак, поехали.
Функция обработчик сигналов
Данная функция вызывается, когда процесс (или нить) получает неблокируемый сигнал. Дефолтный обработчик завершает наш процесс (нить). Но мы можем сами определить обработчики для интересующих нас сигналов. Следует очень осторожно относится к написанию обработчика сигналов, это не просто функция, выполняющаяся по коллбеку, происходит прерывание текущего потока выполнения без какой либо подготовительной работы, таким образом глобальные объекты могут находится в неконсистентном состоянии. Автор не берется приводить свод правил, так как сам их не знает, и призывает последовать совету Kobolog (надеюсь он не против, что я ссылаюсь на него) и изучить хотя бы вот этот материал FAQ.
Установить новый обработчик сигнала можно двумя функциями
Которая принимает номер сигнала, указатель на функцию обработчик (или же SIG_IGN (игнорировать сигнал) или SIG_DFL (дефолтный обработчик)), и возвращает старый обработчик. Сигналы SIGKILL и SIGSTOP не могут быть «перехвачены» или проигнорированы. Использование этой функции крайне не приветствуется, потому что:
- функция не блокирует получение других сигналов пока выполняется текущий обработчик, он будет прерван и начнет выполняться новый обработчик
- после первого получения сигнала (для которого мы установили свой обработчик), его обработчик будет сброшен на SIG_DFL
Этих недостатков лишена функция
Которая также принимает номер сигнала (кроме SIGKILL и SIGSTOP). Второй аргумент это новое описание для сигнала, через третий возвращается старое значение. Структура struct sigaction имеет следующие интересующие нас поля
- sa_handler — аналогичен sighandler_t в функции signal
- sa_mask — маска сигналов который будут блокированы пока выполняется наш обработчик. + по дефолту блокируется и сам полученный сигнал
- sa_flags — позволяет задать дополнительные действия при обработке сигнала о которых лучше почитать тут
Использование данной функции выглядит совсем просто
Здесь мы установили наш обработчик для сигналов SIGUSR1 и SUGUSR2, а также указали, что необходимо блокировать эти же сигналы пока выполняется обработчик.
С обработчиком сигналов есть один не очень удобный момент, он устанавливается на весь процесс и все порожденные нити сразу. Мы не имеет возможность для каждой нити установить свой обработчик сигналов.
Но при этом следует понимать что когда сигнал адресуется процессу, обработчик вызывается именно для главной нити (представляющей процесс). Если же сигнал адресуется для нити, то обработчик вызывается из контекста этой нити. См пример 1.
Блокирование сигналов
Для того, чтобы заблокировать некоторый сигналы для процесса, необходимо добавить их в маску сигналов данного процесса. Для этого используется функция
Мы можем к уже существующей маске сигналов добавить новые сигналы (SIG_BLOCK), можем из этой маски убрать часть сигналов (SIG_UNBLOCK), а так же установить полностью нашу маску сигналов (SIG_SETMASK).
Для работы с маской сигналов внутри нити используется функция
которая позволяет сделать все тоже, но уже для каждой нити в отдельности.
Невозможно заблокировать сигналы SIGKILL или SIGSTOP при помощи этих функций. Попытки это сделать будут игнорироваться.
sigwait
Данная функция позволяет приостановить выполнении процесса (или нити) до получения нужного сигнала (или одного из маски сигналов). Особенностью этой функции является то, что при получении сигнала не будет вызвана функции обработчик сигнала. См. пример 2.
Посыл сигнала
Для того, чтобы послать сигнал процессу можно использовать две функции
С первой все понятно. Вторая нужна для того, чтобы послать сигнал самому себе, и по сути равносильна kill(getpid(), signal). Функция getpid() возвращает PID текущего процесса.
Для того, чтобы послать сигнал отдельной нити, используется функция
Пример использования сигналов
Все, что я описал выше, не дает ответа на вопрос «Зачем мне использовать сигналы». Теперь я хотел бы привести реальный пример использования сигналов и где без них попросту не обойтись.
Представьте, что вы хотите читать или писать какие-то данные в какое то устройство, но это может привести к блокированию. Ну например, чтение в случае работы с сокетами. Или может быть запись в пайп. Вы можете вынести это в отдельный поток, чтобы не блокировать основную работу. Но что делать когда вам нужно завершить приложение? Как корректно прервать блокирующую операцию IO? Можно было бы задавать таймаут, но это не очень хорошее решение. Для этого есть более удобные средства: функции pselect и ppoll. Разница между ними исключительно в юзабельности, поведение у них одинаковое. В первую очередь эти функции нужны для мультиплексирования работы с IO (select/poll). Префикс ‘p’ в начале функции указывает на то, что данная функция может быть корректно прервана сигналом.
Итак, сформулируем требование:
Необходимо разработать приложение, открывающее сокет (для простоты UDP) и выполняющее в потоке операцию чтения. Данное приложение должно корректно без задержек завершаться по требованию пользователя.
Функция треда выглядит вот так
stop это глобальный булев флаг который устанавливается в true нашим обработчиком, что сообщает потоку о необходимости завершиться.
Логика работы такая:
- проверяем, что пока стартовал тред его еще не пожелали завершить
- блокируем завершающий сигнал
- проверяем, что пока блокировали, нас не пожелали завершить
- вызываем ppoll передавая в качестве последнего параметра маску сигналов по которой ждется сигнал
- после выхода из ppoll проверяем что вышли не из за сигнала о завершении
Вот так выглядит главная функция
Устанавливаем наш обработчик для SIGINT, и когда нужно завершить дочерний поток шлем ему этот сигнал.
Полный листинг см. пример 3.
На мой взгляд, недостатком данного способа является то, что в случае нескольких потоков мы можем завершить их только все сразу. Нет возможности устанавливать свой обработчик сигналов для каждого треда. Таким образом, нет возможности реализовать полноценное межпоточное взаимодействие через сигналы. Linux way это не предусматривает.
PS. Исходные коды разместил на сервисе PasteBin (ссылку не даю, а то еще за рекламу посчитают).
PPS. Прошу простить за обилие ошибок. Язык, слабая моя сторона. Спасибо, всем кто помог их исправить.
Данная статья не претендует на полное (и глубокое) описание работы с сигналами и нацелена в первую очередь на тех, кто до этого момента не сталкивались с понятием «сигнал». Для более глубоко понимания работы сигналов автор призывает обратиться в более компетентные источники и ознакомиться с конструктивной критикой в комментариях.
Источник
Сигналы, группы, сеансы
Сигналы в Unix указывают ядру, что надо прервать нормальное планирование процесса и завершить/остановить его, или, вместо продолжения выполнения процесса с места остановки, выполнить функцию — обработчик сигнала, и лишь затем продолжить выполнение основного кода.
Сигналы, предназначенные процессу, создаются (отправляются) в нескольких ситуациях: при аппаратных сбоях, при срабатывании особого таймера, при обработке спецсимволов (Ctrl C, Ctrl Z) драйвером управляющего терминала, с помощью системного вызова kill(). В зависимости от причины, отправляются сигналы разных типов. Тип сигнала обозначается целым числом (номером). В Linux сигналы нумеруются от 1 до 64. Сигнал может быть отправлен отдельной нити процесса, процессу в целом или группе процессов.
Для каждого номера сигнала в процессе (нити) предусмотрено некоторое действие (действие по умолчанию, игнорирование или вызов пользовательской функции). Доставка сигнала заключается в выполнении этого действия.
Между отправкой сигнала и его доставкой проходит некоторое непредсказуемое время, поскольку, обычно, сигналы обрабатываются (доставляются) при выходе из системных вызовов или в тот момент, когда планировщик назначает процесс на выполнение. Исключением является доставка сигнала SIGKILL остановленным процессам. Для большинства сигналов можно явно приостановить доставку с помощью установки маски блокирования доставки sigprocmask() , и, в случае необходимости, удалить сигнал без обработки с помощью sigwait() .
Сигналы SIGKILL и SIGSTOP не могут быть заблокированы или проигнорированы и на них нельзя установить свой обработчик.
Действия по умолчанию:
- SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU — остановка процесса
- SIGCONT — запуск остановленного процесса
- SIGCHLD — игнорируется
- SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGFPE, SIGSEGV — сохранение дампа памяти и завершение (Linux)
- остальные — завершение процесса
Сигнал, маска, обработчик
В упрощенном виде структуру в ядре, обеспечивающую доставку сигналов процессу, можно представить как таблицу:
Номер | Есть Сигнал? | Маска | Обработчик |
---|---|---|---|
1 | 1 | 1 | SIG_DFL |
2 | 0 | 0 | SIG_IGN |
3 | 0 | 0 | sig_func() |
. | . | . | . |
- Есть сигнал? — битовое поле, указывающее наличие недоставленного сигнала
- Маска — битовое поле, указывающее временный запрет на доставку сигнала
- Обработчик — указатель на действие, выполняемое при доставке сигнала. Может принимать значения: SIG_DFL — действие по умолчанию, SIG_IGN — игнорирование сигнала или указатель на функцию — обработчика сигнала.
Традиционно, при отправке процессу нескольких однотипных обычных сигналов, обработчик будет вызван лишь раз. Начиная с POSIX 1003.1, кроме обычных сигналов, поддерживаются сигналы реального времени, для которых создаётся очередь недоставленных сигналов, которая кроме номера сигнала, содержит значение (целое или адрес), которое уникально для каждого экземпляра сигнала.
Примеры использования сигналов
SIGKILL, SIGTERM, SIGINT, SIGHUP — завершение процесса. SIGKILL — не может быть проигнорирован, остальные могут. SIGTERM — оповещение служб о завершении работы ОС, SIGINT — завершение программы по нажатию Ctrl C, SIGHUP — оповещение программ, запущенных через модемное соединение, об обрыве связи (в настоящее время практически не используется).
SIGILL, SIGFPE, SIGBUS, SIGSEGV — аппаратный сбой. SIGILL — недопустимая инструкция CPU, SIGFPE — ошибка вычислений с плавающей точкой (деление на ноль), SIGBUS — физический сбой памяти, SIGSEGV — попытка доступа к несуществующим (защищенным) адресам памяти.
SIGSTOP, SIGCONT — приостановка и продолжение выполнения процесса
SIGPIPE — попытка записи в канал или сокет, у которого нет читателя
SIGCHLD — оповещение о завершении дочернего процесса.
Список сигналов
Особый сигнал с номером 0 фактически не доставляется, а используется в вызове kill() для проверки возможности доставки сигнала определённому процессу.
В Linux используется 64 сигнала. Список можно посмотреть в терминале командой kill -l
Posix.1-1990
Источник — man 7 signal
Отправка сигнала
int kill(pid_t pid, int signum);
Для отправка сигнала необходимо, чтобы uid или euid текущего процесса был равен 0 или совпадал с uid процесса получателя.
Получатель сигнала зависит от величины и знака параметра pid :
- pid > 0 — сигнал отправляется конкретному процессу
- pid == 0 — сигнал отправляется всем членам группы
- pid == -1 — сигнал отправляется всем процессам кроме init (в Linux’е еще кроме себя)
- pid int sigqueue(pid_t pid, int sig, const union sigval value);
Аналогично kill() , но выполняется по правилам сигналов реального времени. Сигнал и связанная с ним дополнительная информация (целое или адрес) value помещаются в очередь сигналов (FIFO). Таким образом процессу можно отправить несколько однотипных сигналов с разными значениями value .
Отправка сигнала текущему процессу. Эквивалент kill(getpid(),signum);
Убирает из маски сигналов сигнала SIGABRT и отправляет его текущему процессу . Если сигнал перехватывается или игнорируется, то после возвращения из обработчика abort() завершает программу. Выхода из функции abort() не предусмотрено. Единственный способ продолжить выполнение — не выходить из обработчика сигнала.
Отправка сигнала SIGALRM себе через time секунд. Возвращает 0 если ранее alarm не был установлен или число секунд остававшихся до срабатывания предыдущего alarma. Таймер управляющий alarm’ами один. Соответственно установка нового alarm’а отменяет старый. Параметр time==0 позволяет получить оставшееся до alarm’а время без установки нового.
Исторический способ обработки сигнала — signal
Установка реакции на сигнал через функцию signal() не до конца стандартизована и сохраняется для совместимости с историческими версиями Unix. Не рекомендуется к использованию. В стандарте POSIX signal() заменен на вызов sigaction(), сохраняющий для совместимости эмуляцию поведения signal().
sighandler — адрес функции обработчика void sighandler(int) или один из двух макросов: SIG_DFL (обработчик по умолчанию) или SIG_IGN (игнорирование сигнала).
signal(. ) возвращает предыдущее значение обработчика или SIG_ERR в случае ошибки.
В Linux и SysV при вызове sighandler обработчик сбрасывается в SIG_DFL и возможна доставка нового сигнала во время работы sighandler . Такое поведение заставляет первой строкой в sighandler восстанавливать себя в качестве обработчика сигнала, и, даже в этом случае, не гарантирует от вызова обработчика по умолчанию. В BSD системах сброс не происходит, доставка новых сигнала блокируется до выхода из sighandler .
Реакция на сигнал — sigaction
Параметры для установки обработчика сигнала через sigaction()
Данные, передаваемые в обработчик сигнала sa_sigaction()
В Linux структура siginfo_t содержит больше полей, но они служат для совместимости и не заполняются.
Блокирование сигналов
Все функции блокирования сигналов работают с маской сигналов типа sigset_t. В Linux это 64 бита, т.е. два слова в 32х разрядной архитектуре или одно в 64х разрядной. Выставленный бит в маске сигналов означает, что доставка сигнала с соответствующим номером будет заблокирована. Сигналы SIGKILL и SIGSTOP заблокировать нельзя.
Манипулирование маской
Параметр how определяет операцию над текущей маской. Значения SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK.
Проверка наличия заблокированных сигналов
Очистка заблокированных сигналов
sigwait ожидает блокировки какого-либо из сигналов, указанных в маске, удаляет этот сигнал и возвращает его номер в параметр sig . Если реализация сигналов предусматривает очередь сигналов, то удаляется только один элемент из очереди.
Управляющий терминал, сеанс, группы
Управляющий терминал, сеанс, группы
Для организации диалоговой работы пользователей в Unix вводится понятие терминальной сессии. С точки зрения пользователя — это процесс работы с текстовым терминалом с момента ввода имени и пароля и до выхода из системы командой logout ( exit , нажатие ^D в пустой строке). Во время терминальной сессии может быть запущено несколько программ, которые будут параллельно выполнятся в фоновом режиме и между которыми можно переключаться в диалоговом режиме. После завершения терминальной сессии возможно принудительное завершение всех запущенных в ней фоновых процессов.
С точки зрения ядра — терминальная сессия — это группа процессов, имеющих один идентификатор сеанса sid. С идентификатором sid связан драйвер управляющего терминала, доступный всем членам сеанса как файл символьного устройства /dev/tty. Для каждого сеанса существует свой /dev/tty. Управляющий терминал взаимодействует с процессами сеанса с помощью отправки сигналов.
В рамках одного сеанса могут существовать несколько групп процессов. С каждым процессом связан идентификатор группы pgid. Одна из групп в сеансе может быть зарегистрирована в драйвере управляющего терминала как группа фоновых процессов. Процессы могут переходить из группы в группу самостоятельно или переводить из группы в группу другие процессы сеанса. Перейти в группу другого сеанса нельзя, но можно создать свой собственный сеанс из одного процесса со своей группой в этом сеансе. Вернуться в предыдущий сеанс уже не получится.
Группа процессов
Группа процессов — инструмент для доставки сигнала нескольким процессам, а также способ арбитража при доступе к терминалу. Идентификатор группы pgid равен pid создавшего её процесса — лидера группы. Процесс может переходить из группы в группу внутри одного сеанса.
Сеанс
Сеанс — средство для контроля путем посылки сигналов над несколькими группами процессов со стороны терминального драйвера. Как правило соответствует диалоговой пользовательской сессии. Идентификатор сеанса sid равняется идентификатору pid, создавшего его процесса — лидера сеанса. Одновременно с сеансом создаётся новая группа с pgid равным pid лидера сеанса. Поскольку переход группы из сеанса в сеанс невозможен, то создающий сеанс процесс не может быть лидером группы.
Создание собственного сеанса рекомендуется начать с fork, чтобы гарантировать, что процесс не является лидером группы.
Фоновая группа сеанса
Процессы в фоновой группе выполняются до тех пор, пока не попытаются осуществить чтение или запись через файловый дескриптор управляющего терминала. В этот момент они получают сигнал SIGTTIN или SIGTTOU соответственно. Действие по умолчанию для данного сигнала — приостановка выполнения процесса.
Назначение фоновой группы:
Управляющий терминал
Некоторые сочетания клавиш позволяют посылать сигналы процессам сеанса:
- ^C — SIGINT — завершение работы
- ^Z — SIGTSTP — приостановка выполнения. bash отслеживает остановку дочерних процессов и вносит их в списки своих фоновых процессов. Остановленный процесс может быть продолжен командой fg n , где n — порядковый нрмер процесса в списке фоновых процессов
Источник