2012-01-01 2 views
4

После вызова fork у меня есть один отец, который должен отправить sigusr1 или sigusr2 (в зависимости от значения переменной cod) своему ребенку. Ребенок должен установить правильные обработчики перед получением sigusr1 или sigusr2. Для этого я приостанавливаю отца, ожидая, пока ребенок скажет ему, что он сделал с установкой обработчика. Отцу сообщается sigusr1, и обработчик для этого сигнала устанавливается перед вызовом fork. Однако, кажется, отец не может вернуться с паузы, заставляя меня думать, что он на самом деле никогда не вызывает обработчик sigusr1.Проблемы с обработкой сигналов

[...] 

    typedef enum{FALSE, TRUE} boolean; 

    boolean sigusr1setted = FALSE; 
    boolean sigusr2setted = FALSE; 


    void 
    sigusr1_handler0(int signo){ 
      return; 
    } 

    void 
    sigusr1_handler(int signo){ 
      sigusr1setted = TRUE; 
    } 

    void 
    sigusr2_handler(int signo){ 
      sigusr2setted = TRUE; 
    } 

    int main(int argc, char *argv[]){ 
     [...] 

     if(signal(SIGUSR1, sigusr1_handler0) == SIG_ERR){ 
      perror("signal 0 error"); 
      exit(EXIT_FAILURE); 
     } 

     pid = fork(); 
        if (pid == 0){ 
         if(signal(SIGUSR1, sigusr1_handler) == SIG_ERR){ 
          perror("signal 1 error"); 
          exit(EXIT_FAILURE); 
         } 

         if(signal(SIGUSR2, sigusr2_handler) == SIG_ERR){ 
          perror("signal 2 error"); 
          exit(EXIT_FAILURE);   
         } 

         kill(SIGUSR1, getppid()); // wake up parent by signaling him with sigusr1 

         // Wait for the parent to send the signals... 
         pause(); 

         if(sigusr1setted){ 
          if(execl("Prog1", "Prog1", (char*)0) < 0){ 
           perror("exec P1 error"); 
           exit(EXIT_FAILURE); 
          } 
         } 

         if(sigusr2setted){ 
          if(execl("Prog2", "Prog2", (char*)0) < 0){ 
           perror("exec P2 error"); 
           exit(EXIT_FAILURE); 
          } 
         } 

         // Should'nt reach this point : something went wrong... 
         exit(EXIT_FAILURE); 

        }else if (pid > 0){ 
         // The father must wake only after the child has done with the handlers installation 

         pause(); 

         // Never reaches this point ... 
         if (cod == 1) 
          kill(SIGUSR1, pid); 
         else 
          kill(SIGUSR2, pid); 

         // Wait for the child to complete.. 
         if(wait(NULL) == -1){ 
          perror("wait 2 error"); 
          exit(EXIT_FAILURE);    
         } 

          [...] 

        }else{ 
         perror("fork 2 error"); 
         exit(EXIT_FAILURE); 
        } 
     [...] 

     exit(EXIT_SUCCESS); 
    } 
+1

Я подозреваю, что вы подготовили условия гонки в противоположном направлении к тому, что вы ожидали. Ребенок отправил SIGUSR1 родительскому элементу * до того, как родитель достиг «паузы». –

+0

Поместите еще несколько printf() s (и убедитесь, что вы сбросили флажок), чтобы вы могли видеть, как далеко добрался ребенок, и если был вызван обработчик сигнала родителя. Это скажет вам больше. –

+0

@OliCharlesworth: Не видел это .. спасибо. – user996922

ответ

0

Сборка вразумительный ответ из комментариев - так это сообщество вики с самого начала. (! Если Oli дает ответ, до-голосования, что вместо этого)

Oli Charlesworth дал то, что, вероятно, суть проблемы:

  • Я подозреваю, что вы произвели состояние гонки в противоположном направлении к тому, что вы ожидали. Ребенок отправил SIGUSR1 родительскому лицу до того, как родитель достиг pause().

ouah отметил точно:

  • Объект разделяется между обработчиком сигнала и код не-обработчика (ваши логические объекты) должны иметь тип volatile sig_atomic_t в противном случае код не определен.

Это говорит о том, что POSIX позволяет немного более слабости, чем стандарт C, для того, что можно сделать внутри обработчика сигнала. Можно также отметить, что C99 предоставляет <stdbool.h> для определения типа bool.

Оригинальный плакат прокомментировал:

Я не понимаю, как я могу убедиться, что родитель идет в pause() вызова первого без использования sleep() у ребенка (который ничего не гарантирует). Есть идеи?

Предложение: Используйте usleep() (μ-сон, или сон в микросекундах), или nanosleep() (сон в наносекунд)?

Или использовать другой механизм синхронизации, такие как:

  1. родительский процесс создает буфер FIFO;
  2. fork();
  3. ребенок открывает FIFO для записи (блокировка до чтения);
  4. родитель открывает FIFO для чтения (блокировка до появления записи);
  5. при разблокировке, так как обратные вызовы open() возвращаются, оба процесса просто закрывают FIFO;
  6. Родитель удаляет FIFO.

Обратите внимание, что между этими двумя процессами отсутствует передача данных через FIFO; код просто полагается на ядро, чтобы блокировать процессы, пока не появится читатель и писатель, поэтому оба процесса готовы к работе.

Другая возможность заключается в том, что родительский процесс мог бы попробовать if (siguser1setted == FALSE) pause();, чтобы уменьшить окно состояния гонки. Однако он только уменьшает окно; он не гарантирует, что состояние гонки не может произойти. То есть закон Мерфи применяется и сигнал может поступать между временем завершения теста и временем, которое выполняется pause().

Все это говорит о том, что сигналы не являются очень хорошим механизмом IPC. Они могут использоваться для IPC, но они редко используются для синхронизации.

Кстати, нет необходимости проверять возвращаемое значение любого из семейств функций exec*(). Если системный вызов возвращается, он не удался.

И снова спросил спрашивающий:

Не было бы лучше использовать POSIX семафоры разделены между процессами?

Семафоры, безусловно, были бы еще одним действенным механизмом для синхронизации двух процессов. Поскольку я, конечно, должен смотреть на страницы руководства для семафоров, тогда как я помню, как использовать FIFO, не глядя, я не уверен, что я действительно их использую, но создание и удаление FIFO имеет свой собственный набор проблем поэтому не ясно, что оно каким-либо образом «лучше» (или «хуже»); просто другой. Это mkfifo(), open(), close(), unlink() для FIFOs против sem_open() (или sem_init()), sem_post(), sem_wait(), sem_close(), и, возможно, sem_unlink() (или sem_destroy()) для семафоров. Возможно, вам стоит подумать о регистрации функции «FIFO remove» или «Семафорная очистка» с помощью atexit(), чтобы убедиться, что FIFO или семафор уничтожены в максимально возможной степени. Однако это, вероятно, OTT для тестовой программы.

Смежные вопросы