АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомДругоеЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

Лабораторная работа №5

Читайте также:
  1. AKM Работа с цепочками событий
  2. File — единственный объект в java.io, который работает непосредственно с дисковыми файлами.
  3. III. ВЛИЯНИЕ ФАКТОРОВ РАБОЧЕЙ СРЕДЫ НА СОСТОЯНИЕ ЗДОРОВЬЯ РАБОТАЮЩИХ.
  4. VI. Работа сновидения
  5. VIII. Работа над задачей
  6. А) Работа сгущения.
  7. Административная контрольная работа по дисциплине
  8. Аудиторная работа
  9. Б) Работа смещения.
  10. Безопасность при эксплуатации стационарных сосудов и аппаратов, работающих под давлением. Техническое освидетельствование.
  11. Безупречность. Работа над безупречностью на разных этапах
  12. БОГАТЫЕ НЕ РАБОТАЮТ НА ДЕНЬГИ

“Сигналы”

Процессы в UNIX используют много разных механизмов взаимодействия. Одним из них являются сигналы. Сигналы - это асинхронные события. Что это значит? Сначала объясним, что такое синхронные события: я два раза в день подхожу к почтовому ящику и проверяю - нет ли в нем почты (событий). Во-первых, я произвожу опрос - "нет ли для меня события?", в программе это выглядело бы как вызов функции опроса и, может быть, ожидания события. Во-вторых, я знаю, что почта может ко мне прийти, поскольку я подписался на какие-то газеты. То есть я предварительно заказывал эти события. Схема с синхронными событиями очень распространена. Кассир сидит у кассы и ожидает, пока к нему в окошечко не заглянет клиент. Поезд периодически проезжает мимо светофора и останавливается, если горит красный. Функция Си пассивно “спит” до тех пор, пока ее не вызовут; однако она всегда готова выполнить свою работу (обслужить клиента). Такое ожидающее заказа (события) действующее лицо называется сервер.

После выполнения заказа сервер вновь переходит в состояние ожидания вызова. Итак, если событие ожидается в специальном месте и в определенные моменты времени (издается некий вызов для опроса) - это синхронные события. Канонический пример – функция gets, которая задержит выполнение программы, пока с клавиатуры не будет введена строка. Большинство ожиданий внутри системных вызовов - синхронны. Ядро ОС выступает для программ пользователей в роли сервера, выполняющего системные вызовы (хотя и не только в этой роли - ядро иногда предпринимает и активные действия: передача процессора другому процессу через определенное время (режим разделения времени), убивание процесса при ошибке, и.т.п.).

Сигналы - это асинхронные события. Они приходят неожиданно, в любой момент времени - вроде телефонного звонка. Кроме того, их не требуется заказывать – сигнал процессу может поступить совсем без повода. Аналогия из жизни такова: человек сидит и пишет письмо. Вдруг его окликают посреди фразы - он отвлекается, отвечает на вопрос, и вновь продолжает прерванное занятие. Человек не ожидал этого оклика (быть может, он готов к нему, но он не озирался по сторонам специально). Кроме того, сигнал мог поступить когда он писал 5-ое предложение, а мог - когда 34-ое. Момент времени, в который произойдет прерывание, не фиксирован. Сигналы имеют номера, причем их количество ограничено - есть определенный список допустимых сигналов. Номера и мнемонические имена сигналов перечислены в include-файле < signal. h > и имеют вид SIG нечто. Допустимы сигналы с номерами 1.. NSIG -1, где NSIG определено в этом файле. При получении сигнала мы узнаем его номер, но не узнаем никакой иной информации: ни от кого поступил сигнал, ни что от нас хотят.

Просто "звонит телефон". Чтобы получить дополнительную информацию, наш процесс должен взять ее из другого известного места; например - прочесть заказ из некоторого файла, об имени которого все наши программы заранее "договорились". Сигналы процессу могут поступать тремя путями:

· от другого процесса, который явно посылает его нам вызовом kill (pid, sig); где pid - идентификатор (номер) процесса-получателя, а sig - номер сигнала. Послать сигнал можно только родственному процессу - запущенному тем же пользователем.

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

· от пользователя - с клавиатуры терминала можно нажимом некоторых клавиш послать сигналы SIGINT и SIGQUIT. Собственно, сигнал посылается драйвером терминала при получении им с клавиатуры определенных символов. Так можно прервать зациклившуюся или надоевшую программу.

Процесс-получатель должен как-то отреагировать на сигнал. Программа может:

· проигнорировать сигнал (не ответить на звонок);

· перехватить сигнал (снять трубку), выполнить какие-то действия, затем продолжить прерванное занятие;

· быть убитой сигналом (звонок был подкреплен броском гранаты в окно).

В большинстве случаев сигнал по умолчанию убивает процесс-получатель. Однако процесс может изменить это умолчание и задать свою реакцию явно. Это делается вызовом signal:

#include < signal. h >

void (* signal (int sig, void (* react)())) ();

Параметр react может иметь значение:

SIG _ IGN

сигнал sig будет отныне игнорироваться. Некоторые сигналы (например SIGKILL)невозможно перехватить или проигнорировать.

SIG _ DFL

восстановить реакцию по умолчанию (обычно - смерть получателя).

Например

void fr (gotsig){..... } /* обработчик */

... signal (sig, fr);... /* задание реакции */

Тогда при получении сигнала sig будет вызвана функция fr, в которую в качестве аргумента системой будет передан номер сигнала, действительно вызвавшего ее - gotsig == sig. Это полезно, т.к. можно задать одну и ту же функцию в качестве реакции для нескольких сигналов:

... signal (sig1, fr); signal (sig2, fr);...

После возврата из функции fr () программа продолжится с прерванного места. Перед вызовом функции-обработчика реакция автоматически сбрасывается в реакцию поумолчанию SIG _ DFL, а после выхода из обработчика снова восстанавливается в fr. Это значит, что во время работы функции-обработчика может прийти сигнал, который убьет программу.

Приведем список некоторых сигналов; полное описание посмотрите в документации. Колонки таблицы: G - может быть перехвачен; D - по умолчанию убивает процесс (k), игнорируется (i); C - образуется дамп памяти процесса: файл core, который затем может быть исследован отладчиком adb; F - реакция на сигнал сбрасывается; S – посылается обычно системой, а не явно.

сигнал G D C F S смысл

SIGTERM + k - + - завершить процесс

SIGKILL - k - + - убить процесс

SIGINT + k - + - прерывание с клавиш

SIGQUIT + k + + - прерывание с клавиш

SIGALRM + k - + + будильник

SIGILL + k + - + запрещенная команда

SIGBUS + k + + + обращение по неверному

SIGSEGV + k + + + адресу

SIGUSR1, USR2 + i - + - пользовательские

SIGCLD + i - + + смерть потомка

Сигнал SIGILL используется иногда для эмуляции команд с плавающей точкой, что происходит примерно так: при обнаружении “запрещенной” команды для отсутствующего процессора "плавающей" арифметики аппаратура дает прерывание и система посылает процессу сигнал SIGILL. По сигналу вызывается функция-эмулятор плавающей арифметики (подключаемая к выполняемому файлу автоматически), которая и обрабатывает требуемую команду. Это может происходить много раз, именно поэтому реакция на этот сигнал не сбрасывается.

SIGALRM посылается в результате его заказа вызовом alarm () (см. ниже).

Сигнал SIGCLD посылается процессу-родителю при выполнении процессом-потомком системного вызова exit (или при смерти вследствие получения сигнала). Обычно процесс-родитель при получении такого сигнала (если он его заказывал) реагирует, выполняя в обработчике сигнала вызов wait (см. ниже). По умолчанию этот сигнал игнорируется.

