D3 Reference Manual

Index | Help

Поиск по страницам

Разделы / Unix / Использование сигналов Unix

Использование сигналов Unix

Общий обзор сигналов ОС Unix с точки зрения виртуальной машины D3.

Эта информация предназначена для опытных программистов, которые создают интерфейсы между приложениями Unix и системой D3 или используют синхронизацию процессов Unix в истинной среде D3.

В обычной работе D3 сигналы ОС Unix не используются по соображениям производительности. Они используются только для обработки исключительных ситуаций. Отказ от использования сигналов в приложении лишает лишь несущественных возможностей - например, возможности послать сообщение или отключить процесс. Это, однако, не означает, что сигналы могут использоваться свободно. Монитор D3 содержит системный вызов для обработки обычных сигналов D3 пользовательским обработчиком сигналов, что позволяет сочетать системную обработку сигналов и обработку сигналов приложениями.

Ниже следует таблица сигналов, используемых в настоящее время системой D3. Будущие разработки могут потребовать использования дополнительных сигналов.

D3 использует только сигналы Системы V.3, распространенные среди самых недавних версий ОС Unix. Обработка прочих сигналов (не являющихся сигналами Системы V.3) обычно оставлена обработчику сигналов, используемому в системе по умолчанию (как правило, SIG_IGN).

Числовые значения могут варьироваться между реализациями Unix. При написании программ, использующих сигналы, на языке C очень важно использовать правильный "include", а при написании таких программ на FlashBASIC - "include dm,bp,unix.h, signal.h".

Сигнал Описание
SIGHUP Останов. Когда поступает этот сигнал, процесс отключается от системы D3, но остается подключенным к виртуальной машине. Этот сигнал контролируется командой "hupcl" в файле конфигурации D3. Как правило, он генерируется драйвером устройства TTY, когда теряется несущая данных (сигнал 'dcd') . Этот сигнал может быть изменен командой "trap dcd" TCL.
SIGINT Прерывание. Генерируется клавишей BREAK и альтернативной виртуальной машиной. Знак прерывания задается посредством "brkchr" в файле конфигурации или команды "set-break" TCL. Процесс посылается в отладчик FlashBASIC или системный отладчик, если была дана команда "brk-debug", или повышается уровень, если была дана команда "brk-level".
SIGQUIT Прекращение. Генерируется клавишей ESC или альтернативным знаком "escape", заданным с помощью "escchr" в файле конфигурации или команды "set-esc" TCL. Если быа дана команда "esc-level", повышается уровень. Если была дана команда "esc-data", сигнал генерируется, но, в конце концов, игнорируется виртуальным отладчиком. Знак, заданный посредством команды "set-esc", помещается впоследствии во входной буфер процесса, но необходимо подчеркнуть, что при вводе этого знака может иметь место значительная задержка.
SIGILL Недействительная инструкция. Посылает процесс в системный отладчик с кодом ошибки 'illegal opcode' ("недействительный код операции"). Используется в некоторых реализациях ОС Unix для шагового выполнения ассемблерного кода.
SIGTRAP Трассировочное прерывание. Посылает процесс в системный отладчик с кодом ошибки 'illegal opcode'. Используется в некоторых реализациях ОС Unix для шагового выполнения ассемблерного кода.
SIGIOT Инструкция прерывания ввода/вывода. Посылает процесс в системный отладчик с кодом ошибки 'illegal opcode'.
SIGEMT Прерывание эмулятора. Посылает процесс в системный отладчик с кодом ошибки 'illegal opcode'. Используется в некоторых реализациях Unix для шагового выполнения ассемблерного кода.
SIGFPE Ошибка плавающей запятой. Посылает процесс в системный отладчик с кодом ошибки 'illegal opcode'.
SIGKILL Уничтожение. Этот сигнал не может быть захвачен и прекращает процесс немедленно после выполнения процедуры отключения (logoff). Уничтожение процесса сброса на диск (flusher) на виртуальной машине отключает от нее все процессы и выключает саму виртуальную машину.
SIGBUS Ошибка шины. Посылает процесс в системный отладчик с кодом ошибки 'illegal opcode'. В некоторых реализациях ОС Unix используется для шагового выполнения ассемблерного кода.
SIGSEGV Нарушение сегментации. Посылает процесс в системный отладчик с кодом ошибки 'illegal opcode'. Используется в неоторых реализациях ОС Unix для шагового выполнения ассемблерного кода.
SIGSYS Неправильный аргумент для системного вызова. Посылает процесс в системный отладчик с кодом ошибки 'illegal opcode'.
SIGPIPE Запись в программный канал, когда читать некому. Процесс посылается в виртуальный отладчик. Программные каналы иногда используются в качестве "лент" для сохранения файлов. Другой конец канала может быть подсоединен к последовательному устройству, процессу сервера связи ... Поэтому, когда появляется этот сигнал, системный вызов записи (write system call) прерывается. В прикладной среде этот сигнал должен быть отловлен, и пользователю должно быть послано сообщение типа "Server not Ready" ("Сервер не готов"). В этом случае системный вызов записи будет прерван, информация об этом будет передана в процесс сохранения (save) в виде сообщения об ошибке четности, и попытки записи будут производиться до тех пор, пока, наконец, одна из них не окажется успешной.
SIGALRM Таймер блокировки. Обработчик сигналов, используемый по умолчанию, - это просто возврат в прерванную программу. Это означает, что сигнал активен, но ничего не делает. Во FlashBASIC этот сигнал может использоваться с функцией "%alarm()" для установки блокировок по времени в критических местах. Этот сигнал может быть изменен командой "trap alrm" TCL.
SIGTERM Сигнал программного прерывания. Прекращает процесс D3, оставляя его в системе (logged on), но отключенным. Этот сигнал "замораживает" среду D3. В случае перезапуска процесса его выполнение возобновляется с того места, где он был остановлен. Как правило, этому сигналу должен предшетвовать сигнал SIGHUP для отключения процесса.
SIGUSR1 Пользовательский сигнал 1. Не используется. Может свободно использоваться приложениями.
SIGUSR2 Пользовательский сигнал 2. Заставляет процесс проверить текущее состояние и, возможно, изменить его. Этот сигнал имеется для сообщения D3 командами "logoff", "TCL" и "END" TCL. Посылка этого сигнала вне контекста вреда не причиняет. Например, именно так следует возобновлять процесс, ожидающий в системном вызове "%pause()". Этот сигнал может использоваться приложениями при условии, если они вызывают обработчик сигналов системы D3 внутри собственного обработчика сигналов, созданного для удовлетворения их требований. См. примеры.
SIGCLD Смерть дочернего процесса. Не используется.
SIGPWR Отказ питания. Этот сигнал отключает процесс от системы D3, но оставляет его подключенным к виртуальной машине. Поведение системы зависит от того, как аппаратура генерирует этот сигнал. Если нет аварийного питания от батарей, маловероятно, что виртуальную машину можно будет выключить "мягко". Система обычно изменяет свое рабочее состояние, чтобы установить контроль над происходящим. В соответствующий сценарий интерпретатора команд Unix, связанный с новым уровнем работы системы, должна быть вставлена команда "ap - k". Этот сигнал может быть изменен командой "trap pwr" TCL.
Системный вызов для информирования о входящем сигнале

Монитор D3 предоставляет системный вызов для информирования виртуальной среды D3 о поступающем сигнале. Как правило, эта C-функция вызывается системными обработчиками сигналов, используемыми по умолчанию, но может быть вызвана и из обрабтчика сигналов в приложении.

Эта функция определяется следующим образом:

#include "/usr/lib/D3/include/sigmon.h"
void sigmon( code )
int code;

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

Код Значение/описание
db_slcout 0/Отсутствие операции. Просто заставляет процесс проверить свое текущее состояние. Эта функция должна быть вызвана обработчиком сигнала SIGUSR2.
db_privop 7/Привилегированный код операции. Посылает вызывающую программу в системный отладчик. При этом вызове управление вызывающей программе не возвращается. Этот вызов может использоваться для прерывания программы на языке C даже в случае, если не вызывается из обработчика сигналов.
db_break 10/Прерывание. Посылает проуесс в отладчик FlashBASIC или системный отладчик. Эта функция должна вызываться обработчиком сигнала SIGINT. Выход в отладчик происходит не сразу. Как правило, процесс выходит в отладчик (или повышает уровень), когда обработчик сигнала заканчивает работу.
db_esc 12/Прекращение. Повышает уровень. Эта функция должна быть вызвана обработчиком сигнала SIGQUIT. Как правило, процесс повышает уровень, когда обработчик сигнала заканчивает работу.
db_logoff 14/Отключение. Эта функция обычно вызывается обработчиком сигнала SIGHUP. Она может быть вызвана обработчиками других сишналов как часть приложения (например, SIGALRM для отключения процесса по истечении заданного времени).
db_pwrdown -1/Отключение. Процесс отключается от виртуальной машины, но остается зарегистрированным в системе (logged on).

