Ответ 1
Объяснение ниже не является точным, и некоторые аспекты того, как это работает, различаются между различными системами (и, возможно, даже одной и той же ОС на разных аппаратных средствах для некоторых частей), но я думаю, что это обычно достаточно хорошо для вас, чтобы удовлетворить ваши достаточно любопытства, чтобы использовать их. Большинство людей начинают использовать сигналы в программировании, даже не понимая этого уровня понимания, но прежде, чем мне стало удобно их использовать, я хотел их понять.
доставка сигнала
Ядро ОС имеет структуру данных, называемую блоком управления процессом для каждого процесса, который имеет данные об этом процессе. Это можно проверить идентификатором процесса (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
. Для сигналов, не связанных с таймером, отправленных от имени текущего процесса, они генерируются, когда происходит прерывание, потому что текущий процесс делает что-то неправильно. Это прерывание дает управление ядром (как и системный вызов), и ядро генерирует сигнал, который должен быть доставлен в текущий процесс.