Реакция SIG _ IGN не сбрасывается в SIG _ DFL при приходе сигнала, т.е. сигнал игнорируется постоянно.

Вызов signal возвращает старое значение реакции, которое может быть запомнено в переменную вида void (* f)(); а потом восстановлено.

Синхронное ожидание (системный вызов) может иногда быть прервано асинхронным событием (сигналом), но об этом ниже.

Некоторые версии UNIX предоставляют более развитые средства работы с сигналами. Опишем некоторые из средств, имеющихся в BSD (в других системах они могут быть смоделированы другими способами).

Пусть у нас в программе есть "критическая секция", во время выполнения которой приход сигналов нежелателен. Мы можем "заморозить" (заблокировать) сигнал, отложив момент его поступления до "разморозки":

|

sighold (sig); заблокировать сигнал

|:

КРИТИЧЕСКАЯ:<---процессу послан сигнал sig,

СЕКЦИЯ: но он не вызывает реакцию немедленно,

|: а "висит", ожидая разрешения.

|:

sigrelse (sig); разблокировать

|<----------- sig

| накопившиеся сигналы доходят,

| вызывается реакция.

Если во время блокировки процессу было послано несколько одинаковых сигналов sig, то при разблокировании поступит только один. Поступление сигналов во время блокировки просто отмечается в специальной битовой шкале в паспорте процесса (примерно так):

mask |= (1 << (sig - 1));

и при разблокировании сигнала sig, если соответствующий бит выставлен, то приходит один такой сигнал (система вызывает функцию реакции). То есть sighold заставляет приходящие сигналы "накапливаться" в специальной маске, вместо того, чтобы немедленно вызывать реакцию на них. А sigrelse разрешает "накопившимся" сигналам (если они есть) прийти и вызывает реакцию на них.

Функция

sigset (sig, react);

аналогична функции signal, за исключением того, что на время работы обработчика сигнала react, приход сигнала sig блокируется; то есть перед вызовом react как бы делается sighold, а при выходе из обработчика - sigrelse. Это значит, что если во время работы обработчика сигнала придет такой же сигнал, то программа не будет завершена, а “запомнит” пришедший сигнал, и обработчик будет вызван повторно (когда сработает sigrelse).

Функция

sigpause (sig);

вызывается внутри "рамки"

sighold (sig);

...

sigpause (sig);

...

sigrelse (sig);

и вызывает задержку выполнения процесса до прихода сигнала sig. Функция разрешает приход сигнала sig (обычно на него должна быть задана реакция при помощи sigset), и “засыпает” до прихода сигнала sig. В UNIX стандарта POSIX для управления сигналами есть вызовы sigaction, sigprocmask, sigpending, sigsuspend.

Сигнал прерывания можно игнорировать. Это делается так:

signal (SIGINT, SIG _ IGN);

Такую программу нельзя прервать с клавиатуры. Напомним, что реакция SIG _ IGN сохраняется при приходе сигнала.

Системный вызов, находящийся в состоянии ожидания какого-то события (read - ждущий нажатия кнопки на клавиатуре, wait ждущий окончания процесса-потомка, и.т.п.), может быть прерван сигналом. При этом сисвызов вернет значение "ошибка" (-1) и errno станет равно EINTR. Это позволяет нам писать системные вызовы с выставлением таймаута: если событие не происходит в течение заданного времени, то завершить ожидание и прервать сисвызов. Для этой цели используется вызов alarm (sec), заказывающий посылку сигнала SIGALRM нашей программе через целое число sec секунд (0 - отменяет заказ):

#include < signal. h >

void (* oldaction)(); int alarmed;

/* прозвонил будильник */

void onalarm (nsig){ alarmed ++; }

...

/* установить реакцию на сигнал */

oldaction = signal (SIGALRM, onalarm);

/* заказать будильник через TIMEOUT сек. */

alarmed = 0; alarm (TIMEOUT /* sec */);

 

sys _ call (...); /* ждет события */

// если нас сбил сигнал, то по сигналу будет

// еще вызвана реакция на него - onalarm

 

if(alarmed){

// событие так и не произошло.

// вызов прерван сигналом т.к. истекло время.

}else{

alarm (0); /* отменить заказ сигнала */

// событие произошло, сисвызов успел

// завершиться до истечения времени.

}

signal (SIGALRM, oldaction);

Следующая программа выдает текущее время каждые 3 секунды:

#include <signal.h>

#include <time.h>

#include <stdio.h>

void tick (nsig){

time_t tim; char * s;

signal (SIGALRM, tick);

alarm (3); time (& tim);

s = ctime (& tim);

s [ strlen (s)-1 ] = '\0'; /* обрубить '\n' */

fprintf (stderr, "\r%s", s);

}

main (){ tick (0);

for(;;) pause ();

}

Linux поддерживает нижеописанные сигналы. Некоторые номера сигналов зависят от используемой архитектуры. Сначала идут сигналы, описанные в стандарте POSIX.1.

Сигнал Значение Действие Описание
       
      либо завершение управляющего процесса
SIGINT   A Прерывание с клавиатуры
SIGQUIT   C Выход с клавиатуры
SIGILL   C Несуществующая инструкция
SIGABRT   C Сигнал прерывания, посланный функцией abort (3)
SIGFPE   C Ошибка операций с плавающей запятой
SIGKILL   AEF Kill-сигнал
SIGSEGV   C Обращение к запретной области памяти
SIGPIPE   A Оборванный канал: запись в канал, из которого не читают
SIGALRM   A Сигнал таймера от функции alarm (2)
SIGTERM   A Сигнал завершения
SIGUSR1 30,10,16 A Первый сигнал, определяемый пользователем
SIGUSR2 31,12,17 A Второй сигнал, определяемый пользователем
SIGCHLD 20,17,18 B Потомок остановлен или прекратил выполнение
SIGCONT 19,18,25   Продолжить выполнение, если остановлен
SIGSTOP 17,19,23 DEF Приостановить выполнение процесса
SIGTSTP 18,20,24 D Останов введен с терминала
SIGTTIN 21,21,26 D ввод с терминала у фонового процесса
SIGTTOU 22,22,27 D вывод на терминал у фонового процесса

Следующие сигналы не входят в стандарт POSIX.1, но описаны в SUSv2.

Сигнал Значение Действие Описание
       
SIGEMT 7,-,7    
SIGSTKFLT -,16,- A Переполнение стека сопроцессора
SIGIO 23,29,22 A I/O теперь возможно (4.2 BSD)
SIGCLD -,-,18   Синоним для SIGCHLD
SIGPWR 29,30,19 A Авария питающего напряжения (System V)
SIGINFO 29,-,-   Синоним для SIGPWR
SIGLOST -,-,- A Потеря файла блокировки
SIGWINCH 28,28,20 B Изменение размеров окна (4.3 BSD, Sun)
SIGUNUSED -,31,- A Неиспользуемый сигнал (в будущем будет SIGSYS)

(Здесь - является признаком того, что сигнал отсутствует; там, где приведено три значения, первое -- для архитектур alpha и sparc, второе для архитектур i386, ppc и sh, последнее для mips. 29-й сигнал -- это SIGINFO / SIGPWR для alpha, но SIGLOST для sparc.)

Буквы в колонке "Действие" имеют следующее значение:

A

Действие по умолчанию -- прекращение выполнения процесса.

B

Действие по умолчанию -- игнорировать сигнал.

C

Действие по умолчанию -- прекращение выполнения процесса и запись дампа памяти.

D

Действие по умолчанию -- приостановка выполнения процесса.

E

Сигнал не может быть перехвачен.

F

Сигнал не может быть проигнорирован.


1 | 2 |

Поиск по сайту:



Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Студалл.Орг (0.014 сек.)