Примеры:

Этот раздел содержит некоторые простые примеры использования сигналов в приложении D3 или при взаимодействии с приложениями Unix. Все эти примеры требуют написания обработчика сигналов на языке C, который может быть связан с монитором D3, как описано в этом документе.

Блокировка по времени

Цель заключается в описании обработчика сигнала, по которому пользовательский процесс должен отключаться, если в течение пределенного времени не было никакой активности. Предполагается, что пользователь находится в приложении FlashBASIC и ожидает ввода.

Напишем следующий обработчик сигнала и функцию:

setalrm.c:
#include <signal.h>
#include "/usr/lib/D3/include/sigmon.h"
myalrm(){
    /* Обработчик сигнала. Если приводится
       в действие, отключает процесс  */
       sigmon( db_logoff );
}
/*  Указание нового обработчика сигналов.
    В качестве обработчика сигналов будет  
    использоваться  обработчик сигналов
    приложения.
    Возврат адреса предыдущего обработчика
    сигналов, чтобы приложение на языке BASIC
    могло удалить блокировку по времени. 
*/
void (*setalrm())(){
    return signal(SIGALRM, myalrm );
}

Откомпилируем вышеуказанную программу и включим ее в монитор D3 путем ввода следующих команд в счете "dm":

addbi  setalrm  signal 
ar  vru  libgm.a  setalrm.o 
make  -f  /usr/lib/D3/Makefile 

Следует отметить, что это включает пользовательскую функцию "setalrm()", а также системный вызов "signal()", обычно не поставляемый с монитором. Последний будет получен из стандартной библиотеки C. После этого функция "%setalrm" может быть вызвана из FlashBASIC.

Введем следующую "прикладную" программу:

 *
 * Перепрограммирование обработчика
 * сигнала "alarm" с сохранением
 * предыдущего системного обработчика.
 * Следует отметить, что указатель функции, 
 * возвращаемый функцией, рассматривается
 * как указатель на символ.
 include dm,bp,unix.h signal.h
 old$alarm = (char*)%setalrm()
 *
 loop
    * Установка времени блокировки на 10 минут.
    * С этого момента, если пользователь
    * не вводит команду в течение 10 мин.,
    * процесс отключается. 
    * 
    %alarm(600)
    * .. Здесь выдается экран приложения для ввода данных
    crt 'Command : ':
    input command
 while 1 do
    begin case 
         case command = 'a'
              * ... Вставить программу для команды 'a'
         case command = 'end'
              print 'end of the application'
              * Отключение блокировки и возврат к
              * обработке сигнала "alarm" стандартным 
              * обработчиком по умолчанию. 
              * 
              %alarm( 0 )
              %signal( SIGALRM, (char*)old$alarm )
              chain 'off'
    end case
 repeat

Следует отметить, что если бы в вышеуказанном примере таймер блокировки не был отключен и если бы не произошел возврат к обработке сигнала по умолчанию, таймер продолжал бы работать даже в случае, если бы процесс вернулся на уровень TCL, т.е. отключение процесса (logging off) произошло бы позже. Можно написать небольшую программу на языке FlashBASIC, которая только устанавливает таймер, и включать ее в процедуры (PROCs) или меню для достижения такой же возможности блокировки по времени в TCL.

Синхронизация двух процессов D3

В традиционных реализациях D3 синхронизаций двух процессов D3 либо путем опроса с целью выявления наличия файла или, что немного лучше, с помощью инструкции LOCK языка FlashBASIC. Но в некоторых реализациях сама инструкция LOCK языка FlashBASIC выполняет внутренний опрос, в результате чего процесс ожидания поглощает время процессора (cpu). С помощью сигналов можно заставить процесс ждать поступления сигнала (BREAK, "logoff" или сигнала, сгенерированного другим процессом, - возможно, процессом Unix или процессом D3 с другой виртуальной машины D3) без потребления ресурсов "cpu".

В следующем примере одно фантомное задание ожидает сигнала, посланного "кем-то", чтобы прочитать в программном канале сообщение, помещенное в канал этим "кем-то". Такую связь с передачей сообщения, может быть, было бы лучше организовать через сообщения Unix, но это просто пример. Кроме того, это прямая связь между двумя конкретными точками. Если бы в этой схеме было несколько партнеров, это потребовало бы семафоров. Серверный процесс делает себя видимым для всей системы (D3 и Unix) путем создания временного файла, который содержит идентификатор (PID); другие процессы используют этот идентификатор для посылки сигнала серверному процессу.

(Простой) обработчик сигналов использует SIGUSR1 . "Сообщениe" содержит заголовок и некоторые данные в коде ASCII. Оно имеет следующий формат:

+---+---+--------//-----------+
|   |NL |                     +
+---+---+--------//-----------+
  |   |          |
  |   |          +- Данные (зависимые от кода)
  |   +------------ Новая строка (не используется)
  +---------------- Код команды
                       T : Завершение
                       M : Сообщение
                       R : Прием файла

Введем следующий обработчик сигналов на языке C:

setusr1.c:
#include <signal.h>
#include "/usr/lib/D3/include/sigmon.h"
myusr1(){
    /* Просто сброс сигнала для следующего */
    сигнала ( SIGUSR1, myusr1 );
}
/* Установка нового обработчика сигналов.
   В качестве обработчика сигналов будет
   использоваться обработчик сигналов 
   приложения. 
   Возврат адреса предыдущего обработчика
   сигналов, чтобы приложение на языке 
   BASIC могло удалить блокировку.
*/
void (*setusr1())(){
    return signal(SIGUSR1, myusr1 );
}

Введем следующее фантомное задание в запись "dm,bp, pserver":

pserver
001 !
002 * Пример сервера файлов, работающего 
003 * как фантом.
004 * Команды поступают в указанный 
    * программный канал.
005 *
006 include dm,bp,unix.h signal.h
007 include dm,bp,unix.h mode.h
008 include dm,bp,unix.h fcntl.h
009 *
010 * Определения:
011 equ NL to char(10)   ;* Разделитель строк в Unix
012 equ AM to char(254)
013 equ PIPE to "/dev/mypipe"
014 equ BUFSIZE to 10000 ;* Максимальный размер записи
015 *
016 char buffer[BUFSIZE]
107 *
018 * Удаление временного файла
019 execute '!rm -f /tmp/pserverid'
020 *
021 restart:*
022 *
023 * Открытие программного канала только 
024 * для чтения, отсутствие ожидания при чтении.
025 * В случае ошибки при открытии канала - останов и
026 * посылка в сообщения в исходный процесс.
027 * 
028 fd=%open(PIPE, O$RDONLY+O$NDELAY)
029 if fd<0 then
030     stop 1,"Открытие невозможно '":PIPE:"'. Ошибка ":system(0)
031 end
032 *
033 * С этого момента BREAK не разрешается.
034 break off
035 *
036 * Создание временного файла, который
037 * содержит наш "PID", чтобы каждый в
038 * системе знал идентификатор сервера.
039 * 
040 mypid=%pgetpid(-1)
041 execute "!echo ":mypid:" > /tmp/pserverid"
042 *
043 * Перепрограммирование обработчика сигнала
044 * "alarm" с сохранением предыдущего обработчика.
045 * Следует отметить, что указатель функции, 
046 * возвращаемый функцией, рассматривается как
047 * указатель на символ.
048 old$usr1 = (char*)%setusr1()
049 *
050 * Попытка чтения из программного канала. Если
051 * сообщения нет, принятый нами сигнал не был 
052 * сигналом приложения. Возможно, кто-то
053 * пытался нас отключить. Поэтому следует
054 * закрыть канал, восстановить сигнал и немного
055 * подождать. Если мы возвращаемся, сигнал
056 * "alarm" был ложным. Перезапуск.
057 loop while 1 do
058     * Ожидание сигнала
059     %pause()
060     *
061     * Чтение из канала
062     n=%read(fd, buffer, BUFSIZE )
063     *
064     begin case
065         case n=-1
066              * IO error
067              gosub terminate
068              stop 1,"PSERVER: IO error ":system(0)
069         case n=0
070              * Канал пуст ...
071              * Полученные сигналы - это не тот
072              * сигнал, которого мы ожидали. Разрешается
073              * BREAK для обеспечения возможности 
074              * отключения (logoff), и производится
075              * перезапуск.
076              gosub terminate
077              sleep 1
078              goto restart
079         case 1
080              * Что-то есть ("n" - количество
081              * байтов)
082              * 1-й байт - это код, 
083              * 2-1 байт - новая строка
084              * (не используется)
085              code=buffer[1,1]
086              message=buffer[3,n-2]
087              * Обработка сообщения
088              gosub do$it
089     end case
090 repeat
091 *
092 do$it:*
093 begin case
094     case code='T'
095          * Завершение
096          gosub terminate
097          stop 1,"PSERVER: terminated"
098     case code='M'
099          * Сообщение.
100          execute "msg * PSERVER: ":message
101     case code='R'
102          * Прием файла. Сообщение: 
103          * полное имя файла (NL) запись (NL) 
104          * текст
105          convert NL to AM in message
106          filename=message<1>
107          item=message<2>
108          message=delete(message,1)
109          message=delete(message,1)
110          open filename then
111              write message on item
112              close
113          end else
114              execute "msg !0 PSERVER: '":filename:"' is not filename"
115          end
116 end case
117 *
118 return
119 *
120 * Общая очистка
121 terminate:*
122    %close(fd)
123    %signal( SIGUSR1, (char*)old$usr1 )
124    execute '!rm -f /tmp/pserverid'
125    break on
126 return
127 *
128  end

