Как работают Unix сигналы? - PullRequest
17 голосов
/ 17 ноября 2010

Как работают сигналы в Unix? Я прошел В. Р. Стивенса, но не смог понять. Пожалуйста, помогите мне.

Ответы [ 4 ]

40 голосов
/ 17 ноября 2010

Приведенное ниже объяснение не является точным, и некоторые аспекты того, как это работает, различаются в разных системах (и, возможно, даже в одной и той же ОС на разных аппаратных средствах для некоторых частей), но я думаю, что обычно это хорошо достаточно , чтобы вы смогли удовлетворить свое любопытство и использовать их.Большинство людей начинают использовать сигналы в программировании даже без такого уровня понимания, но прежде чем я освоился с ними, я хотел их понять.

доставка сигналов

Ядро ОС имеет структуру данных, называемуюблок управления процессом для каждого запущенного процесса, который имеет данные об этом процессе.Это можно найти по идентификатору процесса (PID) и включить таблицу действий с сигналом и ожидающих сигналов.

Когда сигнал отправляется процессу, ядро ​​ОС ищет блок управления процессом этого процесса и анализирует его.таблица действий сигнала, чтобы найти действие для конкретного отправляемого сигнала.Если значение действия сигнала равно SIG_IGN, то новый сигнал забывается ядром.Если значение действия сигнала равно SIG_DFL, то ядро ​​ищет действие обработки сигнала по умолчанию для этого сигнала в другой таблице и выполняет предварительное действие.Если значения являются чем-то еще, то предполагается, что это адрес функции в рамках процесса, которому отправляется сигнал, которому следует вызываться.Значения SIG_IGN и SIG_DFL - это числа, приведенные к указателям на функции, значения которых не являются допустимыми адресами в адресном пространстве процесса (например, 0 и 1, которые находятся на странице 0, которая никогда не отображается в процессе).

Если функция обработки сигнала была зарегистрирована процессом (значение действия сигнала не было ни SIG_IGN, ни SIG_DFL), то для этого сигнала делается запись в таблице ожидающих сигналов, и этот процесс помечается как готовый к RUN (возможно, он ожидал чего-то, например данных, которые станут доступны для вызова read, ожидания сигнала или нескольких других вещей.

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

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

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

Оченьчасто ядру ОС нужно знать, когда возвращается обработчик сигнала.Это связано с тем, что обработчики сигналов принимают аргумент (который может потребовать места в стеке), вы можете заблокировать доставку одного и того же сигнала дважды во время выполнения обработчика сигнала и / или перезапустить системные вызовы после доставки сигнала.Чтобы сделать это немного больше, чем изменения в стеке и указателе инструкций.

Что должно произойти, так это то, что ядро ​​должно заставить процесс сообщить ему, что оно завершило выполнение функции обработчика сигнала. Это может быть сделано путем отображения раздела ОЗУ в адресное пространство процесса, содержащего код для выполнения этого системного вызова, и адреса возврата для функции-обработчика сигнала (верхнее значение в стеке, когда эта функция начала выполняться) в качестве адреса этот код Я думаю, что именно так это и делается в Linux (по крайней мере, в более новых версиях). Другой способ сделать это (я не знаю, будет ли это сделано, но это могло бы быть) - сделать адрес возврата для функции обработчика сигнала недопустимым (например, NULL), что приведет к прерыванию в большинстве систем. , что даст контроль над ядром ОС снова. Как это происходит, не имеет большого значения, но ядро ​​должно снова получить контроль, чтобы исправить стек и знать, что обработчик сигналов завершил работу.

Когда я заглянул в другой вопрос, я узнал

что ядро ​​Linux действительно отображает страницу в процессе для этого, но что фактический системный вызов для регистрации обработчиков сигналов (что sigaction вызывает ) принимает параметр sa_restore, который является адресом, который должен использоваться в качестве адреса возврата от обработчика сигнала, а ядро ​​просто гарантирует, что он будет там помещен. Код по этому адресу выдает системный вызов I'm done (sigreturn), и ядро ​​знает, что обработчик сигнала завершен.

генерация сигнала

Я в основном предполагаю, что вы знаете, как генерируются сигналы в первую очередь. ОС может генерировать их от имени процесса из-за чего-то, например, истечения таймера, умирания дочернего процесса, доступа к памяти, к которой он не должен обращаться, или выдачи инструкции, которой он не должен (либо инструкции, которой не существует) или тот, который является привилегированным), или много других вещей. Случай таймера функционально немного отличается от других, поскольку он может возникать, когда процесс не запущен, и поэтому больше похож на сигналы, отправляемые с помощью системного вызова kill. Для сигналов, не связанных с таймером, отправляемых от имени текущего процесса, они генерируются, когда происходит прерывание, потому что текущий процесс делает что-то не так. Это прерывание обеспечивает управление ядром (так же, как системный вызов), а ядро ​​генерирует сигнал для доставки текущему процессу.

8 голосов
/ 17 ноября 2010

Думайте о сигнальном устройстве как о прерываниях, реализованных ОС (а не в аппаратном обеспечении).

Когда ваша программа весело пересекает свое место выполнения, основанное на main (), эти прерывания могут возникать, вызываяпрограмма, которая должна быть отправлена ​​в вектор (обработчик), запустить там код и затем вернуться в место, где оно было прервано.

Эти прерывания (сигналы) могут возникать из различных источников, например, из-за аппаратных ошибок, таких как доступневерные или смещенные адреса, смерть дочернего процесса, пользовательские сигналы с помощью команды kill или из других процессов с помощью системного вызова kill.Вы потребляете сигналы, назначая для них обработчики, которые отправляются ОС при появлении сигналов.Обратите внимание, что некоторые из этих сигналов не могут быть обработаны, и в результате процесс просто умирает.

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

7 голосов
/ 10 ноября 2014

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

Вот несколько вопросов для рассмотрения:

  • Что, если ядро ​​знает, что сигнал должен быть доставлен процессу X, работающему на CPU_X, но ядро ​​узнает об этом во время работы на CPU_Y (CPU_X! = CPU_Y). Поэтому ядру необходимо остановить запуск процесса на другом ядре.
  • Что если процесс работает в пространстве ядра при получении сигнала? Каждый раз, когда процесс выполняет системный вызов, он входит в пространство ядра и работает со структурами данных и распределением памяти в пространстве ядра. Все ли это происходит в пространстве ядра?
  • Что если процесс спит в пространстве ядра в ожидании какого-то другого события? (чтение, запись, сигнал, опрос, мьютекс - это только некоторые варианты).

Ответы:

  • Если процесс выполняется на другом процессоре, ядро ​​через межпроцессорную связь доставит прерывание другому процессору и сообщение для него. Другой процессор, аппаратно, сохранит состояние и перейдет к ядру на другом процессоре, а затем выполнит доставку сигнала на другой процессор. Это все часть попытки не запускать обработчик сигналов процесса на другом процессоре, что нарушит локальность кэша.
  • Если процесс выполняется в пространстве ядра, он не прерывается. Вместо этого записано, что этот процесс получил сигнал. Когда процесс выходит из пространства ядра (в конце каждого системного вызова), ядро ​​настроит батут для выполнения обработчика сигнала.
  • Если процесс во время работы в пространстве ядра после получения сигнала достигает функции ожидания, то эта функция ожидания (и это является общей для всех функций ожидания в ядре) проверит, есть ли у процесса ожидающий сигнал , Если это так, он не будет переводить процесс в спящий режим, а вместо этого отменит все, что было сделано при входе в ядро, и выйдет в пространство пользователя при настройке батута для выполнения обработчика сигнала, а затем перезапустит систему вызов. Вы можете фактически контролировать, какие сигналы вы хотите прерывать системные вызовы, а какие нет, используя системный вызов siginterrupt(2). Вы можете решить, хотите ли вы, чтобы системные вызовы перезапускались для определенного сигнала, когда вы регистрируете сигнал, используя sigaction(2) с флагом SA_RESTART. Если системный вызов выполняется и прерывается сигналом и не перезапускается автоматически, вы получите возвращаемое значение EINTR (прерванное), и вы должны обработать это значение. Вы также можете посмотреть системный вызов restart_syscall(2) для получения более подробной информации.
  • Если процесс уже находится в режиме ожидания / ожидания в пространстве ядра (фактически весь режим ожидания / ожидания всегда находится в пространстве ядра), он пробуждается из режима ожидания, код ядра очищается после себя и после возврата к пользовательскому пространству переключается на обработчик сигнала. системный вызов автоматически перезапускается по желанию пользователя (очень похоже на предыдущее объяснение того, что происходит, если процесс выполняется в пространстве ядра).

Несколько замечаний о том, почему все это так сложно:

  • Вы не можете просто остановить процесс, выполняющийся в пространстве ядра, так как разработчик ядра выделяет память, делает вещи для структур данных и многое другое. Если вы просто уберете управление, вы испортите состояние ядра и вызовете зависание машины. Код ядра должен быть уведомлен контролируемым образом, что он должен прекратить свою работу, вернуться в пользовательское пространство и разрешить пользовательскому пространству обрабатывать сигнал. Это делается через возвращаемое значение всех (ну, почти всех) спящих функций в ядре. Ожидается, что программисты ядра будут относиться к этим возвращаемым значениям с уважением и действовать соответствующим образом.
  • Сигналы асинхронные.Это означает, что они должны быть доставлены как можно скорее.Представьте себе процесс, в котором есть только один поток, который засыпает на час и получает сигнал.Сон внутри ядра.Таким образом, вы, кроме кода ядра, чтобы проснуться, очистить после себя, вернуться в пользовательское пространство и выполнить обработчик сигнала, возможно, перезапустив системный вызов после завершения обработчика сигнала.Вы, конечно, не ожидаете, что этот процесс выполнит обработчик сигнала только через час.Тогда вы ожидаете, что сон возобновится.Простое пользовательское пространство и специалисты по ядру предпринимают большие усилия, чтобы разрешить это.
  • В целом все сигналы подобны обработчикам прерываний, но для пользовательского пространства.Это хорошая аналогия, но не идеальная.Хотя обработчики прерываний генерируются аппаратными средствами, некоторые обработчики сигналов происходят из аппаратного обеспечения, но большинство из них являются просто программными (сигнал о смерти дочернего процесса, сигнал из другого процесса с использованием системного вызова kill(2) и более).

Таккакова задержка обработки сигнала?

  • Если при получении сигнала выполняется какой-то другой процесс, то он до планировщика ядра решает, разрешить ли другому процессу закончить свой временной интервал и только тогдадоставить сигнал или нет.Если вы работаете в обычной системе Linux / Unix, это означает, что вы можете задержаться на 1 или более временных интервалов, прежде чем получите сигнал (что означает миллисекунды, эквивалентные вечности).
  • Когда вы получаете сигнал, если ваш процесс имеет высокий приоритет или другие процессы уже получили свой временной интервал, вы получите сигнал довольно быстро.Если вы работаете в пространстве пользователя, вы получите его «немедленно», если вы работаете в пространстве ядра, вы вскоре достигнете функции ожидания или вернетесь из ядра, и в этом случае при возврате в пространство пользователя будет вызван ваш обработчик сигнала.Обычно это короткое время, так как в ядре тратится не так много времени.
  • Если вы спите в ядре, и ничто иное не находится выше вашего приоритета или не требует запуска, поток ядра обрабатывает вашу системуcall вызывается, очищается после всего, что он делал на пути к ядру, возвращается в пользовательское пространство и выполняет ваш сигнал.Это не займет много времени (мы говорили здесь микросекунды).
  • Если вы используете версию Linux в реальном времени и ваш процесс имеет наивысший приоритет в реальном времени, вы получите сигнал очень скоро после того, каксрабатывает.Говорили 50 микросекунд или даже лучше (зависит от других факторов, в которые я не могу вдаваться).
1 голос
/ 23 декабря 2015

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

Чтобы понять это, проверьте следующую ссылку.

https://unix.stackexchange.com/questions/80044/how-signals-work-internally

http://www.linuxjournal.com/article/3985

http://www.linuxprogrammingblog.com/all-about-linux-signals?page=show

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...