Какие сигналы нельзя перехватить в linux
Могут быть ситуации, когда вы не захотите, чтобы пользователи ваших скриптов с помощью ввода на клавиатуре специальной последовательности клавиш несвоевременно выходили из скрипта, например, поскольку нужно освободить входной поток или стереть ненужные данные. Инструкция trap перехватывает эти последовательности и ее можно так запрограммировать, что при обнаружении этих сигналов будет выполнен список команд.
Синтаксис инструкции trap сравнительно простой:
trap [COMMANDS] [SIGNALS]
Команде trap указывается перехватить перечисленные сигналы SIGNALS, которые могут быть именами сигналов с префиксом SIG или без этого префикса, либо номерами сигналов. Если сигнал равен 0 или EXIT, команды COMMANDS выполняются тогда, когда происходит выход в командную оболочку. Если одним из сигналов является сигнал DEBUG, список команд COMMANDS выполняется после выполнения каждой простой команды. Сигнал может быть также определен как ERR, в этом случае команды COMMANDS выполняются каждый раз, когда выход из простой команды происходит с ненулевым кодом возврата. Обратите внимание, что эти команды не будут выполняться, если ненулевой код возврат будет возвращен из части инструкции if или из цикла while или until. Они не будут исполняться даже в случае, когда с помощью логических команд AND (&&) или OR (||) будет возвращен ненулевой код выхода, или когда код возврата команды инвертируется с помощью оператора !.
Если спецификации сигнала были указаны правильно, то код возврата самой команды trap равен нулю. В команде trap есть несколько параметров, которые описаны в документации по Bash.
Вот очень простой пример перехвата Ctrl+C, вводимого пользователя, при котором печатается сообщение. При попытке уничтожить эту программу без указания сигнала KILL, ничего происходить не будет.
#!/bin/bash # traptest.sh trap "echo Booh!" SIGINT SIGTERM echo "pid is $$" while : # This is the same as "while true". do sleep 60 # This script is not really doing anything. done
Как Bash интерпретирует команду trap
Когда во время ожидания завершения команды Bash принимает сигнал, для которого была установлена команда trap, команда trap не будет выполняться до завершения исполняемой команды. Когда Bash с помощью встроенной команды wait ожидает выполнение асинхронной команды, прием сигнала, для которого была задана команда trap, вызовет немедленный выход из встроенной команды wait с кодом возврата, большим 128, а затем сразу будет выполнена команда trap.
| Предыдущий раздел: | Оглавление | Следующий раздел: |
| Сигналы | Дополнительные примеры |
Какие сигналы нельзя перехватить в linux
Главное отличие сигналов от других средств взаимодействия между процессами заключается в том, что их обработка программой обычно происходит сразу же после поступления сигнала (или не происходит вообще), независимо от того, что программа делает в данный момент. Сигнал прерывает нормальный порядок выполнения инструкций в программе и передает управление специальной функции – обработчику сигнала. Если обработка сигнала не приводит к завершению процесса, то по выходе из функции-обработчика выполнение процесса возобновляется с той точки, в которой оно было прервано. У программ также есть возможность приостановить обработку поступающих сигналов временно, на период выполнения какой-либо важной операции. В традиционной терминологии приостановка получения определенных сигналов называется блокированием. Если для поступившего сигнала было установлено блокирование, сигнал будет передан программе, как только она разблокирует данный тип сигналов. Этим блокирование отличается от игнорирования сигнала, при котором сигналы соответствующего типа никогда не передаются программе. Следует помнить, что не все сигналы могут быть проигнорированы. Например, при получении программой сигнала принудительного завершения SIGKILL система ничего не сообщает программе, а просто прекращает ее работу. Таким образом, преимущество сигналов перед другими средствами взаимодействия с программой заключается в том, что посылать программе сигналы можно в любой момент ее работы, не дожидаясь наступления каких-то особых условий. Источником сигналов может быть как сам операционная система, так и другие программы пользователя. Если вам показалось, что сигналы похожи на прерывания, то вы совершенно правы. Для реализации сигналов действительно используются программные прерывания.
Номерам сигналов соответствуют константы, определенные в файле . Имена всех этих констант начинаются с префикса SIG, за которыми следует сокращенное название сигнала. Стандарт POSIX определяет две группы сигналов – «классические» сигналы Unix и сигналы реального времени. В отличие от классических сигналов сигналы реального времени всегда буферизуются, так что программа получит все посланные ей сигналы. В этой статье мы рассмотрим только классические сигналы Unix, каковых в Linux насчитывается 31. Этим сигналам назначены номера с 1 до 31 (номер 0, так называемый null-сигнал имеет особый смысл). Полный список сигналов можно получить из заголовочного файла signal.h. Мы рассмотрим несколько наиболее интересных сигналов.
Сигнал SIGHUP (номер 1) изначально был предназначен для того, чтобы информировать программу о потере связи с управляющим терминалом (терминалы часто подключались к системе с помощью модемов, так что название сигнала происходит от hung up – повесить трубку). Сигнал SIGHUP посылается приложению так же и в том случае, если процесс-лидер сессии завершил свою работу. Многие программы-демоны, у которых нет лидера сессии, так же обрабатывают этот сигнал. В ответ на получение SIGHUP демон обычно перезапускается (или просто повторно читает файл конфигурации). По умолчанию программа, получившая этот сигнал, завершается.
Сигнал SIGINT (номер 2) обычно посылается процессу, если пользователь терминала дал команду прервать процесс (обычно эта команда – сочетание клавиш Ctrl-C) .
Сигнал SIGABRT (номер 6) посылается программе в результате вызова функции abort(3). В результате программа завершается с сохранением на диске образа памяти.
Сигнал SIGKILL (номер 9) завершает работу программы. Программа не может ни обработать, ни игнорировать этот сигнал.
Сигнал SIGSEGV (номер 11) посылается процессу, который пытается обратиться к не принадлежащей ему области памяти. Если обработчик сигнала не установлен, программа завершается с сохранением на диске образа памяти.
Сигнал SIGTERM (номер 15) вызывает «вежливое» завершение программы. Получив этот сигнал, программа может выполнить необходимые перед завершением операции (например, высвободить занятые ресурсы). Получение SIGTERM свидетельствует не об ошибке в программе, а о желании ОС или пользователя завершить ее.
Сигнал SIGCHLD (номер 17) посылается процессу в том случае, если его дочерний процесс завершился или был приостановлен. Родительский процесс также получит этот сигнал, если он установил режим отслеживания сигналов дочернего процесса и дочерний процесс получил какой-либо сигнал. По умолчанию сигнал SIGCHLD игнорируется.
Сигнал SIGCONT (номер 18) возобновляет выполнение процесса, остановленного сигналом SIGSTOP.
Сигнал SIGSTOP (номер 19) приостанавливает выполнение процесса. Как и SIGKILL, этот сигнал не возможно перехватить или игнорировать.
Сигнал SIGTSTP (номер 20) приостанавливает процесс по команде пользователя (обычно эта команда – сочетание клавиш Ctrl-Z).
Сигнал SIGIO/SIGPOLL (в Linux обе константы обозначают один сигнал – номер 29) сообщает процессу, что на одном из дескрипторов, открытых асинхронно, появились данные. По умолчанию этот сигнал, как ни странно, завершает работу программы.
В стандартной системе Unix определены два сигнала, SIGUSR1 (в Linux – номер 10) и SIGUSR2 (номер 12), предназначенные для передачи произвольной информации, но использование этих сигналов не приветствуется. Одной из причин негативного отношения программистов Unix к пользовательским сигналам является то, что сигналы, вообще говоря, представляют собой ограниченный ресурс, совместное использование которого может вызвать конфликты (например, если программист задействовал эти сигналы в своей программе и при этом использует стороннюю библиотеку, в которой эти сигналы также задействованы). Если вы не знали, то вам, возможно, будет интересно узнать, что обработка сигналов является частью стандарта языка Си и, как таковая, поддерживается даже на платформе Microsoft Windows. Однако, стандартный интерфейс сигналов Си, основанный на функции signal(), довольно неуклюж (недостатки интерфейса сигналов Си подробно описаны в книге [2]), так что мы воспользуемся более совершенным вариантом интерфейса, основанным на функции sigaction(2). Для демонстрации работы обработки сигналов мы напишем небольшую программу (файл sigdemo.c в исходниках).
Для функций, обрабатывающих потоки, существует и еще одно важное требование – реентерабильность. Поскольку обработчик сигнала может быть вызван в любой точке выполнения программы (а при не кототорых условиях во время обработки одного сигнала может быть вызван другой обработчик сигнала) в обработчиках додлжны использоваться функции, которые удовлетворяют требованию реентерабельности, то есть, могут быть вызваны в то время, когда они уже вызваны где-то в другой точке программы. Фактически, требование реентерабельности сводится к тому, чтобы функция не использовала никаких глобальных ресурсов, не позаботившись о синхронизации доступа к этим ресурсам. Некоторые функции ввода-вывода, в том числе, функция printf(), которую мы (и не только мы) используем в примерах обработчиков сигналов, реентерабельными не являются. Это значит, что выводу одной функции printf() может помешать вывод другой функции. В приложении приводится список реентерабельных функций, которые безопасно вызвать из обработчиков сигналов.
Единственным параметром нашего варианта функции-обработчика сигнала (в Unix-системах существует и другой вариант) является переменная типа int, в которой передается номер сигнала, вызвавшего обработчик. Нам этот номер не нужен, поскольку мы знаем, что только один сигнал, — SIGTERM, может вызвать нашу функцию, однако, в принципе, ничто не мешает нам использовать одну функцию для обработки нескольких разных сигналов, и тогда параметр функции- обработчика будет иметь для нас смысл. Функция-обработчик не возвращает никакого значения, что вполне логично, так как она вызывается не нашей программой, а неким системным компонентом. Особый интерес представляет завершение программы из обработчика сигнала. Назначение обработчика сигналу SIGTERM означает, что умалчиваемое действие сигнала, – завершение программы, не будет выполняться автоматически, и нам необходимо (если, конечно, мы хотим, чтобы этот сигнал завершал программу) позаботиться об этом явным образом. Если вы закомментируете вызов exit() в нашем примере, то увидите, что программа не будет завершать по получении сигнала SIGTERM. В принципе, вы можете придать сигналу SIGTERM совершенно иной смысл, например, оповещать программу о наступлении времени вашей любимой телепередачи (или о выходе нового номера журнала Linux Format), однако назначать стандартным сигналам нестандартные действия категорически не рекомендуется. Обработчик SIGTERM предназначен для того, чтобы, по требованию системы или пользователя, программа могла быстро и элегантно закончить текущую задачу и завершить свое выполнение. Именно этим обработчик и должен заниматься.
Перейдем теперь к тексту главной функции программы. Установка и удаление обработчиков сигналов осуществляются функцией sigaction(2). Первым параметром этой функции является номер сигнала, а в качестве второго и третьего параметров следует передать указатели на структуру sigaction. Эта структура содержит данные об операции, выполняемой над обработчиком сигнала. Второй параметр sigaction() служит для передачи новых значений для обработки сигнала, а третий – возвращает ранее установленные значения. В таблице 1 приводится краткое описание полей структуры sigaction.
| Поле | Значение |
|---|---|
| sa_handler | Указатель на функцию обработчик сигнала или константа. |
| sa_mask | Маска сигналов, блокируемых на время вызова обработчика. |
| sa_flags | Дополнительные флаги. |
Таблица 1. Поля структуры sigaction.
Поле sa_handler должно содержать либо адрес функции-обработчика, либо специальную константу, указывающую, что нужно делать с сигналом. Константа SIG_IGN указывает, что сигнал следует игнорировать, а константа SIG_DFL – что нужно восстановить обработку сигнала, заданную системой по умолчанию. Поле sa_mask позволяет заблокировать некоторое множество сигналов на время выполнения обработчика данного сигнала. Делается это для того, чтобы обработка других сигналов не могла прервать обработку данного (это может быть необходимо, особенно, если один обработчик обрабатывает несколько разных сигналов). Параметр sa_flags позволяет задать ряд флагов для выполнения более тонкой настройки обработчика сигналов. Например, флаг SA_RESETHAND указывает, что после завершения обработки сигнала заданным обработчиком должен быть восстановлен обработчик, заданный по умолчанию, так что все последующие сигналы будут обрабатываться умалчиваемым обработчиком.
Рассмотрим теперь блокировку сигналов. Поскольку игнорирование сигнала устанавливается функцией sigaction(), можно было бы ожидать, что и блокировка устанавливается этой же функцией, но это не так. Так как зачастую программисту приходится блокировать несколько сигналов сразу, для блокировки существует специальная функция sigprocmask(2), которая оперирует наборами сигналов (signal sets). Разделение интерфейса между несколькими функциями вызвано еще и требованиями многопоточности. Параметры, устанавливаемые sigaction(), действительны для всей программы в целом, тогда как блокировку сигналов потоки осуществляют независимо друг от друга. Наборы сигналов хранятся в переменных специального типа — sigset_t, а операции над ними осуществляются с помощью специальных функций. Функция sigemptyset() инициализирует набор сигналов пустыми значениями, а функция sigfillset() устанавливает все возможные значения в наборе. Используемая нами функция sigaddset() добавляет значение сигнала в набор, а функция sigdelset() удаляет сигнал из набора. После того как набор сигналов сформирован, мы передаем его функции sigprocmask(), которая выполняет блокирование и разблокирование сигналов.
Первым параметром этой функции должна быть одна из констант, определяющих операцию над заданными сигналами. Константа SIG_BLOCK указывает, что сигналы из нового набора должны быть добавлены к списку уже заблокированных сигналов. Константа SIG_SETMASK указывает, что новый набор блокируемых сигналов должен заменить уже существующий (при этом заблокированные ранее сигналы будут разблокированы, если они не заблокированы в новом наборе), а константа SIG_UNBLOCK указывает на необходимость разблокировать сигналы, переданные в наборе. В нашей программе мы блокируем сигнал SIGHUP и вы можете видеть, что программа не обрабатывает этот сигнал. Послать нашей программе сигнал SIGHUP вы можете с помощью консольной команды где PID – идентификатор процесса.
Сигналы прерывают нормальный порядок выполнения программы и могут завершить работу программы, не способной завершиться иным образом. Но иногда бывает так, что программе просто нечего делать до тех пор, пока она не получит какой-либо сигнал. Иначе говоря, программу нужно заставить ждать появления сигнала, по возможности не нагружая процессор. Такая ситуация может возникнуть, например, в многопоточном программировании, когда нужно синхронизировать завершение нескольких потоков. Ожидание сигнала можно реализовать с помощью цикла, проверяющего значение флажка, который может сбросить обработчик сигнала. В некоторых случаях (таких как рассмотренный выше пример) можно реализовать ожидание и с помощью бесконечного цикла. Очевидно, однако, что эти методы не эффективны и не элегантны. В POSIX- системах существует специальная функция sigwait(3), которая «усыпляет» процесс до тех пор, пока процессу не будет передан один из заданного набора сигналов.
Модифицируем нашу программу так, чтобы вместо бесконечного цикла она входила в цикл ожидания сигнала SIGHUP (файл swdemo.c на компакт-диске):
Функцию sigwait() можно использовать и для исследования сигналов. На компакт-диске вы найдете программку siglog.c, которая распечатывает информацию о каждом поступившем сигнале (естественно, исследуются только те сигналы, которые могут быть заблокированы). Рассмотрим здесь фрагмент этой программы:
Таблица 2 демонстрирует поведение функции kill()в зависимости от значения PID:
| PID > 1 | Сигнал посылается процессу с соответствующим PID. |
| PID == 0 | Сигнал посылается всем процессам из той же группы что и процесс-источник. |
| PID < 0 | Сигнал посылается всем процессам, чей идентификатор группы равен абсолютному значению PID. |
| PID == 1 | Сигнал посылается всем процессам системы. |
Вызов эквивалентен вызову
Так же как и для других примитивов IPC, для сигналов действует система прав доступа, основанная на правах доступа владельцев процессов. Процесс-приемник получит сигнал только в том случае, если у процесса-источника есть соответствующие права. С помощью функции kill()можно проверить, существует ли в системе процесс с заданным PID, не посылая процессу никаких сигналов. Для этого предназначен псевдо-сигнал с номером 0. Если соответствующего процесса не существует, функция kill()вернет значение 1, соответствующее об ошибке. В любом случае, сигнал не будет отправлен. Читателей, полюбивших обработку сигналов, я могу обрадовать тем, что мы рассмотрели далеко не все функции, связанные с сигналами. При изучении документации вас ждет еще много полезного и приятного, мы же закончим на этом наше знакомство с сигналами.
В вашей системе есть страница man, на которой перечислены все имеющиеся сигналы, но доступ к этой странице происходит по-разному в зависимости от того, какая у вас операционная систем. В большинстве систем Linux страницу можно открыть с помощью команды man 7 signal. Если возникнут проблемы, то найдите точное месторасположение страницы man с помощью команды
Имена сигналов можно получить с помощью команды kill -l .
Сигналы в вашей командной оболочке Bash
Когда отсутствуют какие-либо команды trap, интерактивная оболочка Bash игнорирует сигналы SIGTERM и SIGQUIT. Сигнал SIGINT перехватывается и обрабатывается, и если осуществляется управление заданиями, то также игнорируются сигналы SIGTTIN, SIGTTOU и SIGTSTP. Если эти сигналы поступают от клавиатуры, то команды, участвующие в подстановке команд, также игнорируют эти сигналы.
По сигналу SIGHUP по умолчанию происходит выход из командной оболочки. Интерактивная оболочка отправит сигнал SIGHUP всем заданиям, работающим или остановленным; если вы хотите отменить такое действие для конкретного процесса, которое задается по умолчанию, смотрите документацию по встроенной команде disown. Во встроенной команде shopt укажите параметр huponexit для уничтожения всех заданий, принимающих сигнал SIGHUP.
Посылаем сигнал в командной оболочке
В оболочке Bash можно посылать следующие сигналы:
Таблица 12.1. Управляющие сигналы в Bash
Сигнал прерывания, отправляет сигнал SIGINT заданию, работающему в приоритетном режиме.
Сигнал задержанной приостановки. Вызывает остановку работающего процесса, когда он попытается прочитать из терминала входные данные. Управление возвращается в командную оболочку, причем пользовательский процесс может оставаться приоритетным, фоновым или может быть уничтожен. Задержанную приостановку можно использовать только в тех операционных системах, где эта функция поддерживается.
Сигнал приостановки, посылается SIGTSTP в работающую программу, что ведет к остановке программы и возвращению управления в командную оболочку.
Проверьте настройки вашего терминала stty. Приостановка и возобновление выдачи выходных данных отключена, если вы используете эмуляцию «современных» терминалов. Стандартный терминал xterm по умолчанию поддерживает Ctrl+S и Ctrl+Q.
Использование сигналов с функцией kill
В большинстве современных командных оболочек, к которым относится Bash, есть встроенная функция kill. В Bash в качестве параметров можно указывать имена и номера сигналов, а аргументами могут быть задания или идентификаторы процессов. Состояние кода возврата можно получить с помощью параметра -l : ноль, если успешно отправлен хотя бы один сигнал, и не ноль, если произошла ошибка.
Когда используется команда kill из /usr/bin в вашей системе, то в команде могут быть дополнительные возможности, позволяющие уничтожать процессы с идентификатором не вашего, а другого пользователя, и указывать процессы по именам, точно также, как в pgrep и pkill.
Если ничего не задано, то оба варианта команды kill посылают сигнал TERM.
Это список наиболее распространенных сигналов:
Таблица 12.2. Наиболее распространенные сигналы
Прерывание с клавиатуры
Сигналы SIGKILL и SIGSTOP нельзя перехватывать, блокировать или игнорировать.
Когда уничтожается процесс или серия процессов, разумно начать с попытки использовать менее опасный сигнал SIGTERM. Таким образом, программам, которым важно правильное их завершение, предоставляется шанс следовать процедурам, которые создаются для случаев, когда программы получают сигнал SIGTERM, например, процедурам уборки мусора и закрытия открытых файлов. Если вы посылаете в процесс сигнал SIGKILL, все шансы сделать в этом процессе аккуратную уборку мусора и остановку могут быть потеряны и это может привести к плачевным последствиям.
Но если чистое завершение не работает, единственным способом остаются сигналы INT или KILL. Например, когда процесс не уничтожается с помощью нажатия клавиш Ctrl+C, то лучше использовать kill -9 и указать идентификатор процесса:
Когда процесс запускает несколько экземпляров, проще воспользоваться командой killall. В ней используются те же самые параметры, как и в команде kill, но она применяется ко всем экземплярам данного процесса. Испытайте эту команду прежде, чем ей воспользоваться на практике, поскольку в некоторых коммерческих версиях UNIX она может работать не так, как ожидается.
Всего в Linux 63 сигнала, обозначаемых своими номерами или символическими именами. Имена всех сигналов начинаются с SIG, и эту приставку часто опускают: так, сигнал, требующий прекратить выполнение процесса, называется SIGKILL, или KILL, или сигнал 9.
Получив сигнал, процесс может: игнорировать его; вызвать для обработки установленную по умолчанию функцию; вызвать собственный обработчик (перехватить сигнал). Некоторые сигналы (например, KILL) перехватить или игнорировать невозможно.
Пользователь может послать сигнал процессу с идентификатором PID командой
где — это номер или символическое имя.
Несколько часто встречающихся сигналов перечислены в таблице 3.1. Полный список можно получить по команде kill -l (list).
Сигналы Linux Таблица 3.1
№ Имя Назначение Реакция процесса-получателя 1 HUP Hangup — отбой Демоны перечитывают свои конфигурационные файлы 2 INT Interrupt Прекратить выполнение (перехватывается) 3 QUIT Сильнее, чем INT то же 4 ILL Illegal instruction. Программная ошибка Обработать ошибку. По умолчанию — прекратить выполнение 8 FPE Floating point exception Вычислительная ошибка (деление на ноль) Обработать ошибку. По умолчанию — прекратить выполнение 9 KILL Убить процесс Немедленно прекратить выполнение. Не перехватывается 11 SEGV Segmentation violation. Попытка доступа к чужой области памяти Обработать ошибку. По умолчанию — прекратить выполнение 13 PIPE Нет процесса, читающего из конвейера Обработать ошибку 15 TERM Termination. Завершить процесс Корректно завершить выполнение. Перехватывается 17 CHLD Завершился дочерний процесс Принять возвращенное им значение
Некоторые сигналы посылаются по нажатии комбинации клавиш. Так, 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. Сигналы
27.3.10. Сигналы и сокеты
27.3.10. Сигналы и сокеты С сокетами связаны три сигнала:? SIGIO — сокет готов к вводу/выводу. Сигнал посылается процессу, который связан с сокетом;? SIGURG — сокет получил экспресс-данные (мы их использовать не будем, поэтому особо останавливаться на них нет смысла);? SIGPIPE — запись
7.2.6.2. Сигналы
7.2.6.2. Сигналы
3.3. Сигналы
Сигналы (классические):
Сигнал SIGHUP (номер 1) изначально был предназначен для того, чтобы информировать программу о потере связи с управляющим терминалом (терминалы часто подключались к системе с помощью модемов, так что название сигнала происходит от hung up – повесить трубку). Сигнал SIGHUP посылается приложению так же и в том случае, если процесс-лидер сессии завершил свою работу. Многие программы-демоны, у которых нет лидера сессии, так же обрабатывают этот сигнал. В ответ на получение SIGHUP демон обычно перезапускается (или просто повторно читает файл конфигурации). По умолчанию программа, получившая этот сигнал, завершается.
Сигнал SIGINT (номер 2) обычно посылается процессу, если пользователь терминала дал команду прервать процесс (обычно эта команда – сочетание клавиш Ctrl-C) .
Сигнал SIGABRT (номер 6) посылается программе в результате вызова функции abort(3). В результате программа завершается с сохранением на диске образа памяти.
Сигнал SIGKILL (номер 9) завершает работу программы. Программа не может ни обработать, ни игнорировать этот сигнал.
Сигнал SIGSEGV (номер 11) посылается процессу, который пытается обратиться к не принадлежащей ему области памяти. Если обработчик сигнала не установлен, программа завершается с сохранением на диске образа памяти.
Сигнал SIGTERM (номер 15) вызывает «вежливое» завершение программы. Получив этот сигнал, программа может выполнить необходимые перед завершением операции (например, высвободить занятые ресурсы). Получение SIGTERM свидетельствует не об ошибке в программе, а о желании ОС или пользователя завершить ее.
Сигнал SIGCHLD (номер 17) посылается процессу в том случае, если его дочерний процесс завершился или был приостановлен. Родительский процесс также получит этот сигнал, если он установил режим отслеживания сигналов дочернего процесса и дочерний процесс получил какой-либо сигнал. По умолчанию сигнал SIGCHLD игнорируется.
Сигнал SIGCONT (номер 18) возобновляет выполнение процесса, остановленного сигналом SIGSTOP.
Сигнал SIGSTOP (номер 19) приостанавливает выполнение процесса. Как и SIGKILL, этот сигнал не возможно перехватить или игнорировать.
Сигнал SIGTSTP (номер 20) приостанавливает процесс по команде пользователя (обычно эта команда – сочетание клавиш Ctrl-Z).
Сигнал SIGIO/SIGPOLL (в Linux обе константы обозначают один сигнал – номер 29) сообщает процессу, что на одном из дескрипторов, открытых асинхронно, появились данные. По умолчанию этот сигнал, как ни странно, завершает работу программы.
Итак, задача. Нам нужно организовать прием/передачу сигналов между двумя процессами. Но процессы у нас не простые. Один порождает другой. То есть один процесс будет родительским, а второй — дочерним.
Итак, начнем. ОС — Ubuntu Linux, компилятор — gcc.
Первый шаг — напишем функцию, которая будет порождать дочерний процесс.
Как известно, в Linux процессы порождаются функцией fork. Она клонирует текущий процесс. Чтобы сделать процесс, который выполняет другой код, нужно использовать функцию из семейства exec.
Разъясняю. fork — копирует процесс. Эта функция возвращает pid порожденного процесса. Если fork вернет значение меньше 0, то это значит что произошла ошибка, и продолжать нельзя. Нужно выйти.
Если fork вернет 0, это значит что процесс клонировался, и мы сейчас находимся в порожденном процессе. В данном случае мы вызываем функцию execl, которая назначает порожденному процессу новый исполняемый файл.
Код, который идет дальше — это код, который выполняет текущий процесс.
Дальше мы будем отправлять порожденному процессу сигналы. В качестве рабочего сигнала я выбрал SIGHUP.
Посылка сигнала выполняется с помощью функции kill(согласен, не сильно благозвучное название ).
Читайте также:
- После установки виндовс 7 не работает мышь на ноутбуке леново
- Для работы battle net необходимо запустить службу windows
- Как растянуть обои на 2 монитора windows 10
- Журнал событий windows 7 можно ли отключить
- Сколько стоит офис для windows 10
Функция обработчик сигналов
Данная функция вызывается, когда процесс (или нить) получает неблокируемый сигнал. Дефолтный обработчик завершает наш процесс (нить). Но мы можем сами определить обработчики для интересующих нас сигналов. Следует очень осторожно относится к написанию обработчика сигналов, это не просто функция, выполняющаяся по коллбеку, происходит прерывание текущего потока выполнения без какой либо подготовительной работы, таким образом глобальные объекты могут находится в неконсистентном состоянии. Автор не берется приводить свод правил, так как сам их не знает, и призывает последовать совету Kobolog (надеюсь он не против, что я ссылаюсь на него) и изучить хотя бы вот этот материал FAQ.
void hdl(int sig)
Установить новый обработчик сигнала можно двумя функциями
sighandler_t signal(int signum, sighandler_t handler);
- функция не блокирует получение других сигналов пока выполняется текущий обработчик, он будет прерван и начнет выполняться новый обработчик
- после первого получения сигнала (для которого мы установили свой обработчик), его обработчик будет сброшен на SIG_DFL
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
- sa_handler — аналогичен sighandler_t в функции signal
- sa_mask — маска сигналов который будут блокированы пока выполняется наш обработчик. + по дефолту блокируется и сам полученный сигнал
- sa_flags — позволяет задать дополнительные действия при обработке сигнала о которых лучше почитать тут
struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = hdl; sigset_t set; sigemptyset(&set); sigaddset(&set, SIGUSR1); sigaddset(&set, SIGUSR2); act.sa_mask = set; sigaction(SIGUSR1, &act, 0); sigaction(SIGUSR2, &act, 0);
Здесь мы установили наш обработчик для сигналов SIGUSR1 и SUGUSR2, а также указали, что необходимо блокировать эти же сигналы пока выполняется обработчик.
С обработчиком сигналов есть один не очень удобный момент, он устанавливается на весь процесс и все порожденные нити сразу. Мы не имеет возможность для каждой нити установить свой обработчик сигналов.
Но при этом следует понимать что когда сигнал адресуется процессу, обработчик вызывается именно для главной нити (представляющей процесс). Если же сигнал адресуется для нити, то обработчик вызывается из контекста этой нити. См пример 1.
Блокирование сигналов
Для того, чтобы заблокировать некоторый сигналы для процесса, необходимо добавить их в маску сигналов данного процесса. Для этого используется функция
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
Мы можем к уже существующей маске сигналов добавить новые сигналы (SIG_BLOCK), можем из этой маски убрать часть сигналов (SIG_UNBLOCK), а так же установить полностью нашу маску сигналов (SIG_SETMASK).
Для работы с маской сигналов внутри нити используется функция
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset);
которая позволяет сделать все тоже, но уже для каждой нити в отдельности.
Невозможно заблокировать сигналы SIGKILL или SIGSTOP при помощи этих функций. Попытки это сделать будут игнорироваться.
sigwait
Данная функция позволяет приостановить выполнении процесса (или нити) до получения нужного сигнала (или одного из маски сигналов). Особенностью этой функции является то, что при получении сигнала не будет вызвана функции обработчик сигнала. См. пример 2.
Посыл сигнала
Для того, чтобы послать сигнал процессу можно использовать две функции
int kill(pid_t pid, int sig); int raise(int sig);
С первой все понятно. Вторая нужна для того, чтобы послать сигнал самому себе, и по сути равносильна kill(getpid(), signal). Функция getpid() возвращает PID текущего процесса.
Для того, чтобы послать сигнал отдельной нити, используется функция
int pthread_kill(pthread_t thread, int sig);
Пример использования сигналов
Все, что я описал выше, не дает ответа на вопрос «Зачем мне использовать сигналы». Теперь я хотел бы привести реальный пример использования сигналов и где без них попросту не обойтись.
Представьте, что вы хотите читать или писать какие-то данные в какое то устройство, но это может привести к блокированию. Ну например, чтение в случае работы с сокетами. Или может быть запись в пайп. Вы можете вынести это в отдельный поток, чтобы не блокировать основную работу. Но что делать когда вам нужно завершить приложение? Как корректно прервать блокирующую операцию IO? Можно было бы задавать таймаут, но это не очень хорошее решение. Для этого есть более удобные средства: функции pselect и ppoll. Разница между ними исключительно в юзабельности, поведение у них одинаковое. В первую очередь эти функции нужны для мультиплексирования работы с IO (select/poll). Префикс ‘p’ в начале функции указывает на то, что данная функция может быть корректно прервана сигналом.
Итак, сформулируем требование:
Необходимо разработать приложение, открывающее сокет (для простоты UDP) и выполняющее в потоке операцию чтения. Данное приложение должно корректно без задержек завершаться по требованию пользователя.
Функция треда выглядит вот так
void* blocking_read(void* arg) < if(stop) < // не успели стартовать, а нас уже прикрыли ? std::cout // Блокируем сигнал SIGINT sigset_t set, orig; sigemptyset(&set); sigaddset(&set, SIGINT); sigemptyset(&orig); pthread_sigmask(SIG_BLOCK, &set, &orig); if(stop) < // пока мы устанавливали блокировку сигнала он уже произошол // возвращаем все как было и выходим std::cout // Здесь нас не могут прервать сигналом SIGINT std::cout // Мы либо считали данные, либо произошла какаято ошибка. Но мы не получали // сигнала о завершении работы и продолжаем работать "по плану" close(sockfd); pthread_exit((void *)0); >
- проверяем, что пока стартовал тред его еще не пожелали завершить
- блокируем завершающий сигнал
- проверяем, что пока блокировали, нас не пожелали завершить
- вызываем ppoll передавая в качестве последнего параметра маску сигналов по которой ждется сигнал
- после выхода из ppoll проверяем что вышли не из за сигнала о завершении
int main()
Устанавливаем наш обработчик для SIGINT, и когда нужно завершить дочерний поток шлем ему этот сигнал.
Полный листинг см. пример 3.
На мой взгляд, недостатком данного способа является то, что в случае нескольких потоков мы можем завершить их только все сразу. Нет возможности устанавливать свой обработчик сигналов для каждого треда. Таким образом, нет возможности реализовать полноценное межпоточное взаимодействие через сигналы. Linux way это не предусматривает.
PS. Исходные коды разместил на сервисе PasteBin (ссылку не даю, а то еще за рекламу посчитают).
PPS. Прошу простить за обилие ошибок. Язык, слабая моя сторона. Спасибо, всем кто помог их исправить.
Данная статья не претендует на полное (и глубокое) описание работы с сигналами и нацелена в первую очередь на тех, кто до этого момента не сталкивались с понятием «сигнал». Для более глубоко понимания работы сигналов автор призывает обратиться в более компетентные источники и ознакомиться с конструктивной критикой в комментариях.
Какие сигналы нельзя перехватить в Linux список сигналов и их особенности
В мире операционных систем UNIX-подобных систем, включая Linux, сигналы играют важную роль для обеспечения коммуникации между процессами и обработки различных событий. Сигналы представляют собой короткие сообщения, которые могут быть отправлены одним процессом другому процессу или самому себе.
В Linux существует множество различных сигналов, каждый из которых имеет свой уникальный номер и особые свойства. Большинство сигналов могут быть перехвачены и обработаны процессом, однако есть несколько исключений — некоторые сигналы не могут быть перехвачены, игнорированы или предотвращены.
Вы боитесь искусственного интеллекта?
Да. Он скоро захватит мир!
Нет. Но страшно из-за него потерять работу.
В случае войны с ИИ, мы победим!
Показать результаты
Проголосовало: 364
Одним из таких сигналов является SIGKILL (9).
Сигнал SIGKILL является наиболее радикальным и непримиримым сигналом, который может быть отправлен процессу. Он вынуждает процесс немедленно прекратить свою работу, не давая ему возможность обработать или проигнорировать сигнал. Нельзя перехватить или заблокировать SIGKILL, и это делает его незаменимым инструментом в критических ситуациях, когда требуется немедленное завершение процесса.
Среди других сигналов, которые нельзя перехватить, можно выделить SIGSTOP (19) и SIGCONT (18).
Сигнал SIGSTOP используется для остановки процесса, оставляя его в приостановленном состоянии до тех пор, пока не будет получен сигнал SIGCONT.
Сигнал SIGCONT отправляется процессу для возобновления его работы после приостановки. Оба эти сигнала играют важную роль в управлении выполнением процессов и не могут быть перехвачены или заблокированы.
Знание о списках сигналов и их особенностей является важным компонентом понимания взаимодействия между процессами в Linux и может стать весьма полезным при разработке и отладке программного обеспечения.
Сигналы, не поддающиеся перехвату в Linux
В операционной системе Linux есть несколько сигналов, которые не могут быть перехвачены пользовательскими процессами или обработаны способами, доступными в рамках пользовательского пространства.
Один из таких сигналов – SIGKILL. Этот сигнал отправляет процессу запрос на немедленное завершение, не давая ему возможности обратиться к обработчику сигналов. Пользовательские процессы не могут перехватывать SIGKILL и изменять его действие.
Читайте также: Секреты здоровой пищеварительной системы: правильный образ жизни или важность правильного питания
Также нельзя перехватить сигнал SIGSTOP. Этот сигнал останавливает процесс, поэтому он не может быть перехвачен и изменен обработчиком сигналов. При получении сигнала SIGSTOP процесс приостанавливается до тех пор, пока не будет получен сигнал SIGCONT.
Следующий не перехватываемый сигнал – SIGKILL. Он также отправляет процессу запрос на завершение, но с отличием, что этот сигнал нельзя проигнорировать или изменить его действие. Если процесс получает SIGKILL, он немедленно завершается, не выполняя дополнительных действий.
Кроме того, сигналы SIGCONT и SIGTSTP также не могут быть перехвачены процессами. SIGCONT используется для возобновления ранее приостановленного процесса, а SIGTSTP – для остановки процесса с возможностью продолжения его выполнения в дальнейшем.
Прерывание программы
В операционной системе Linux сигналы представляют собой механизм коммуникации между процессами и ядром. Сигналы могут быть посланы процессу для уведомления его о различных событиях или для прерывания его выполнения.
Одним из важных типов сигналов являются сигналы прерывания программы. Эти сигналы могут быть посланы процессу другим процессом или ядром системы для прерывания его обычного выполнения и выполнения действий, связанных с обработкой этого сигнала.
Примером сигнала прерывания программы является сигнал SIGINT, который может быть послан процессу при нажатии комбинации клавиш CTRL+C в терминале. Когда процесс получает этот сигнал, он может выполнить определенные действия, например, завершить свою работу или вывести сообщение.
В таблице ниже представлен некоторые из наиболее часто используемых сигналов прерывания программы в Linux, их идентификаторы и описание:
| Сигнал | Идентификатор | Описание |
|---|---|---|
| SIGINT | 2 | Сигнал прерывания терминала. Посылается при нажатии CTRL+C. |
| SIGQUIT | 3 | Сигнал прерывания терминала с трассировкой. Посылается при нажатии CTRL+\. |
| SIGSTOP | 19 | Сигнал остановки процесса. Останавливает выполнение процесса. |
Каждый сигнал имеет свой уникальный идентификатор, который определяет тип сигнала. При получении сигнала процесс может зарегистрировать обработчик этого сигнала, который будет вызван для обработки сигнала.
Прерывание программы является важным аспектом работы в операционной системе Linux. Знание списка сигналов и их особенностей помогает разработчикам и системным администраторам эффективно обрабатывать сигналы и создавать надежные и стабильные программы.
Сигнал SIGKILL
Не существует возможности перехватить или игнорировать SIGKILL, поскольку он напрямую передается ядру операционной системы, обеспечивая высокую надежность при завершении процессов.
Читайте также: Подробная инструкция по входу в личный кабинет на сайте Суточнору.
Запуская SIGKILL, вы обязательно уничтожаете процесс без предупреждения, сохраняя при этом его различные ресурсы и открытые файлы в аварийном порядке.
Важно отметить, что SIGKILL не может быть заблокирован или остановлен. Он гарантирует полное и безопасное завершение процесса в самые краткие сроки. Однако, из-за этого процессы, завершаемые с помощью SIGKILL, могут оставаться в «незавершенном» состоянии, что потенциально может привести к потере данных.
Сигнал SIGSTOP
Сигнал SIGSTOP является немаскируемым и не может быть перехвачен или проигнорирован пользовательским процессом. Это означает, что процесс не может предотвратить свою остановку с помощью обработчика сигналов. Задача SIGSTOP — надежно остановить выполнение процесса, даже если он находится в «бесконечном» цикле или выполняет другую критическую операцию.
Применение сигнала SIGSTOP может быть полезным во множестве ситуаций, например, для временной остановки выполнения процесса при отладке программы или для приостановки работы процесса при выполнении системной операции.
Единственным способом возобновить выполнение процесса после получения сигнала SIGSTOP является отправка сигнала SIGCONT. Это позволяет контролировать остановку и возобновление работы процесса из других процессов или с помощью системных программ, таких как отладчик.
Уведомление об ошибках
В Linux ошибки и исключительные ситуации обрабатываются с помощью сигналов. Сигналы — это асинхронные уведомления ядра ОС для процесса о возникновении определенного события. Каждый сигнал имеет свой уникальный номер и может быть обработан или проигнорирован процессом или программой. Сигналы могут быть отправлены процессу из различных источников, включая сам процесс, ядро ОС или другие процессы.
Уведомление об ошибках — это одно из основных использований сигналов в Linux. Когда возникает критическая ошибка или необрабатываемое исключение, ядро ОС отправляет соответствующий сигнал процессу, который запустил программу, в которой произошла ошибка. Процесс может затем обрабатывать этот сигнал или завершить свою работу, записав информацию об ошибке в журнал или отправив ее разработчикам для дальнейшего анализа.
Обработка уведомлений об ошибках в Linux важна для обеспечения безопасности и надежности работы программ. Программисты и системные администраторы могут использовать сигналы для ловли ошибок, регистрации возникших проблем и принятия соответствующих мер по их исправлению или устранению.
Кроме того, уведомление об ошибках также может быть использовано для отладки программ. При возникновении ошибки или исключительной ситуации важно получить информацию о том, где именно произошла ошибка, какие данные вызвали ошибку и какое состояние системы в момент ошибки. Сигналы могут быть настроены для перехвата и обработки этих данных, что облегчает поиск и устранение ошибок в программе.
Читайте также: Апатия в отношениях: почему многим людям они не нужны
В целом, уведомление об ошибках — это важный инструмент для разработчиков и пользователей системы Linux, который позволяет эффективно управлять и обрабатывать ошибки в программном обеспечении. Благодаря сигналам и системе обработки ошибок, разработчики могут создавать более надежные и безопасные программы, а пользователи могут испытывать меньше проблем и удобно отлаживать программы при необходимости.
Сигнал SIGSEGV
Обычно, возникновение сигнала SIGSEGV свидетельствует о наличии ошибок в программе. При получении данного сигнала, операционная система прерывает работу процесса и уничтожает его, чтобы предотвратить возможное повреждение памяти и предотвратить сбой системы в целом.
Часто причиной возникновения сигнала SIGSEGV является попытка доступа к нулевому указателю или выход за границы массива. Это может происходить при ошибочной работе с памятью, такой как неправильное разыменование указателя или чтение данных за пределами выделенного блока памяти.
При обработке сигнала SIGSEGV можно использовать функцию signal(), чтобы установить собственный обработчик данного сигнала. Это позволяет завершить программу корректным образом или выполнить дополнительные действия, например, записать информацию об ошибке в лог или вывести сообщение на экран.
Сигнал SIGSEGV является важным инструментом для отслеживания и исправления ошибок в программном коде, поскольку его возникновение указывает на наличие проблемы, которую необходимо исправить. При разработке программ следует уделять особое внимание обработке данного сигнала и избегать допущений, которые могут привести к его возникновению.
| Номер | Название | Описание |
|---|---|---|
| 11 | SIGSEGV | Ошибка доступа к памяти. |
Сигнал SIGBUS
Когда возникает ошибка шины данных, операционная система генерирует сигнал SIGBUS, который отправляется процессу, выполняющему некорректную операцию. Этот сигнал указывает на проблему в работе памяти или доступе к данным.
Обработка сигнала SIGBUS позволяет процессу корректно завершиться или выполнить необходимые действия для восстановления работы программы при возникновении ошибки шины данных.
Считаете эту инструкцию неправильной? Не работает официальный сайт или личный кабинет? Обязательно напишите об этом нам в комментариях! Мы исправим проблему )