Создание канала связи путем выполнения следующих команд из TCL:

su   (Следующие операции может выполнять только "superuser") 
mknod  /dev/mypipe  p  
chmod  0666  /dev/mypipe 
exit  (Возвращение в TCL)

Компиляция и каталогизация программы FlashBASIC. Компиляция программы на языке C и добавление ее в список встроенных функций следующим образом:

addbi  setusr1 
cc  -c  setusr1.c 
ar  vru  libgmu.a  setusr1.o 

Чтобы использовать монитор, приспособленный к требованиям пользователя, с фантомным процессом, необходимо закрыть систему и переместить этот монитор в общий справочник /usr/bin, находясь в монопользовательском режиме.

shutdown 
make  -f  /usr/lib/pick/Makefile 
init  s 
(ожидание выполнения перехода системы в однопользовательский режим)
mv  /usr/bin/ap  /usr/bin/ap.save 
mv  ./ap  /usr/bin 
init  2  
(ожидание возращение в многопользовательский режим) 

Перезапуск виртуальной машины D3.

Создание фантомного сервера заданий:

z  pserver 

Теперь сервер ожидает сигнала для чтения сообщения. Как правило, это делается другим процессом (D3 или Unix), но в целях отладки можно использовать следующий сценарий, который позволяет посылать сообщения, небольшие файлы Unix или прекращать работу сервера:

snd:
# Посылка команд серверу (pserver) через программный канал.
PIPE=/dev/mypipe             # Программный канал связи,
SERVERID=/tmp/pserverid      # куда сервер помещает свой PID
COMMAND=$1
# Проверка активности сервера D3.
if [ ! -r $SERVERID ]
then
    echo D3 server is not active
    exit 1
fi
# Получение PID сервера.
PSERVERID=`cat $SERVERID`
# Посылка ему сигнала 0 для проверки его существования.
kill -0 $PSERVERID
if [  $? != 0 ]
then
    echo D3 server is not active. PID is not valid.
    exit 1
fi
# Синтаксический анализ команды.
case $COMMAND in
    "r"|"R")
        # Прием файла : snd r unix.file 
        # D3.file item
        echo "R\n$3\n$4\n~`cat $2~`" > $PIPE
        ;;
    "m"|"M")
        # Посылка сообщения: snd m <text>
        shift
        echo "M\n$*" > $PIPE
        ;;
    "t"|"T")
        # Завершение.
        echo "T\n" > $PIPE
        ;;
    *)
        echo "usage: snd r unix.file D3.file item"
        echo "       snd m message"
        echo "       snd t"
        ;;
esac
# Приведение сервера в действие путем посылки SIGUSR1 
# -30 for AIX; -16 for SCO, DG
kill -30 $PSERVERID

Проверка сервера с помощью следующих команд оболочки Unix:

snd  m  Hi there  

Эта команда должна послать сообщение всем пользователям D3.

snd  r  /etc/inittab  dm,pointer-file,  inittab  

Эта команда должна послать файл Unix /etc/inittab в запись D3 dm,pointer-file, inittab .

snd  t 

Эта команда завершает процесс сервера.