Я играю с процессами и сигналами на linux, ниже приведен простой тест, который я написал в C:
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <mqueue.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
void work(void);
int main(void) {
pid_t children[10];
for(size_t i = 0; i < 10; i++) {
pid_t pid = fork();
if(pid == -1) {
perror("parent: error forking");
return EXIT_FAILURE;
}
if(pid == 0) {
raise(SIGSTOP); // child stops itself
work(); // after resuming it goes on to execute work()
return EXIT_SUCCESS; // and finally, it successfully terminates
} else {
fprintf(stdout, "parent: spawned child (%d)\n", pid);
children[i] = pid;
}
}
// parent spawned all 10 children who are now stopped - begin resuming them one by one
for(size_t i = 0; i < 10; i++) {
fprintf(stdout, "parent: signaling child (%d) to continue...\n", children[i]);
if(kill(children[i], SIGCONT) == -1) {
fprintf(stderr, "parent: error signalling child (%d) to continue: %s\n", children[i], strerror(errno));
}
}
return EXIT_SUCCESS; // exit from parent once all children have been resumed
}
void work(void) {
pid_t mypid = getpid();
srand(mypid);
int32_t sleep_time = (rand() % 10) + 1;
fprintf(stdout, "(%d): began sleeping for %d seconds\n", mypid, sleep_time);
sleep(sleep_time);
fprintf(stdout, "(%d): done sleeping after %d seconds\n", mypid, sleep_time);
}
Идея заключается в следующем:
Родитель порождает 10 дочерних процессов, каждый из которых отправляет себе SIGSTOP
сразу после своего появления. Как только родитель успешно порождает все 10 процессов, он сразу же отправляет всем 10 из них SIGCONT
.
Как только дочерний процесс возобновляется, он начинает выполнение work()
(что просто приостанавливает его выполнение на случайное время от 0 до 10 секунд, выводя информацию на стандартный вывод по пути), после чего он успешно завершается.
Вот как выглядит успешный вывод:
[I] bogdan in ~/dev/mserve
>> ./prog
parent: spawned child (138655)
parent: spawned child (138656)
parent: spawned child (138657)
parent: spawned child (138658)
parent: spawned child (138659)
parent: spawned child (138660)
parent: spawned child (138661)
parent: spawned child (138662)
parent: spawned child (138663)
parent: spawned child (138664)
parent: signaling child (138655) to continue...
(138655): began sleeping for 9 seconds
parent: signaling child (138656) to continue...
(138656): began sleeping for 3 seconds
parent: signaling child (138657) to continue...
parent: signaling child (138658) to continue...
parent: signaling child (138659) to continue...
parent: signaling child (138660) to continue...
parent: signaling child (138661) to continue...
parent: signaling child (138662) to continue...
parent: signaling child (138663) to continue...
parent: signaling child (138664) to continue...
(138659): began sleeping for 4 seconds
(138657): began sleeping for 5 seconds
(138658): began sleeping for 7 seconds
(138660): began sleeping for 10 seconds
(138663): began sleeping for 3 seconds
(138662): began sleeping for 7 seconds
(138664): began sleeping for 7 seconds
(138661): began sleeping for 2 seconds
[I] bogdan in ~/dev/mserve
(138661): done sleeping after 2 seconds
(138656): done sleeping after 3 seconds
(138663): done sleeping after 3 seconds
(138659): done sleeping after 4 seconds
(138657): done sleeping after 5 seconds
(138658): done sleeping after 7 seconds
(138662): done sleeping after 7 seconds
(138664): done sleeping after 7 seconds
(138655): done sleeping after 9 seconds
(138660): done sleeping after 10 seconds
Как указано в информационных сообщениях, все 10 процессов успешно завершили свой сон и завершили работу.
Проблема
Возможно, один из каждых 3 раз, случайное число из 10 дочерних процессов "зависает" и не может возобновиться после SIGSTOP. kill(2)
от родителя, который отправляет SIGCONT, завершается успешно, но процесс (ы) остаются в состоянии ожидания.
Выходные данные выглядят следующим образом:
[I] bogdan in ~/dev/mserve
> ./alt
parent: spawned child (139369)
parent: spawned child (139370)
parent: spawned child (139371)
parent: spawned child (139372)
parent: spawned child (139373)
parent: spawned child (139374)
parent: spawned child (139375)
parent: spawned child (139376)
parent: spawned child (139377)
parent: spawned child (139378)
parent: signaling child (139369) to continue...
parent: signaling child (139370) to continue...
parent: signaling child (139371) to continue...
parent: signaling child (139372) to continue...
parent: signaling child (139373) to continue...
parent: signaling child (139374) to continue...
parent: signaling child (139375) to continue...
parent: signaling child (139376) to continue...
parent: signaling child (139377) to continue...
parent: signaling child (139378) to continue...
(139371): began sleeping for 4 seconds
(139369): began sleeping for 8 seconds
(139373): began sleeping for 7 seconds
(139370): began sleeping for 3 seconds
(139375): began sleeping for 9 seconds
(139372): began sleeping for 7 seconds
(139374): began sleeping for 10 seconds
(139376): began sleeping for 8 seconds
(139377): began sleeping for 7 seconds
[I] bogdan in ~/dev/mserve
(139370): done sleeping after 3 seconds
(139371): done sleeping after 4 seconds
(139373): done sleeping after 7 seconds
(139372): done sleeping after 7 seconds
(139377): done sleeping after 7 seconds
(139369): done sleeping after 8 seconds
(139376): done sleeping after 8 seconds
(139375): done sleeping after 9 seconds
(139374): done sleeping after 10 seconds
Только на этот раз 9 процессов успешно завершены (было напечатано 9 «готовых спящих» сообщений).
Выполняя $ ps au
в оболочке, я могу наблюдать «зависшие» процессы (обратите внимание на состояние T
):
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
bogdan 139378 0.0 0.0 2312 80 pts/3 T 20:31 0:00 ./prog
Я даже могу дать им сигнал продолжить из моей оболочки:
$ kill -SIGCONT 139378
(139378): began sleeping for 5 seconds
...
(139378): done sleeping after 5 seconds
Еще одна странная деталь
При выполнении родительского процесса с помощью strace
( например, $ strace ./program
), проблема никогда не возникает, все 10 процессов возобновляются должным образом в 100% случаев. Только когда я запускаю родителя непосредственно из своей оболочки, я могу наблюдать за этой проблемой.
Я уже несколько раз просматривал справочную страницу signal(7)
, но я не понимаю, почему это происходит.