я хочу знать причину вывода этой программы

#c #signals #computer-science #kill-process #pause

#c #сигналы #информатика #kill-process #пауза

Вопрос:

В оригинальной программе kill(pid, SIGUSR1); вызывается первым и pause(); в родительском процессе и в дочернем процессе pause(); вызывается первым, а затем kill(getppid(), SIGUSR1); его вывод приведен ниже

В измененной программе, если я заменю kill(getppid(), SIGUSR1); на pause(); в дочернем процессе вывод совершенно другой, я вставил вывод под кодом.

Может кто-нибудь объяснить мне, почему результат изменен

 **********************ORIGINAL PROGRAM***********************************
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

void action(int dummy){  
     sleep(1); 
     printf("Switchingn");
}

int main(int argc, char *argv[]){  
    pid_t pid;

    if((pid=fork())>0){//parent  
        sleep(1);
        while(1){
            printf("Parent is runningn");  
            kill(pid, SIGUSR1);  
            signal(SIGUSR1, action);  
            pause();
        }
    }
    else  //child code
        while(1){//child  
            signal(SIGUSR1, action);  
            pause();
            printf("Child is runningn");  
            kill(getppid(), SIGUSR1);
    }
}


//OUTPUT OF THIS PROGRAM
Parent is running
Switching
Child is running
Switching
Parent is running
Switching
Child is running
Switching
Parent is running
Switching
Child is running
Switching
Parent is running
Switching
Child is running
Switching
Parent is running
Switching
Child is running






*********************CHANGED PROGRAM************************

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

void action(int dummy){  
     sleep(1); 
     printf("Switchingn");
}

int main(int argc, char *argv[]){  
    pid_t pid;

    if((pid=fork())>0){//parent  
        sleep(1);
        while(1){
            printf("Parent is runningn");  
            kill(pid, SIGUSR1);  
            signal(SIGUSR1, action);  
            pause();
        }
    }
    else  //child code
        while(1){//child  
            signal(SIGUSR1, action);  
            kill(getppid(), SIGUSR1);
            printf("Child is runningn");  
             pause();
    }
}





//OUTPUT OF THIS PROGRAM
//Child is running
//User defined signal 1


 

Ответ №1:

Если процесс получает SIGUSR1 до того, как он настроил для него обработчик сигнала (и не игнорирует или не удерживает его), процесс будет завершен. (Подробнее. см. Справочную страницу signal).

Ваш код (обе версии) имеет несколько условий гонки.

В первой версии:

 if((pid=fork())>0){//parent  
    sleep(1);
    while(1){
        printf("Parent is runningn");  
        kill(pid, SIGUSR1);  
        signal(SIGUSR1, action);  
        pause();
    }
}
else  //child code
    while(1){//child  
        signal(SIGUSR1, action);  
        pause();
        printf("Child is runningn");  
        kill(getppid(), SIGUSR1);
}
 

Ребенок будет ждать SIGUSR1.

Примерно в то же время родительский сервер переходит в спящий режим, а затем отправляет SIGUSR1 дочернему устройству.

Дочерний элемент, после получения сигнала, выполнит пару printfs, а затем отправит SIGUSR1 родительскому элементу.

Примерно в то же время родительский сервер настроит обработчик сигналов для SIGUSR1.

Вполне вероятно (но не обязательно всегда так), что дочерний элемент настроит обработчик сигнала, пока родитель выполняет режим ожидания (1); сигнал, отправленный от родителя дочернему элементу, будет перехвачен, а не приведет к завершению дочернего элемента.

Вероятно (но не обязательно всегда так), что к тому времени, когда дочерний элемент выполнит свои два printf, родительский элемент настроил обработчик сигнала; сигнал, отправленный от дочернего элемента родительскому элементу, будет перехвачен, а не приведет к завершению родительского элемента.

Но, поскольку существуют условия гонки, незначительные изменения во времени могут привести к поломке.

Во второй версии:

 if((pid=fork())>0){//parent  
    sleep(1);
    while(1){
        printf("Parent is runningn");  
        kill(pid, SIGUSR1);  
        signal(SIGUSR1, action);  
        pause();
    }
}
else  //child code
    while(1){//child  
        signal(SIGUSR1, action);  
        kill(getppid(), SIGUSR1);
        printf("Child is runningn");  
        pause();
}
 

Дочерний элемент отправляет SIGUSR1 родительскому элементу сразу после его разветвления, и это почти наверняка произойдет, пока родительский элемент находится в середине режима ожидания (1). Поскольку родительский сервер еще не настроил обработчик для SIGUSR1, сигнал завершит его. Затем оболочка выводит определенный пользователем сигнал 1, который является длинным именем сигнала SIGUSR1.

Все будет работать лучше, если вы настроите обработчик сигналов для SIGUSR1 перед форком. Таким образом, и родительский, и дочерний элементы будут готовы к обработке сигнала.