Главная · Поиск книг · Поступления книг · Top 40 · Форумы · Ссылки · Читатели

Настройка текста
Перенос строк


    Прохождения игр    
Demon's Souls |#13| Storm King
Demon's Souls |#11| Мaneater part 2
Demon's Souls |#10| Мaneater (part 1)
Demon's Souls |#9| Heart of surprises

Другие игры...


liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня
Rambler's Top100
Образование - Богатырев А. Весь текст 1009.15 Kb

Хрестоматия по программированию на Си в Unix

Предыдущая страница Следующая страница
1 ... 34 35 36 37 38 39 40  41 42 43 44 45 46 47 ... 87

          ... 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.  По сигналу вызывается функция-эмулятор плаваю-
     щей арифметики (подключаемая к  выполняемому  файлу  автоматически),  которая  и
     обрабатывает  требуемую команду. Это может происходить много раз, именно поэтому

А. Богатырев, 1992-95                  - 214 -                              Си в UNIX

     реакция на этот сигнал не сбрасывается.
-    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);

А. Богатырев, 1992-95                  - 215 -                              Си в UNIX

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

6.4.1.  Напишите программу, выдающую на экран файл /etc/termcap.  Перехватывайте сиг-
нал   SIGINT, при получении сигнала запрашивайте "Продолжать?".  По ответу 'y' - про-
должить выдачу; по 'n' - завершить программу; по 'r' - начать выдавать файл с начала:
lseek(fd,0L,0).  Не забудьте заново переустановить реакцию на SIGINT, поскольку после
получения сигнала реакция автоматически сбрасывается.

    #include 
    void onintr(sig){       /* sig - номер сигнала  */
      signal (sig, onintr); /* восстановить реакцию */
      ... запрос и действия ...
    }
    main(){ signal (SIGINT, onintr); ... }

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

    signal (SIGINT, SIG_IGN);

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

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

    #include 
    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);

Напишите программу, которая ожидает ввода с клавиатуры  в  течение  10  секунд.  Если
ничего  не  введено  -  печатает  "Нет ввода", иначе - печатает "Спасибо".  Для ввода
можно использовать как вызов read,  так  и  функцию  gets  (или  getchar),  поскольку

А. Богатырев, 1992-95                  - 216 -                              Си в UNIX

функция  эта  все  равно  внутри себя издает системный вызов read.  Исследуйте, какое
значение возвращает fgets (gets) в случае прерывания ее системным вызовом.

    /* Копирование стандартного ввода на стандартный вывод
     * с установленным тайм-аутом.
     * Это позволяет использовать программу для чтения из FIFO-файлов
     * и с клавиатуры.
     * Небольшая модификация позволяет использовать программу
     * для копирования "растущего" файла (т.е. такого, который в
     * настоящий момент еще продолжает записываться).
     * Замечание:
     *       В ДЕМОС-2.2 сигнал НЕ сбивает чтение из FIFO-файла,
     *       а получение сигнала откладывается до выхода из read()
     *       по успешному чтению информации. Пользуйтесь open()-ом
     *       с флагом O_NDELAY, чтобы получить требуемый эффект.
     *
     *      Вызов: a.out /dev/tty
     *
     * По мотивам книги М.Дансмура и Г.Дейвиса.
     */

    #define WAIT_TIME 5 /* ждать 5 секунд */
    #define MAX_TRYS  5 /* максимум 5 попыток */
    #define BSIZE     256
    #define STDIN     0 /* дескриптор стандартного ввода  */
    #define STDOUT    1 /* дескриптор стандартного вывода */

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    char buffer [ BSIZE ];
    extern int errno;       /* код ошибки */

    void timeout(nsig){ signal( SIGALRM, timeout ); }
    void main(argc, argv) char **argv;{
       int fd, n, trys = 0;  struct stat stin, stout;

       if( argc != 2 ){
           fprintf(stderr, "Вызов: %s файл\n", argv[0]); exit(1);
       }
       if((fd = !strcmp(argv[1],"-")? STDIN : open(argv[1],O_RDONLY)) < 0){
           fprintf(stderr, "Не могу читать %s\n", argv[1]); exit(2);
       }
       /* Проверить, что ввод не совпадает с выводом,
        *     hardcat aFile >> aFile
        * кроме случая, когда вывод - терминал.
        * Такая проверка полезна для программ-фильтров (STDIN->STDOUT),
        * чтобы исключить порчу исходной информации */
       fstat(fd, &stin); fstat(STDOUT, &stout);
       if( !isatty(STDOUT) && stin.st_ino == stout.st_ino &&
                              stin.st_dev == stout.st_dev
       ){ fprintf(stderr,
          "\aВвод == выводу, возможно потеряна информация в %s.\n",argv[1]);
          exit(33);
       }

А. Богатырев, 1992-95                  - 217 -                              Си в UNIX

       signal( SIGALRM, timeout );
       while( trys < MAX_TRYS ){
               alarm( WAIT_TIME ); /* заказать сигнал через 5 сек */

               /* и ждем ввода ... */
               n = read( fd, buffer, BSIZE );

               alarm(0);       /* отменили заказ сигнала */
                     /* (хотя, возможно, он уже получен) */

               /* проверяем: почему мы слезли с вызова read() ? */
               if( n < 0 && errno == EINTR ){
                   /* Мы были сбиты сигналом SIGALRM,
                    * код ошибки EINTR - сисвызов прерван
                    * неким сигналом.
                    */
                   fprintf( stderr, "\7timed out (%d раз)\n", ++trys );
                   continue;
               }

               if( n < 0 ){
                   /* ошибка чтения */
                   fprintf( stderr, "read error.\n" ); exit(4);
               }
               if( n == 0 ){
                   /* достигнут конец файла */
                   fprintf( stderr, "Достигнут EOF.\n\n" ); exit(0);
               }
               /* копируем прочитанную информацию */
               write( STDOUT, buffer, n );
               trys = 0;
       }
       fprintf( stderr, "Все попытки провалились.\n" ); exit(5);
    }

Если мы хотим, чтобы сисвызов не мог прерываться сигналом, мы должны защитить его:

    #include 
    void (*fsaved)();
            ...
Предыдущая страница Следующая страница
1 ... 34 35 36 37 38 39 40  41 42 43 44 45 46 47 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама