Ответ 1
В коде есть некоторые ошибки:
-
Избегайте вызова функции
printf( )
в обработчике сигналов.SIGNAL(7)
руководство предоставляет список разрешенных функций, вызывающих их, безопасно внутри обработчиков сигналов. Читайте:Функции безопасности с использованием Async
Функция обработчика сигнала должна быть очень осторожной, поскольку обработка в другом месте может быть прервано в какой-то произвольной точке исполнения программы. POSIX имеет понятие "безопасная функция". Если сигнал прерывает выполнение небезопасной функции, а обработчик вызывает небезопасную функцию, тогда поведение программы undefined.
-
Использовать тип возврата main()
int
; прочитайте "Что должноmain()
вернуться в C?" -
x
должен бытьpid_t
. (Идентификация процесса).
Теперь давайте предположим, что ваша программа компилируется и запускается (не прерывается никаким другим сигналом при выполнении обработчика):
Я просто отступаю от вашего кода и переключая определение функции f()
до main, потому что отсутствует объявление функции, а также добавление некоторых комментариев, которые вы должны прочитать:
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void f( )
{
printf ("signal received\n");
exit (0);//if child receives signal then child exits from here
} // ******* mostly happens
int main ( )
{ int x;
signal (SIGUSR1, f);// First: handler registered
x = fork ( ); // Second: Child created, now child will execute in
if (x == -1) // parallel with parent
exit (1);
if (x != 0) {// parent
kill(x, SIGUSR1) ; // Third: sends signal to child
sleep(2); // Forth: parent sleep
}
return 0; // parent always exits
// Child can exits from here ****
// if signal sent from parent delayed
}
В функции main()
вы регистрируете функцию f()
для сигнала SIGUSR1
, после чего вызывает fork()
, чтобы создать новый процесс. Во время выполнения функции fork()
возвращается дочерний процесс, который запускается параллельно с родительским процессом.
Поскольку я вижу ваш код, я думаю, что вы понимаете, что дочерний процесс - это копирование родительского процесса, за исключением того, что значения переменных могут отличаться от возвращаемой точки fork()
и, следовательно, x
отличается в дочернем и родительском процессах. Мы можем использовать возвращаемое значение из fork, чтобы определить, запущена ли программа в родительском процессе или в дочернем. Но учтите, что это не родительский, а фактически дочерний процесс получает сигнал SIGUSR1
. Значение идентификатора самого процесса всегда 0
для любого процесса. Вы проверяете возвращаемое значение x = fork()
, которое является pid вновь созданного дочернего процесса, в дочернем процессе значение x
равно 0
и в родительском x != 0
. Следовательно, сигнал отправляется из родительского процесса в дочерний процесс.
Ваши комментарии:
Я думаю, что программа выше просит систему запустить функцию
f( )
(которая отображает"signal received"
), когда сигналSIGUSR1
принимается родительским процессом.
У меня создалось впечатление, что вы не считаете, что оба процесса выполняются одновременно, и "может случиться так, что вскоре после fork()
создайте дочерний процесс, запуск дочернего процесса и его немедленное завершение до того, как родительский процесс сможет отправить сигнал для ребенка (или дочерний процесс может принимать сигнал)". В этом случае функция f()
никогда не получит возможность выполнить, а printf в обработчике сигналов никогда не будет печататься.
Но возможность того, что я описал выше, очень низок, потому что fork требует времени для создания нового процесса. И даже если вы снова и снова выполняете код, большинство сигналов, передаваемых из родительского процесса, будут выполнять обработчик сигналов.
Каков правильный способ написания этого кода?
Код xc. Правильный способ - установить флаг, который указывает, что обработчик сигнала выполнен, и затем вызвать функцию printf на основе значения флага вне обработчика сигнала, как я описал в своем ответе: Как избежать использования printf в обработчике сигналов? И причина, по которой это объясняется Джонатаном Леффлером в его ответить.
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
volatile sig_atomic_t flag = 0; //initially flag == 0
void f(){
flag = 1; // flag is one if handler executes
}
int main(void){
pid_t x;
(void) signal (SIGUSR1, f);
x = fork();
if(x == -1)
exit(EXIT_FAILURE);
if (x != 0){// parent
kill(x, SIGUSR1);
sleep(1);
}
if(flag)//print only if signal caught and flag == 1
printf("%s signal received \n", x == 0 ? "Child:" : "Parent:");
return EXIT_SUCCESS;
}
Скомпилируйте его и выполните:
@:~$ gcc -Wall -pedantic -std=c99 x.c -o x
@:~$ ./x
Child: signal received
@:~$
Обратите внимание, что дочерний процесс печатает, потому что родительский посылает сигнал дочернему устройству (но родительский процесс не печатает как отсутствие сигнала в родительском элементе). Таким образом, поведение вышеуказанного кода по-прежнему похоже на то, что вы получали в своем коде. Ниже я добавил еще один пример, в котором я пытаюсь продемонстрировать, что "одновременное выполнение результатов процессов отличается в разных экземплярах исполнения" (читать комментарии).
// include header files...
volatile sig_atomic_t flag = 0;
void f(){
flag = 1;
}
int main(void){
pid_t x;
(void) signal (SIGUSR1, f);
(void) signal (SIGUSR2, f); // added one more signal
x= fork ( );
if(x == -1)
exit(EXIT_FAILURE);
if (x != 0){// parent
kill(x, SIGUSR1);
while(!flag); // in loop until flag == 0
}
if (x == 0){//child
kill(getppid(), SIGUSR2); // send signal to parent
while(!flag); // in loop until flag == 0
}// ^^^^ loop terminates just after signal-handler sets `flag`
if(flag)
printf("%s signal received \n", x == 0 ? "Child:" : "Parent:");
return EXIT_SUCCESS;
}
В приведенном выше коде два сигнала регистрируются как в родительском, так и в дочернем процессах. Родительский процесс не спит, а занят в цикле while, пока сигнал не установит флаг. Аналогично, у дочернего процесса есть цикл, который прерывается, когда флаг становится 1 в обработчике сигналов. Теперь скомпилируйте этот код и повторите его. Я часто пытался получить следующий вывод в своей системе.
@:~$ gcc -Wall -pedantic -std=c99 x.c -o x
@:~$ ./x
Child: signal received
Parent: signal received
@:~$ ./x
Child: signal received
Parent: signal received
@:~$ ./x
Child: signal received
Parent: signal received
@:~$ ./x
Parent: signal received // <------
@:~$ Child: signal received
./x
Child: signal received
Parent: signal received
@:~$ ./x
Parent: signal received // <------
@:~$ Child: signal received
@:~$
Отметьте вывод, в одном случае: "пока дочерний процесс не создаст родительский переданный сигнал и не войдет во время цикла, и когда дочерний процесс получит шанс выполнить (в зависимости от планирования процессора), он отправляет сигнал родителям и перед родительским процессом получить шанс на выполнение дочернего процесса, получает сигнал и выводит сообщение". Но иногда бывает так, что перед печатью printf print; родитель получает и печатает сообщение (это я помечен с помощью стрелки).
В последнем примере я пытаюсь показать выполнение дочернего процесса параллельно с родительским процессом, и вывод может отличаться, если вы не применяете механизм управления concurrency.
Некоторый хороший ресурс Чтобы узнать сигналы (1) Библиотека GNU C: Обработка сигналов (2) Стандарт кодирования CERT C 11. Сигналы (SIG).