Программирование >>  Структура ядра и системные вызовы 

1 ... 40 41 42 [ 43 ] 44 45 46 ... 98


ячейке соответствующей записи таблицы процессов сигнальный флаг для процесса-получателя. Если процесс-получатель находится в режиме ожидания (например, ожидает заверщения порожденного процесса или выполняет API pause), ядро будит его и ставит в очередь на выполнение. Если процесс-получатель работает, ядро проверяет U-область этого процесса, которая содержит массив спецификаций обработки сигналов, где каждый элемент массива соответствует сигналу, определенному в системе. С помощью этого массива ядро определяет, каким образом процесс будет реагировать на ожидающий обработки сигнал. Если элемент массива, соответствующий данному сигналу, содержит нулевое значение, процесс выполняет действие, предусмотренное по умолчанию. Если элемент массива содержит 1, процесс игнорирует сигнал и ядро отбрасывает его. Наконец, если элемент массива содержит другое значение, оно используется как указатель на функцию-обработчик сигналов, определенную пользователем. Ядро настраивает процесс на немедленное выполнение этой функции, и если функция-обработчик не заверщает процесс, то он возвращается в ту точку, где был получен сигнал (или в другую точку, куда обработчик сигнала передает управление).

Для случая, когда обработки процессом ожидают разные сигналы, порядок передачи их в процесс-получатель не определен. Если обработки ждут несколько экземпляров одного сигнала, то рещение вопроса о том, сколько экземпляров будет доставлено в процесс - один или все, зависит от реализации ОС. В UNIX System V.3 каждый сигнальный флаг в ячейке таблицы процессов регистрирует только наличие ожидающего сигнала; определить же общее количество сигналов невозможно.

Способ обработки перехваченных сигналов, который принят в UNIX System V.2 и более ранних версиях, был раскритикован как ненадежный. Поэтому в BSD UNIX 4.2 (а также более поздних версиях) и POSIX.l для обработки перехваченных сигналов используются другие механизмы.

В частности, в UNIX System V.2 и более ранних версиях принят порядок, согласно которому при перехвате сигнала ядро сначала деактивизирует обработчик данного сигнала (в U-области процесса-получателя), а затем вызывает заданную для этого сигнала пользовательскую функцию обработки. Таким образом, если в процесс из разных точек посылается множество экземпляров сигнала, процесс перехватывает только первый экземпляр. Все последующие экземпляры сигнала обрабатываются методом, принятым по умолчанию.

Для того чтобы процесс непрерывно перехватывал множество экземпляров сигнала, он должен при каждом перехвате реинсталлировать функцию-обработчик. При этом, однако, все равно нет гарантии того, что процесс перехватит все экземпляры: с момента вызова обработчика перехваченного сигнала X до момента восстановления метода обработки он находится в режиме принятия стандартного действия, предусмотренного для сигнала X. Если за этот промежуток времени в процесс доставляется еще один экземпляр сигнала X, процессу приходится обрабатывать его по стандартному методу. Возникает состояние состязания, при котором два события наступают одновременно, и предсказать, какое из них сработает первым, невозможно.

В BSD UNIX 4.2 (а также более поздних версиях) и POSIX.l для повыщения надежности обработки сигналов принят иной подход: когда сигнал перехватывается, ядро не деактивизирует функцию-обработчик, поэтому процессу не нужно восстанавливать метод обработки сигнала. Далее, ядро блокирует дальнейщую доставку этого же сигнала в процесс до тех пор, пока функция-обработчик не закончит выполнение. Появляется гарантия того, что она не будет вызываться рекурсивно для множества экземпляров одного сигнала. В UNIX System V.3 бьи введен API sigset, который работает более надежно, чем API signal.

В UNIX System V.4 принят метод обработки сигналов, определенный в POSIX. 1. Тем не менее пользователи могут дать ядру указание использовать метод обработки, принятый в System V.2, отдельно по каждому сигналу. Это делается с помощью API signal.

9.2. Функция signal

Все UNIX-системы и язык программирования ANSI С поддерживают API signal, с помощью которого можно определять методы обработки отдельных сигналов. Прототип функции этого API выглядит следующим образом:

#include <signal.h>

void (*signa/ {\nt signal num, void (*handler)(int)))(int)

Аргументами данного API являются signaljium - идентификатор сигнала (например, SIGINT, SIGTERM и т.д.), определенный в заголовке <sig-nal.h>, и handler - указатель на определенную пользователем функцию-обработчик сигнала. Эта функция должна принимать целочисленный аргумент. Никаких значений она не возвращает.

В приведенном ниже примере производится попытка перехвата сигнала SIGTERM, игнорируется сигнап SIGINT, а по сигналу SIGSEGV выполняется действие, предусмотренное по умолчанию. API pause приостанавливает выполнение вызывающего процесса до тех пор, пока он не будет прерван каким-либо сигналом и соответствующий обработчик сигнала не возвратит результат:

#include <iostream.h>

♦include <signal.h>

/* Функция-обработчик сигналов */

void catch sig( int sig num )

signal(sig num, catch sig); cout catch sig: sig num endl;



/* главная функция int main О

signal (SIGTERM, cat<:h sig) ; signal(SIGINT, SIG IGN); signal(SIGSEGV, SIG DFL); pause();

/* ожидание сигнала */

SIG IGN и SIG DFL - это макросы, определенные в заголовке <signal.h>:

♦define SIG DFL ♦define SIG IGN

void (*)(int)0 void (*)(int)1

SIG IGN задает сигнал, который будет проигнорирован. Будучи посланным в процесс, такой сигнал просто отбрасывается, а сам процесс не прерывается.

SIG DFL сигнализирует, что нужно выполнить действие по умолчанию.

Возвращаемое значение API signal - указатель на предыдущий обработчик данного сигнала. Его можно использовать для восстановления обработчика сигнала:

♦include <signal.h>

int main О

void (*old handler)(int) = signal(SIGINT, SIG IGN); /♦выполнить важнейшие операции обработки*/

signal(SIGINT, old handler); /* восстановить предыдущий обработчик

сигнала

API signal HQ является стандартным для POSIX. 1. При этом он, однако, определен в ANSI С и имеется во всех UNIX-системах. Так как поведение API signal в System V.2 и более ранних версиях отличается от поведения в системах BSD и POSIX. 1, то использовать его в переносимых приложениях не рекомендуется. В BSD UNIX и POSIX. 1 определен новый набор интерфейсов прикладного программирования, предназначенных для манипулирования сигналами. Эти интерфейсы присутствуют во всех системах UNIX и системах, соответствующих стандарту POSIX. 1. Описаны они в следующих двух разделах.

UNIX System V.3 и System V.4 поддерживают API sigset, имеющий тот же прототип и назначение, что и signal:

#include <signal.h>

void (*sigset {int signal num, void {*handler){int))){int)

Аргументы и возвращаемое значение API sigset - те же, что и у signal. Обе функции задают методы обработки для любого указанного в вызове сигнала.

Однако API signal в отличии от API sigset является недостаточно надежным (см. раздел 9.1). Если обработчик сигнала устанавливается с помощью API sigset, то при поступлении нескольких экземпляров этого сигнала обрабатывается один из них, а остальные блокируются. Кроме того, режим обработки сигнала при вызове в SIG DFL не сбрасывается.

9.3. Сигнальная маска

Каждый процесс в UNIX-системе (BSD 4.2 и выше, а также System V.4) и в POSIX. 1 имеет сигнальную маску, которая определяет, какие сигналы, из посылаемых процессу блокируются. Разблокирование и обработка блокированного сигнала выполняются процессом-получателем. Если сигнал задан как игнорируемый и блокируемый, то решение вопроса о том, будет ли он при передаче в процесс отброшен или переведен в режим ожидания, зависит от реализации ОС.

При создании процесс наследует сигнальную маску своего родителя, но ни один сигнал, ожидающий обработки родительским процессом, к порожденному процессу не пропускается. Процесс может запрашивать и создавать сигнальную маску с помощью API sigprocmask:

#include <signal.h>

int sigprocmask (int cmd, const sigset t* new mask, sigset t* old mask)

Аргумент newjnask определяет набор сигналов, которые будут добавлены в сигнальную маску вызывающего процесса или удалены из нее, а аргумент cmd указывает, как значение newjnask должно быть использовано данным API. Ниже перечислены возможные значения аргумента cmd и варианты использования аргумента newjnask.

Значение cmd

Выполняемые действия

S1G.

.SETMASK

Заменяет сигнальную маску вызывающего процесса значением, указанным в аргументе newjnask

.BLOCK

Добавляет сигналы, указанные в аргументе new mask, в сигнальную маску вызывающего процесса

SIG.

.UNBLOCK

Удаляет из сигнальной маски вызывающего процесса сигналы, указанные в аргументе newjnask

Если фактическим аргументом newjnask является NULL-указатель, то аргумент cmd игнорируется и сигнальная маска текущего процесса не изменяется.

Аргумент oldjnask - это адрес переменной типа sigsetJ, которой присваивается исходная сигнальная маска вызывающего процесса до вызова



sigprocmask. Если фактическим аргументом oldjnask является NULL-указатель, то предыдущая сигнальная маска не возвращается.

В случае успещного заверщения вызов sigprocmask возвращает О, в противном случае возвращает -L Причиной неудачи может стать неверное задание адресов в аргументах newmask и (или) old mask.

Тип данных sigsett определяется в заголовке <signal.h>. Данные этого типа представлякуг собой набор битов, каждый из которых является флагом, соответствующим одному определенному в данной системе сигналу.

В BSD UNIX и POSIX.l определен набор API, известных как семейство функций sigsetops, которые устанавливают, сбрасывают и запрашивают флаги сигналов в переменной типа sigsett:

#include <signal.h>

int sigemptyset ( sigset t* sigmask ); int sigaddset ( 5igset t* sigmask, const int signal num ); int sigdelset ( 5ig5et t* sigmask, const int signal num ); int sigfillset ( 5ig5et t* sigmask );

int sigismember ( const 5ig5et t* sigmask, const int signal num);

Производимые с помощью этих API действия: sigemptyset сбрасывает все сигнальные флаги в маске, указанной в аргументе sigmask; sigaddset усгашв-ливает флаг, соответствующий сигналу signal num, в маске sigmask; sigdelset очищает флаг, соответствующий сигналу signal num, в маске sigmask; sigfdlset устанавливает все флаги сигналов в маске sigmask.

В случае успешного выполнения вызовы sigemptyset, sigaddset, sigdelset, jj/fee возвращают О, в случае неудачи -1. Возможной причиной неудачи могут бьггь неверно заданные аргументы sigmask и (или) signal num.

API sigismember возвращает 1, если в маске sigmask установлен флаг, соответствующий сигналу signaljium. В случае неудачи возвращается 0.

В следующем примере проверяется наличие сигнала SIGINT в сигнальной маске процесса. Если такового там нет, он будет добавлен в маску. Затем программа удаляет сигнал SIGSEGV из сигнальной маски процесса:

finclude <stdio.h> finclude <signal.h> int mainO {

sigset t sigmask; sigemptyset(ssigmask);

if (sigprocmask(0, 0, &sigmask)== -1) (

perror( sigprocmask ); exit(l);

else sigaddset(Ssigmask, SIGINT);

/♦инициализировать набор*/ /♦получить сигнальную маску*/

/♦установить флаг SIGINT*/

sigdelset(isigmask, SIGSEGV); 7*убрать флаг SIGSEGV*/ if (sigprocmask(SIG SETMASK, Ssigmask, 0)== -1)

perror ( sigprocmask ) ; /*установить новую сигнальную маску*/

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

Процесс может запросить перечень сигналов, ожидающих его, с помощью API sigpending:

#include <signal.h>

int sigpending ( sigset t* sigmask );

Аргумент sigmask этого API является переменной типа sigsetj. Этой переменной присваивается набор сигналов, ожидающих вызывающий процесс. В случае успешного выполнения этот API возвращает нуль, а в случае неудачи -1.

API sigpending определяет наличие сигналов, ожидающих данный процесс, и задает специальные методы обработки этих сигналов до того, как процесс вызовет API sigprocmask для их разблокирования.

В нашем примере на консоль выдается сообщение о том, ожидает ли обработки сигнал SIGTERM:

finclude <iostream.h> finclude <stdio.h> finclude <signal.h>

int mainO (

sigset t sigmask;

sigemptyset(ssigmask) ;

if (sigpending(Ssigmask)== -1)

perror( sigpending ); else cout SIGTERM signal is:

(sigismember(ssigmask,SIGTERM) ? Set endl;

No Set )

Следует отметить, что помимо перечисленных в UNIX System V.3 и System V.4 в качестве упрощенных средств манипулирования сигнальными масками поддерживаются следующие API:



1 ... 40 41 42 [ 43 ] 44 45 46 ... 98

Монтаж вентиляции цены на монтаж evtan.ru.
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика