Различные системы ведут себя по-разному, используя signal()
. На Mac под управлением macOS (10.14.1 Mojave, но это также относится и к другим версиям) оригинальный код, использующий signal()
, работает нормально - есть множество ошибок, но обработка сигналов работает. В виртуальной машине под управлением Ubuntu 18.04 LTS (размещенной на том же Mac) код, использующий signal()
, не работает должным образом, поскольку (как отмечено в комментариях ) обработка сигнала сбрасывается на значение по умолчанию, если сигнал перехватывается до ввода обработчика сигнала. Это устанавливает условия гонки. Оба эти различных поведения соответствуют стандарту C - macOS обеспечивает «надежные сигналы», а Linux - нет.
Однако еще не все потеряно; Linux и macOS имеют sigaction()
, что обеспечивает отличный контроль - и может использоваться для имитации любого набора поведения signal()
. См. Также В чем разница между sigaction()
и signal()
?
Существуют и другие проблемы, которые необходимо решить. Вы должны проверить аргументы и (убедитесь, что можете) открыть файл перед разветвлением; Вы должны сообщить об ошибке. Ребенок должен отметить PID своего исходного родителя, чтобы в случае смерти родителя он мог попытаться отправить ему сигнал и получить уведомление о том, что произошел сбой. Когда родительский родитель умирает, родительский PID переключается на PID 1, процесс init
, который в основном заканчивает тем, что игнорирует сигнал.
Я исправил проблему с feof()
- нет смысла использовать feof()
в условиях контроля цикла (см. while (!feof(file))
всегда неправильно! ). Вместо этого протестируйте основную функцию ввода / вывода. (Библиотека C в Ubuntu помечает функцию fgets()
, поэтому необходимо использовать возвращаемое значение. Обратите внимание на предупреждения компилятора.)
Приведенный ниже код замедляет основной цикл печати, поэтому он обрабатывает 4 строки в секунду, а не работает с полным наклоном. MacOS и Linux имеют nanosleep()
; Linux не сделал доступным usleep()
, хотя он имеет более простой (но менее мощный) интерфейс.
Приведенный ниже код отделяет sigcount
, используемый родителем, от i
, используемого ребенком для подсчета полученных и отправленных сигналов. Я также предположил поддержку C99 или более поздних версий и переместил объявления переменных ближе к месту их использования.
Как написано, код никогда не входит в состояние, в котором SIGUSR1
игнорируется, не говоря уже о состоянии, в котором он вызывает ошибку. Дочерний элемент (сейчас) отправляет достаточное количество сигналов (n *= 2;
отправляет вдвое больше сигналов, чем ожидает «родитель»), но родитель все еще застрял в исходном коде для if (sigcount < n)
. Обратите внимание, что вы не можете считать полученные сигналы, если игнорируете эти сигналы. Эта часть кода нуждается в серьезной переработке. Вы должны выйти из цикла чтения файлов, когда вы получили соответствующее количество сигналов, а затем просто сосчитать следующие пять сигналов (не игнорировать их), а затем установить обработчик sig_exit
. Это не реализовано в приведенном ниже коде.
Я не пытался решить проблему с вызовом printf()
в обработчиках сигналов. Похоже, что это не вызывает проблем в этом коде (и я бы этого не ожидал), но в целом это опасно. См. Как избежать использования printf()
в обработчиках сигналов? для получения более подробной информации.
#define _XOPEN_SOURCE 700
позволяет определять функции POSIX и т. Д., Даже если для параметров компиляции требуется -std=c11
(что отключает большинство расширений). Исходный код программы был sig41.c
, поэтому программа была sig41
. Код правильно компилируется с помощью (GCC 8.2.0):
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes \
> sig41.c -o sig41
$
Наряду с некоторыми другими незначительными изменениями (например, сообщая об ошибках на stderr
), этот код работает как в Ubuntu, так и в macOS:
#define _XOPEN_SOURCE 700
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
static void sig_exit(int signo);
static void sig_handler(int signo);
static int sigcount, n;
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
FILE *f = fopen(argv[1], "r");
if (f == NULL)
{
fprintf(stderr, "Error opening file %s for reading\n", argv[1]);
exit(EXIT_FAILURE);
}
struct sigaction sa = { 0 };
sa.sa_handler = sig_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGUSR1, &sa, 0);
srand(time(NULL));
n = rand() % 20 + 10;
int pid = fork();
if (pid < 0)
{
fprintf(stderr, "failed to fork!\n");
exit(EXIT_FAILURE);
}
else if (pid != 0)
{
printf("%d\n", getpid());
if (sigcount < n)
{
int linecount = 1;
while (linecount == 1)
{
char buffer[1024];
while (fgets(buffer, 1024, f))
{
if (sigcount % 2 == 0)
{
printf("%d %s", linecount, buffer);
}
linecount++;
// nap time = quarter second
struct timespec nap = { .tv_sec = 0, .tv_nsec = 250000000 };
nanosleep(&nap, NULL);
}
rewind(f);
linecount = 1;
}
}
else if (sigcount < (n + 6))
{
printf("Going into SIG_IGN mode\n");
sa.sa_handler = SIG_IGN;
sigaction(SIGUSR1, &sa, 0);
}
else
{
printf("%d of %d signals received - sig_exit mode\n", sigcount, n);
sa.sa_handler = sig_exit;
sigaction(SIGUSR1, &sa, 0);
}
}
else
{
fclose(f);
int pid = getpid();
int ppid = getppid();
n *= 2; // Child needs to send more signals
for (int i = 0; i < n; i++)
{
int interval = rand() % 10 + 1;
printf("Sending signal %d of %d from %d to %d\n", i + 1, n, pid, ppid);
if (kill(ppid, SIGUSR1) != 0)
{
fprintf(stderr, "Child failed to signal parent - exiting\n");
exit(1);
}
printf("Child sleeping for %d seconds\n", interval);
sleep(interval);
}
}
}
static void sig_handler(int signo)
{
sigcount++;
if (signo == SIGUSR1)
printf("Received SIGUSR1 signal %d of %d\n", sigcount, n);
else
printf("Error: received undefined signal\n");
}
static void sig_exit(int signo)
{
if (signo == SIGUSR1)
{
fprintf(stderr, "Received SIGUSR1 signal\n");
exit(SIGUSR1);
}
else
printf("Error: received undefined signal\n");
}
Немного сложно хорошо показать эту работу. Я остановил программу, чтобы остановить ее.
$ ./sig41 sig41.c
3247
1 #define _XOPEN_SOURCE 700
Sending signal 1 of 30 from 3248 to 3247
Child sleeping for 7 seconds
Received SIGUSR1 signal 1 of 15
Sending signal 2 of 30 from 3248 to 3247
Child sleeping for 7 seconds
Received SIGUSR1 signal 2 of 15
30 sa.sa_flags = 0;
31 sigemptyset(&sa.sa_mask);
…
56 }
57 linecount++;
Sending signal 3 of 30 from 3248 to 3247
Child sleeping for 1 seconds
Received SIGUSR1 signal 3 of 15
Sending signal 4 of 30 from 3248 to 3247
Child sleeping for 4 seconds
Received SIGUSR1 signal 4 of 15
62 rewind(f);
63 linecount = 1;
…
76 sigaction(SIGUSR1, &sa, 0);
77 }
Sending signal 5 of 30 from 3248 to 3247
Child sleeping for 2 seconds
Received SIGUSR1 signal 5 of 15
Sending signal 6 of 30 from 3248 to 3247
Child sleeping for 3 seconds
Received SIGUSR1 signal 6 of 15
86 {
87 int interval = rand() % 10 + 1;
…
96 }
97 }
Sending signal 7 of 30 from 3248 to 3247
Child sleeping for 7 seconds
Received SIGUSR1 signal 7 of 15
Sending signal 8 of 30 from 3248 to 3247
Child sleeping for 10 seconds
Received SIGUSR1 signal 8 of 15
8 static void sig_exit(int signo);
9 static void sig_handler(int signo);
…
46 {
47 int linecount = 1;
Sending signal 9 of 30 from 3248 to 3247
Child sleeping for 5 seconds
Received SIGUSR1 signal 9 of 15
Sending signal 10 of 30 from 3248 to 3247
Child sleeping for 8 seconds
Received SIGUSR1 signal 10 of 15
68 printf("Going into SIG_IGN mode\n");
69 sa.sa_handler = SIG_IGN;
…
98 }
99
Sending signal 11 of 30 from 3248 to 3247
Child sleeping for 9 seconds
Received SIGUSR1 signal 11 of 15
Sending signal 12 of 30 from 3248 to 3247
Child sleeping for 4 seconds
Received SIGUSR1 signal 12 of 15
18 exit(EXIT_FAILURE);
19 }
…
32 sigaction(SIGUSR1, &sa, 0);
33
Sending signal 13 of 30 from 3248 to 3247
Child sleeping for 6 seconds
Received SIGUSR1 signal 13 of 15
Sending signal 14 of 30 from 3248 to 3247
Child sleeping for 6 seconds
Received SIGUSR1 signal 14 of 15
58 // nap time = quarter second
59 struct timespec nap = { .tv_sec = 0, .tv_nsec = 250000000 };
…
80 {
81 fclose(f);
Sending signal 15 of 30 from 3248 to 3247
Child sleeping for 7 seconds
Received SIGUSR1 signal 15 of 15
Sending signal 16 of 30 from 3248 to 3247
Child sleeping for 8 seconds
Received SIGUSR1 signal 16 of 15
110 {
111 if (signo == SIGUSR1)
…
22 if (f == NULL)
23 {
Sending signal 17 of 30 from 3248 to 3247
Child sleeping for 1 seconds
Received SIGUSR1 signal 17 of 15
Sending signal 18 of 30 from 3248 to 3247
Child sleeping for 6 seconds
Received SIGUSR1 signal 18 of 15
28 struct sigaction sa = { 0 };
29 sa.sa_handler = sig_handler;
…
^C
$
Если вы работаете с выходными данными, вы можете увидеть, что выходные данные останавливаются для заданного количества строк - если дочерний элемент спит в течение 1 секунды, 4 выходные строки опускаются.