Тема OS17асу. средства обработки сигналов Сигналы (UNIX) - программный механизм, информирующий процесс о наступлении асинхронного события, обеспечивает логическую связь между процессами, а также между процессами и пользователями. Сигналы подобны аппаратному прерыванию, но не использует систему приотритетов и все сигналы обрабатываются одинаково, различаясь по типу сигнала. Процессы могут посылать сигналы друг другу определенных типов. В обмене сигналами может принять участие и ядро ОС (перехват сигналов для трассировки и журналирования). Процесс может ответить на сигнал выполнением некоторых предопределенных действий (по умолчанию), либо выполнить определенную программистом функцию или проигнорировать сигнал. Доставка сигнала выполняется обновлением полей в таблице процесса, которому послан данный сигнал. Каждому типу сигнала соответствует поле в единственный бит, поэтому они не могут накапливаться. Каждый сигнал имеет уникальный номер, однозначно определяющий событие, которым он вызван. Классические версии OS UNIX определяли 16 сигналов с номерами от 1 до 16. В современных версиях OS UNIX список сигналов расширен до 32. Для удобства спецификации сигналов используются символьные мнемоники. Ниже перечислены мнемоники основных сигналов, с кратким описанием событий, инициирующих их появление. Aсинхронные терминальные сигналы 1. SIGHUP - разрыв связи с терминалом; 2. SIGINT - прерывание от терминала, генерируется при нажатии Ctrl-C; 3. SIGQUIT - сигнал выхода, генерируется при нажатии Ctrl-\. Синхронные сигналы ловушки 4. SIGILL - попытка выполнить нелегальную машинную инструкцию; 5. SIGFPE - десятичное переполнение или деление на 0; 6. SIGBUS - ошибка шины при неверной косвенной адресации; 11. SIGSEGV - нарушение границ сегментов адресного пространства; 12. SIGSYS - неверный параметр при обращении к системному вызову; 13. SIGPIPE - разрыв программного канала. Сигнал от таймера реального времени 14. SIGALRM - генерируется ядром при истечении интервала, заданного системным вызовом alarm. Сигналы от событий в других процессах или системе 9. SIGKILL - безусловное завершение процесса; 15. SIGTERM - условное завершение процесса; 16. SIGUSR1 - сигнал, определенный пользователем; 17. SIGUSR2 - сигнал, определенный пользователем; 18. SIGCLD - изменение статуса процесса-потомка; 19. SIGPWR - рестарт по сбою питания; 23. SIGSTOP - приостановка выполнения процесса; 24. SIGCONT - возобновление выполнения приостановленного процесса. Примеры сигналов - нажатие клавиш клавиатуры или мыши (передать данные процессу), Ctrl-C/Ctrl-Break(завершить процесс), Ctrl-Alt-Del(перезагрузка ОС). Опрос множества сигналов в мультиплексном режиме (медленные устройства), в селекторном режиме (для быстродействующих УВВ). Обработка сигналов Сигналы генерируется, когда происходят определенные события, которые вызывают посылку сигнала. Сигнальные события могут быть вызвананы программными и аппаратными причинами, прерыванием от терминала, результатом измерения времени и выполнением других процессов. Если инициатор сигнала ядро, другой процесс или интерактивные действия пользователя, то сигнал считается асинхронным по отношению к процессу-приемнику сигнала. Когда сигнал инициируется выполнением инструкций процесса-приемника, сигнал считается синхронным. В обоих случаях получение сигнала вызывает прерывание работы процесса-приемника сигнала и выполнение заранее определенных действий по его обработке. Возможность принудительной посылки конкретному процессу или группе процессов определенного сигнала даже при отсутствии реальной причины его возникновения обеспечивают системный вызов и команда kill. Их аргументы задают номер сигнала и идентификатор процесса-приемника сигнала. Значение идентификатора процесса-приемника должно быть больше 0, когда сигнал посылается процессу индивидуально. При распределенной посылке сигнала аргумент идентификации приемника может быть равен 0, когда сигнал посылается группе процессов, равен (-1), когда сигнал посылается всем процессам владельца процесса-источника сигнала, либо меньше (-1), когда сигнал посылается всем процессам группы владельца процесса, инициатора сигнала. Для организации обработки сигналов используются следующие сигнальные поля структуры struct proc дескриптора процесса-приемника сигнала: p_sig, p_hold, p_cursig, p_ignore и p_catch. Поле p_sig используется для регистрации номера принятого сигнала. При этом применяется следующий способ регистрации. Если принят сигнал с номером n, то устанавливается бит (n-1) поля p_sig, который будет сброшен после обработки сигнала. Например, значения поля p_sig, равное 5, означает регистрацию сигналов SIGHUP и SIGQUIT с номерами 1 и 3, соответственно. Следует отметить, что OS UNIX не поддерживает очередь сигналов. Поэтому, если процесс принял последовательно несколько одинаковых сигналов до начала обработки 1-го из них то будет обработан только результат последнего приема. Очевидно, что разнотипные сигналы могут быть одновременно зарегистрированы в поле p_sig и обработаны в порядке их поступления. Поле p_cursig сохраняет номер текущего сигнала, который должен быть обработан. Поле p_hold фиксирует номера сигналов, обработка которых должна быть задержена на период выполнения какого-либо блока инструкций процедурного сегмента программы процесса. Блок программы процесса, когда появление определенных сигналов нежелательно должен быть ограничен системными вызовами sighold и sigrelse, которые, соответственно, задерживают и освобождают обработку указанных сигналов. Поля p_ignore и p_catch определяют форму реакции процесса на получение сигнала. После получения сигнала возможны 3 альтернативы реакции процесса - обработка по умолчанию, игнорирование и перехват. Вид реакции устанавливает системный вызов signal, которому в качестве аргументов передается номер сигнала и адрес функции обработки. Когда нужно установить стандартную обработку, принятую по умолчанию, в системный вызов signal вместо адреса функции обработки передается 0 или макрос SIG_DFL. Стандартной реакцией процесса на большинство сигналов является принудительное завершение процесса. Реакция по умолчанию на такие сигналы как SIGQUIT, SIGFPE, SIGSEGV, SIGBUS, SIGSYS приводит к принудительному завершению процесса с образованием файла дампа памяти core. Когда в процессе нужно игнорировать сигнал, в системный вызов signal вместо адреса функции обработки передается 1 или макрос SIG_IGN. Почти все сигналы могут быть принудительно игнорированы. По умолчанию игнорируются сигналы, которые не должны приводить к завершению процесса, например, SIGCLD, SIGUSR1, SIGUSR2. Под перехватом сигнала понимается передача в системный вызов signal, адреса функции, которая реализует желаемую реакцию процесса на данный сигнал. Следует отметить, что после перехвата сигнала автоматически восстанавливается стандартная реакция на него. Поэтому целесообразно переустанавливать перехват сигнала системным вызовом signal, когда нужно сохранить его нестандартную обработку. Когда необходимость в перехвате сигнала исчерпана следует восстановить обработку по умолчанию, используя системный вызов signal с аргументом 0 или SIG_DFL. Следует отметить, что сигнал безусловного завершения SIGKILL с номером 9 не может быть игнорирован или перехвачен никаким процессом. Следующий фрагмент С-кода демонстрирует перехват сигнала прерывания от терминала. #include /* Функция обработки сигнала SIGINT */ void handler() { signal(SIGINT, handler); puts("Сигнал SIGINT перехвачен"); return; } /* handler */ /* Основная функция программы процесса */ main() { /* Установка перехвата сигнала SIGINT */ signal(SIGINT, handler); /* Код программы процесса, где перехвачен сигнал SIGINT */ ......................................................... /* Восстановление стандартной обработки сигнала SIGINT */ signal(SIGINT, SIG_DFL); } /* main */ Установленная реакция процесса на сигналы отражается контекстом процесса в форме таблице сигналов u_signal. Записи этой таблицы упорядочены по номерам сигналов в виде массива адресов их функций обработки. Формально каждый элемент таблицы сигналов может принимать одно из 3-х значений в соответствии с выбранной альтернативой обработки: u_signal[i] = 0 - принята обработка по умолчанию сигнала i+1; u_signal[i] = 1 - установлено игнорирование сигнала i+1; u_signal[i] = n - адрес функции обработки в адресном пространстве процесса при перехвате сигнала i+1. Порожденный процесс наследует весь контекст обработки сигналов от процесса-предка. После смены программы выполнения процесса системным вызовом семейства exec, установленная нестандартная обработка сигналов будет игнорирована. Если сигнал был принят во время реализации системного вызова, то выполнение последнего будет прервано для обработки сигнала. После завершения обработки сигнала прерванный системный вызов перезапускается автоматически. В заключении следует отметить, что механизм обработки сигналов в OS UNIX позволяет реализовать популярную в настоящее время методологию событийного программирования. Сигналы как средства комуникации процессов В ОС UNIX каждый процесс может послать другому процессу некоторое воздействие, которое называют "сигнал"; соответственно, если процесс-отправитель имеет право передать сигнал процессу-получателю, то при выполнении передачи в последнем возникает событие, связанное с сигналом. Это событие очень похоже на прерывание, возникающее в аппаратуре вычислительной системы. В ОС имеется набор сигналов, которые могут передавать друг другу процессы; перечень сигналов описан в файле "signal.h". Отправитель может подать некоторым образом команду ОС, что он передает сигнал с заданным номером процессу-получателю, процесс-получатель может прореагировать на сигнал тремя способами: 1) прекращение выполнения, причиной которого является пришедший сигнал; 2) игнорирование сигнала (здесь следует отметить, что игнорировать можно далеко не все сигналы); 3) вызывается предопределенная процессом функция, которая может выполнить какие-то действия; возврат из этой функции осуществляется в точку прихода сигнала. Рассмотрим взаимодействие между процессами с помощью приема-передачи сигналов. Мы уже говорили о том, что в системе UNIX можно построить аналогию механизма прерываний из некоторых событий, которые могут возникать при работе процессов. Эти события так же, как и прерывания, однозначно определены для конкретной версии ОС, т.е. набор сигналов определен. Возникновение сигналов почти так же, как и возникновение прерываний, может происходить по следующим причинам: "некоторое событие внутри программы, например, деление на ноль или переполнение; "событие, связанное с приходом некоторой информации от устройства, например, событие, связанное с передачей от клавиатуры комбинации "Ctrl+C"; "событие, связанное с воздействием одного процесса на другой, например, "SIG_KILL". Система имеет фиксированный набор событий, которые могут возникать. Каждое событие имеет свое уникальное имя; эти имена обычно едины для всех версий UNIX. Такие имена называются сигналами. Есть сигналы, которые присутствуют практически во всех UNIX, но также есть сигналы, специфичные лишь для конкретной версии UNIX (FreeBSD, SCO UNIX, Linux, ...) Например, в версии BSD есть сигнал приостановки работы процесса, реакцией на который является замораживание процесса, а есть сигнал, который размораживает процесс. Это сигнал FreeBSD версии. Прототип функции обработки сигнала: void (* signal (sig, fun)) () int sig; void (* fun) (); При обращении к signal мы передаем: sig - имя сигнала; fun - указатель на функцию, которая будет обрабатывать событие, связанное с возникновением этого сигнала. Функция signal возвращает указатель на предыдущую функцию обработки данного сигнала. Мы говорили о том, что событие, связанное с возникновением сигнала может быть обработано в системе тремя способами: 1. SIG_DEF - стандартная реакция на сигнал, которая предусмотрена системой; 2. SIG_IGN - игнорирование сигнала (следует отметить, что далеко не все сигналы можно игнорировать, например, SIG_KILL); 3. Некоторая пользовательская функция обработки сигнала. Соответственно, указывая либо имена предопределенных констант, либо указатель на функцию, которую мы хотим определить как функцию-обработчик сигнала, можно предопределить реакцию на тот или иной сигнал. Установка обработки сигнала происходит одноразово, это означает то, что если мы установили некоторую обработку, то по этому правилу будет обработано только одно событие, связанное с появлением данного сигнала. И при входе в функцию- обработчика устанавливается стандартная реакция на сигнал. Возврат из функции-обработчика происходит в точку прерывания процесса. Приведем пример программы "Будильник". Средствами ОС мы будем "заводить" будильник. Функция alarm инициализирует появление сигнала SIG_ALRM. main () {char s[80]; signal(SIG_ALRM, alrm); /* установка режима связи с событием SIG_ALRM на функцию alrm */ alarm(5); /* заводим будильник */ printf("Введите имя \n"); for (;;) {printf("имя:"); if (gets(s,80) != NULL) break;}; printf("OK! \n"); } alrm () {printf("\n жду имя \n"); alarm(5); signal (SIG_ALRM,alrm);} В начале программы мы устанавливаем реакцию на сигнал SIG_ALRM - функцию alrm, далее мы заводим будильник, запрашиваем "Введите имя" и ожидаем ввода строки символов. Если ввод строки задерживается, то будет вызвана функция alrm, которая напомнит, что программа "ждет имя", опять заведет будильник и поставит себя на обработку сигнала SIG_ALRM еще раз. И так будет до тех пор, пока не будет введена строка. Здесь имеется один нюанс: если в момент выполнения системного вызова возникает событие, связанное с сигналом, то система прерывает выполнение системного вызова и возвращает код ответа, равный "-1". Это мы можем также проанализировать по функции errno. Надо отметить, что одноразово устанавливается только "свой" обработчик. Дефолтный обработчик или игнорирование устанавливается многоразово, то есть его не надо каждый раз подтверждать после обработки сигнала. Еще две функции, которые необходимы нам для организации взаимодействия между процессами:.: 1) int kill(int pid, sig) - это функция передачи сигнала процессу. Она работает следующим образом: процессу с номером pid осуществляется попытка передачи сигнала, значение которого равно sig. Соответственно, сигнал может быть передан в рамках процессов, принадлежащих к одной группе. Код ответа: -1, если сигнал передать не удалось, пояснение опять же можно найти в errno. Функция kill может использоваться для проверки существования процесса с заданным идентификатором. Если функция выполняется с sig=0, то это тестовый сигнал, который определяет: можно или нет передать процессу сигнал; если можно, то код ответа kill отличен от "-1". Если pid=0, то заданный сигнал передается всем процессам, входящим в группу. 2) int wait(int *wait_ret) - ожидание события в сыновнем процессе. Если сыновнего процесса нет, то управление возвращается сразу же с кодом ответа "-1" и расшифровкой в errno. Если в процессе-сыне возникло событие, то анализируются младшие 16 бит в значении wait_ret: а) Если сын приостановлен (трассировка или получение сигнала), тогда старшие 8 бит wait_ret - ??? ???????, ??????? ??????? ???????-???, а младшие содержат код 0177. б) Если сыновий процесс успешно завершился через обращение к функции exit. Тогда младшие 8 бит равны нулю, а старшие 8 бит равны коду, установленному функцией exit. в) Если сын завершился из-за возникновения у него необрабатываемого сигнала, то старшие 8 бит равны нулю, а младшие - номер сигнала, который завершил процесс. Функция wait возвращает идентификатор процесса в случае успешного выполнения и "-1" в противном случае. Если одно из перечисленных событий произошло до обращения к функции, то результат возвращается сразу же, то есть никакого ожидания не происходит, это говорит о том, что информация о событиях в процессе безвозвратно не теряется. рассмотрим еще один пример. Наш будильник будет уже многопроцессный. alr() {printf("\n Быстрее!!! \n"); signal (SIG_ALRM, alr);} main () {char s[80]; int pid; signal(SIG_ALRM, alr); if (pid=fork()) for (;;) { sleep(5); /*приостанавливаем процесс на 5 секунд */ kill(pid, SIG_ALRM); /*отправляем сигнал SIG_ALRM процессу-сыну */ } print("имя?"); for (;;) { printf("имя?"); if gets(s,80)!=NULL) break; } printf("OK!\n"); kill(getpid(), SIG_KILL); /* убиваем зациклившегося отца */ } Следует заметить, что в разных версиях UNIX имена сигналов могут различаться. Наша программа реализуется в двух процессах. Как и в предыдущем примере, имеется функция реакции на сигнал alr(), которая выводит на экран надпись и переустанавливает функцию реакции на сигнал опять же на себя. В основной программе мы также указываем alr() как реакцию на SIG_ALRM. После этого мы запускаем сыновний процесс, и отцовский процесс (бесконечный цикл) "засыпает" на 5 единиц времени, после чего сыновнему процессу будет отправлен сигнал SIG_ALRM. Все, что ниже цикла, будет выполняться в процессе-сыне: мы ожидаем ввода строки, если ввод осуществлен, то происходит убиение отца (SIG_KILL). Замечание: мы говорим о некотором обобщенном UNIX, реальные UNIX-ы могут иметь некоторые отличия друг от друга. На сегодняшний день имеются достаточно формализованные стандарты на интерфейсы ОС, в частности для UNIX это POSIX-standard, т.е. были проведены работы по стандартизации интерфейсов всех уровней для открытых систем. Основной задачей является унификация работы с системами, как на уровне запросов от пользователя, так и на уровне системных вызовов. В принципе, на сегодняшний день практически все разработчики ОС стараются привести свои системы к стандарту POSIX. В частности, Microsoft объявила, что системные вызовы и работа с файлами в Windows NT происходит в стандарте POSIX. Но так или иначе реальные коммерческие системы от этого стандарта отходят. Компьютер и внешние события Практически все функции современных вычислительных систем так или иначе сводятся к обработке внешних событий. Единственная категория приложений, для которых внешние события совершенно неактуальны - это так называемые пакетные приложения, чаще всего - вычислительные задачи. Доля таких задач в общем объеме компьютерных приложений в наше время невелика и постоянно падает. В остальных же случаях, даже если не вспоминать о специализированных управляющих компьютерах, серверы обрабатывают внешние по отношению к ним запросы клиентов, а персональный компьютер - реагирует на действия пользователя. Различие между управляющими системами (приложениями реального времени) и системами общего назначения (термин - система разделенного времени вышел из употребления и не всегда точно отражает суть дела) состоит лишь в том, что первые должны обеспечивать гарантированное время реакции на событие, в то время как вторые "всего лишь" - предоставить хорошее среднее время такой реакции и/или обработку большого количества событий в секунду. Время обработки одного события и количество событий, обрабатываемых в единицу времени, далеко не всегда являются жестко взаимосвязанными - ведь при многопоточной обработке система может обрабатывать несколько событий параллельно. Единственный способ, которым фон-неймановский компьютер может отреагировать на что бы то ни было - это исполнить программу, последовательность команд. В случае внешнего события, естественным решением кажется предоставить команду условного перехода, условием которого является признак события. В системах команд микроконтроллеров частореализуют именно такие переходы (см. например табл. 2.2). В качестве признака события в этом случае используется значение одного из битов специального регистра, биты которого соответствуют входам микросхемы контроллера. Бит равен единице, если на соответствующий вход подано высокое напряжение, и нулю - если низкое. Наличие таких команд полезно, но решает проблему не полностью: да, сели событие произошло, мы моыжем вызвать программу и осуществить реакцию но что мы будем делать, если его еще не происходило? Опрос Наивное решение состоит в том, что нам следует циклически опрашивать признак события (пример 6.1). Это решение хорошо не только концептуальной простотой, но и тем, что если цикл опроса короток, время реакции будет очень маленьким. Поэтому такой метод нередко используют для обработки последовательностей событий, следующих друг за другом с небольшим интервалом. Однако это решение, называемое опросом (polling), имеет и большой недостаток: загрузив процессор опросом, мы не можем занять его чем бы то ни было другим. Пример 6.1. Пример использования режима опроса ; Приведенный фрагмент кода использует опрос таймера TMRO, ; работающего от "часового" кварцевого генератора с частотой 32768Гц. ; Цикл опроса в чистом виде TMRO - регистр таймера, ; Timervalue - просто переменная, ; регистр 0 - аккумулятор, обозначаемый также как W. ; Такой цикл ожидает одного отсчета таймера. MovF TMRO, 0 MovWF TimerValue G5H_Continuel MovF TimerValue, 0 SubWF TMRO, 0 BNZ G5H_Continuel ; Код содержит два цикла опроса: первый цикл генерирует ; сигнал высокого напряжения, второй - низкого. ; В результате получается периодический сигнал, называемый меандром. ; Фрагмент определителя номера на основе микроконтроллера PIC ; (с) 1996, Дмитрий Иртегов. ; Запрос к АТС ка выдачу номера. ; Генератор меандра с частотой 501.5 гц. Выдает 50 периодов (100 мс). ; генерирует 2 периода по 16 тиков и один - по 17. ; Получается очень похоже. 6enerate500Hz MovLW 50 MovWF AONByteCounter MovLW 3 MovWF Tmpl MovF TMRO, 0 MovWF TimerValue G5H_NextPeriod MovLW 8 AddWF TimerValue, 1 BSF LINE_CTL_PORT, LINE_ANSWER G5H_Continuel MovF TimerValue, 0 SubWF TMRO, 0 BNZ G5H_Continuel MovLW 8 AddWF TimerValue, 1 BCF LINE_CTL_PORT, LINE_ANSWER DecFSZ Tmpl, 1 GoTo G5H_ContinueO MovLW 3 MovWF Tmpl IncF TimerValue, 1 G5H_ContinueO MovF TimerValue, 0 SubWF TMRO, 0 BNZ GSH_ContinueO DecFSZ AONByteCounter, 1 GoTo G5H_NextPeriod Return Этот недостаток можно переформулировать и иначе: если процессор занят Чем-то другим, он может узнать о событии, только завершив текущую Деятельность. Если событие действительно важное, впрочем, мы можем расставить команды проверки его признака по всему коду программы, но для сложных программ, обрабатывающих много различных событий, это решение вряд ли можно считать практичным. С точки зрения встраиваемых приложений, режим опроса имеет еще один существенный недостаток: опрашивающий процессор нельзя выключить В то же время, выключенный процессор потребляет гораздо меньше энергии и не создает электромагнитных помех, поэтому при разработке программ для таких приложений считается хорошим тоном выключать (переводить в режим ожидания) процессор всегда, когда это возможно. В этом случае конечно, необходимо предусмотреть какие-либо средства для вывода процессора из этого состояния при возникновении интересующего нас события. Канальные процессоры и прямой доступ к памяти Одно из решений состоит в том, чтобы завести отдельный процессор и поручить ему всю работу по опросу. Процессор, занимающийся только организацией ввода-вывода, называют периферийным или канальным (channel). Понятно, впрочем, что это повышает стоимость системы и не решает проблемы радикально - теперь вместо флагов, непосредственно сигнализирующих о внешних событиях, центральный процессор вынужден опрашивать флаги, выставляемые канальным процессором. В зависимости от характера событий и требуемой обработки это решение может оказаться и совсем неприемлемым, например, если на каждое событие требуется немедленная реакция именно центрального процессора. В противном случае, если немедленно после события требуется лишь простая обработка, а сложные вычисления можно отложить на потом, канальный процессор можно упростить и сделать существенно дешевле центрального. Так, при работе с контроллерами дисков, лент и других устройств массовой памяти возникает задача копирования отдельных байтов (или, в зависимости от разрядности шины контроллера, полуслов или слов) из контроллера в память и обратно. Передача одного блока (512 байт у большинства современных контроллеров) состоит из 128 операций передачи слова, идущих друг за другом с небольшими интервалами. Темп передачи данных определяется скоростью вращения диска или движения ленты. Этот темп обычно ниже скорости системной шины, поэтому передача данных должна включать в себя опрос признака готовности контроллера принять или предоставить следующее слово. Интервал между словами обычно измеряется несколькими циклами шины. Нередко бывает и так, что частоты шины и контроллера не кратны, поэтому последовательные слова надо передавать через различное число циклов. Дополнительная сложность состоит в том, что, не предоставив вовремя следующее слово для записи, мы испортим весь процесс - эта проблема особенно серьезна на устройствах однократной записи, например прожигателях компакт-дисков. Аналогично, не успев прочитать очередное слово, мы потеряем его и вынуждены будем отматывать ленту назад пли ждать следующего оборота диска. Видно, что это именно та ситуация, которую мы ранее описывали как показание к использованию режима опроса: поток следующих друг за другом с небольшим интервалом событий, каждое из которых нельзя потерять, а нужно обязательно обработать. Обработка события, которая нужна, чтобы избежать такой неприятности, крайне проста, так что устройство, способное с ней справиться, не обязано даже быть полностью программируемым процессором. При передаче надо всего лишь убедиться, что блок данных не кончился, взять следующее слово из памяти, дождаться готовности устройства, скопировать слово и вернуться к началу алгоритма. Если блок данных кончился или контроллер выдал ошибку, необходимо сообщить об этом центральному процессору. Для реализации этого алгоритма достаточно трех регистров (указателя в памяти, значения текущего слова и счетчика переданных слов). Реализующее этот алгоритм устройство называют контроллером прямого доступа к памяти (Direct Memory Access controller, DMA controller) (рис. 6.1). Такие контроллеры часто рассчитаны на одновременную работу с несколькими устройствами - имеют несколько каналов - и, соответственно, больше регистров. Описание реальной микросхемы контроллера ПДП можно найтив [Паппас/Марри 1993]. Обычно контроллеры ПДП не считают процессорами, однако без большой натяжки можно сказать, что это все-таки канальный процессор, хотя и очень примитивный. Контроллеры ПДП, рассчитанные на совместную работу с процессором, обладающим виртуальной памятью, часто имеют некий аналог диспетчера памяти ЦП, для того, чтобы позволить операционной системе предоставлять указатель для ПДП в виртуальном адресном пространстве, или, во всяком случае, упростить работу по преобразованию виртуального адреса в физический. Различают два типа реализаций ПДП: - мастер шины (bus master) (в старой русскоязычной литературе встречалось также словосочетание задатчик шины), когда устройство имеет свой собственный контроллер ПДП, - централизованный контроллер, устанавливаемый на системной плате и способный работать с несколькими различными устройствами. В качестве альтернативы ПДП можно предложить снабжение устройства буфером, который работает с частотой системной шины. Центральный процессор передает данные в буфер, и лишь когда заканчивает передачу, инициирует операцию устройства. Логика работы самого устройства с этим буфером, впрочем, ничем не отличается от ПДП, с той лишь разницей, что используется не общесистемная, а встроенная память. На практике, оба подхода часто используются совместно: ПДП позволяет минимизировать загрузку центрального процессора, а буфер - избежать потери данных, если системная шина занята другим устройством. Типичный современный дисковый контроллер имеет и средства ПДП, и внутренний буфер. У кэширующих (имеющих кэш-память) и RAID-контроллеров объем буфера может измеряться многими мегабайтами. Кроме того, современные жесткие диски также имеют собственные буферы. Периферийные процессоры находят широкое применение в современных вычислительных системах. Так, типичный современный персональный компьютер, кроме центрального процессора, обычно имеет и специализированный видеопроцессор, так называемый графический ускоритель. У кэширующих дисковых контроллеров и аппаратных реализаций RAID (см. разд. Дисковые массивы) обычно также есть собственный процессор, в данном случае, как правило, используются полностью программируемые процессоры. Лазерные и струйные печатающие устройства имеют процессор, который интерпретирует команды языка управления принтером (PCL или Postscript), есть процессоры модемах и во многих других периферийных устройствах. Впрочем, нередко встречаются и попытки обратить процесс децентрализации вычислений -так называемые "софтовые" или Win-модемы (называемые так потому, что программное обеспечение, способное работать с таким модемом, часто поставляется только под Windows), многие бытовые принтеры и т. д. В отличие от перечисленных устройств, классический полностью программируемый канальный процессор подключен непосредственно к системной шине и может оперировать несколькими устройствами, в зависимости от загруженной в него канальной программы. Канатьные процессоры долгое время считались отличительной особенностью больших ЭВМ. В мини-и микрокомпьютерах использование специализированных канальных процессоров, более сложных, чем контроллер ПДП, считалось неприемлемым по стоимостным показателям. Удивительно, что даже современное радикальное удешевление оборудования не изменило положения: предложение консорциума I2O (Intelligent Input/Output) снабжать компьютеры на основе процессоров х86 канальным процессором Intel 960, с энтузиазмом поддержанное практически всеми поставщиками операционных систем, почему-то не было столь же горячо поддержано потребителями. Потребители мини- и микросистем, нуждающиеся в высокой производительности, предпочитают использовать в качестве дополнительных процессоров устройства с той же архитектурой, что и центральный процессор. Это называется симметричной многопроцессорностью (SMP), и позволяет перераспределять между процессорами не только обработку событий, но и собственно вычислительную деятельность. Понятно, что обрабатывать все события по принципу опроса в такой архитектуре - бессмысленная, а зачастую и нетерпимая расточительность. К счастью, еще с 60-х годов, практически все процессоры как центральные, так и канальные, используют стратегию работы с событиями, во многих отношениях гораздо более совершенную, чем опрос. signal - список всех возможных системных сигналов. SIGNAL(7) Linux Programmer's Manual SIGNAL(7) НАЗВАНИЕ signal - список всех возможных системных сигналов ОПИСАНИЕ Linux поддерживает все сигналы, описанные ниже. Некоторые номера сигналов зависят от реализации. Сначала приведем информацию о сигналах, описанных в POSIX.1. Сигнал Номер Действие Комментарии -------------------------------------------------------------------------------------------------------------------- SIGHUP 1 A Управляющим терминалом зафиксировано прерывание связи, либо завершился управляющий процесс SIGINT 2 A Прерывание с клавиатуры SIGQUIT 3 C Прекратить работу с клавиатурой SIGILL 4 C Некорректная инструкция от процессора SIGABRT 6 C Сигнал о прекращении, посланный abort(3) SIGFPE 8 C Неправильная операция с "плавающей" запятой SIGKILL 9 AEF Сигнал Kill SIGSEGV 11 C Некорректное обращение к памяти SIGPIPE 13 A Запись в канале, не имеющем считывающих процессов SIGALRM 14 A Сигнал таймера от alarm(2) SIGTERM 15 A Сигнал снятия SIGUSR1 30,10,16 A Определяемый пользователем сигнал #1 SIGUSR2 31,12,17 A Определяемый пользователем сигнал #2 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. Сигнал Номер Действие Комментарии ----------------------------------------------------------------------------------------- SIGBUS 10,7,10 C Ошибка шины (затруднен доступ к памяти) SIGPOLL A Событие, которое можно отложить (Sys V). Синоним SIGIO SIGPROF 27,27,29 A Закончилось время профилирующего таймера SIGSYS 12,-,12 C Не допустимый для процедуры аргумент (SVID) SIGTRAP 5 C Ловушка отладки SIGURG 16,23,21 B Приоритетные данные в сокете (4.2 BSD) SIGVTALRM 26,26,28 A Виртуальный таймер (4.2 BSD) SIGXCPU 24,24,30 C превышено время работы процессора (4.2 BSD) SIGXFSZ 25,25,31 C превышен размер файла (4.2 BSD) В случае появления SIGSYS, SIGXCPU, SIGXFSZ (а для некоторых реализаций и SIGBUS) Linux по умолчанию (до нынешней версии 2.3.27) выполняет действие A (снятие), тогда как SUSv2 выполняет действие C (снятие процесса и запись информации в файл core). Некоторые другие сигналы. Сигнал Номер Действие Комментарии ------------------------------------------------------------------------------- SIGIOT 6 C IOT trap. Синоним SIGABRT SIGEMT 7,-,7 SIGSTKFLT -,16,- A Ошибка в стеке сопроцессора SIGIO 23,29,22 A Теперь возможен ввод/вывод (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 Снятие процесса и запись информации в файл core (определено по умолчанию). D Останов процесса . E Сигнал не может быть "перехвачен". F Сигнал не может быть игнорирован. СООТВЕТСТВИЕ СТАНДАРТАМ POSIX.1 НАЙДЕННЫЕ ОШИБКИ SIGIO и SIGLOST равнозначны. Последнее описано в ядре, но некоторые программы в процессе сборки определяют сигнал под номером 29 как SIGLOST. Следующий список сигналов поддерживается функциями управления процессом. См. man-страницу signal(7) вашей системной документации о поведении по умолчанию этих сигналов. Таблица 1. Поддерживаемые сигналы SIGFPE SIGCONT SIGKILL SIGSTOP SIGUSR1 SIGTSTP SIGHUP SIGUSR2 SIGTTIN SIGINT SIGSEGV SIGTTOU SIGQUIT SIGPIPE SIGURG SIGILL SIGALRM SIGXCPU SIGTRAP SIGTERM SIGXFSZ SIGABRT SIGSTKFLT SIGVTALRM SIGIOT SIGCHLD SIGPROF SIGBUS SIGCLD SIGWINCH SIGPOLL SIGIO SIGPWR SIGSYS Мультиплексор Multiplexer (MUX) - 1. аппаратно реализованная схема для выбора одного выходного сигнала из нескольких входных. 2. устройство уплотнения, позволяющее передавать по одной линии несколько сигналов (потоков данных) одновременно. Мультиплексирование (уплотнение) Multiplexing - Передача нескольких сигналов по одной коммуникационной линии. Двумя наиболее широко используемыми технологиями мультиплексирования являются частотное разделение каналов, когда для передачи различных сигналов используются несущие с различной частотой, и временное разделение каналов, когда отдельные сигналы передаются в свои временные промежутки.