Главная страница Межпроцессное взаимодействие (состязание) должен при помощи упомянутых выше структур получить сигнал. Если данный сигнал обрабатывается, его необходимо доставить процессу. Для этого требуется сначала сохранить информацию о процессе, чтобы после обработки восстановить его нормальное состояние. Эта информация сохраняется в стеке процесса, причем предварительно делается проверка наличия достаточного места в стеке. Стеком заведует менеджер памяти, который и выполняет этот контроль, а затем, чтобы поместить информацию в стек, вызывает задачу системы, которая также изменяет значение счетчика команд процесса, чтобы выполнился код обработчика. Когда обработчик завершается, делается системный вызов sigreturn. Посредством этого вызова менеджер памяти и ядро участвуют в восстановлении контекста - сигналов и регистров процесса, возвращая его к обычному режиму работы. Если сигнал не обрабатывается, выполняется действие по умолчанию. При этом, если необходимо сбросить дамп ядра (то есть записать образ процесса в файл для последующего анализа под отладчиком), может быть затронута файловая система, а если процесс должен быть прекращен, затрагивается как менеджер памяти, так и файловая система и ядро. Наконец, если сигнал адресован фуппе процессов, менеджер памяти может предписать повторить эти действия несколько раз. Сигналы, которые знает MINIX, определяются в файле /usr/include/signal.h, согласно стандарту POSIX. Они перечислены в табл. 4.4. В MINIX объявлены все требуемые POSIX сигналы, но пока что не все они поддерживаются. Например, стандартом определен набор сигналов для управления задачами, при помощи которых можно переводить задачу в фоновый режим и возвращать ее обратно. В MINIX не поддержано управление задачами, но программы, которые генерируют такие сигналы, могут быть перенесены под MINIX. Неподдерживаемые сигналы будут просто игнорироваться. Кроме того, MINIX определяет ряд сигналов, не имеющихся в POSIX, а также несколько синонимов для POSIX-имен, с целью обеспечить совместимость с более старым исходным кодом. Сигналы генерируются либо ядром, либо при помощи системного вызова kill. Ядро MINIX всегда может генерировать сигналы SIGINT, SIGQUIT и SIGALARM, возможность генерировать другие сигналы зависит от аппаратной поддержки. Например, процессоры 8086 и 8088 не умеют обнаруживать недопустимые инструкции, а процессоры 286 и старше при попытке выполнить такую инструкцию уже вызывают прерывание. Это обусловлено возможностями аппаратного обеспечения. Чтобы в ответ на прерывание генерировать сигнал, разработчики операционной системы должны заготовить для этого код. В главе 2 мы видели, что в файле kernel/exception.с как раз содержится нужный код, для разных сочетаний условий. Таким образом, когда MINIX работает на машине с процессором 286 или старше, недопустимая инструкция приведет к появлению сигнала SIGKILL, но на компьютере с процессором 8088 такого никогда не произойдет. То, что оборудование способно генерировать аппаратное прерывание при возникновении некоторой ситуации, не означает, что разработчики операционной системы могут во всем понадеяться на эту возможность. Так, все процессоры Intel, начиная с 286, распознают несколько типов нарушений целостности памяти, вызывающих исключения. Код в файле kernel/exception.h преобразует эти исключения в сигналы SIGSEGV. Таблица 4.4. Сигналы MINIX, регламентированные POSlX. Знак (*) означает, что сигнал зависит от аппаратной поддержки, сигналы с пометкой (М) не исходят из POSIX и введены в MINIX для поддержания совместимости со старым кодом. В таблицу не попали несколько устаревших имен и синонимов
Нарушениям аппаратных границ стека и границ других сегментов соответствуют разные типы исключений, в расчете на различную их обработку. Тем не менее, в силу особенностей работы MINIX с памятью, не все возникающие нарушения могут быть обнаружены. Аппаратные регистры задают базовый адрес сегмента и его длину. В MINIX базовый адрес аппаратного сегмента стека совпадает с базовым адресом аппаратного сегмента данных, но аппаратно заданный размер сегмента данных превышает контролируемую программно границу. Другими словами, контролируемый аппаратно размер сегмента данных соответствует ситуации, когда стек уменьшился до нуля. Аналогично, заданный аппаратно объем стека соответствует нулевому объему данных. Таким образом, хотя некоторые из нарушений и поддаются аппаратному обнаружению, наиболее вероятная ошибка - повреждение области данных из-за переполнения стека - не де- тактируется. Это следствие того, что с точки зрения аппаратных регистров и таблиц дескрипторов сегменты данных и стека пересекаются. Потенциально возможно добавить в ядро код, который бы проверял регистры процесса всякий раз после того, как процесс получает управление, и в случае нарушения программно заданных границ сегментов данных или стека генерировал бы сигнал SIGSEGV. Но не совсем понятно, стоит ли это делать, ведь аппаратные ловушки в силах обнаружить сбой по доступу сразу же после того, как он произошел. Программная же проверка может случиться спустя много тысяч инструкций после момента переполнения, когда обработчик прерывания уже мало на что годится и восстановление неосуществимо. Где бы ни брали свое начало сигналы, менеджер памяти обрабатывает их одинаково. Сначала для каждого процесса-получателя делается набор проверок, с целью убедиться, можно ли передать ему сигнал. Один процесс может передавать другому сигнал в том случае, если первый принадлежит суперпользователю, либо если его эффективный UID равен реальному или эффективному UID второго. Но существует еще несколько условий, вмешивающихся в отправку сигнала. Например, нельзя передавать сигнал зомби. Также процессу нельзя передавать сигнал, если он явно сделал вызов sigaction, чтобы игнорировать возможный сигнал, или вызвал sigprocmask, чтобы его блокировать. Блокировка сигнала и пренебрежение им - разные вещи. Заблокированный сигнал запоминается и передается процессу, когда тот снимет блокировку (если он ее вообще снимет). Наконец, если у процесса-получателя недостаточно места в стеке, этот процесс принудительно завершается. Когда все тесты пройдены, сигнал можно отправлять. Если процесс не сделал ничего, чтобы обработать сигнал, то и никакой информации передавать ему не требуется. В таком случае менеджер памяти выполняет обработку сигнала по умолчанию, обычно это означает либо завершение процесса, либо сброс дампа памяти в файл. Несколько сигналов по умолчанию игнорируются. Сигналы, которые в табл. 4.4 обозначены как не поддерживаемые, рекомендованы стандартом POSIX, но в MINIX игнорируются. Обработка сигнала процессом заключается в исполнении заданного пользователем кода обработчика, адрес обработчика сохранен в структуре sigaction в таблице процессов. В главе 2 мы видели, как в кадр стека в пределах таблицы процессов записывается информация, необходимая для восстановления процесса после его прерывания. Путем модификации кадра стека процесса-получателя сигнала можно добиться того, чтобы в следующий раз, когда процесс получит управление, он начал бы исполнять код обработчика сигнала. Посредством манипуляций с собственным стеком процесса, в пользовательском пространстве, делается так, чтобы по завершении обработчика произошел системный вызов sigreturn. Явно эта подпрограмма из пользовательского кода никогда не вызывается. Вызов исполняется за счет того, что ядро помещает его адрес в стек, то есть здесь это адрес возврата, на который осуществляется переход после завершения обработчика. Вызов sigreturn восстанавливает исходный кадр стека получившего сигнал процесса, чтобы тот мог продолжить свое выполнение с момента, на котором его застал сигнал.
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |