Проблемы с обработкой сигнала - PullRequest
4 голосов
/ 01 января 2012

После вызова вилки у меня есть один отец, который должен отправить 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 Ответ

0 голосов
/ 01 января 2012

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

Оли Чарльзуорт дал, вероятно, суть проблемы:

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

ouah , точно отмеченных:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

...