Отправка SIGCONT завершается сбоем с непредсказуемым поведением - linux - PullRequest
0 голосов
/ 25 апреля 2020

Я играю с процессами и сигналами на 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), но я не понимаю, почему это происходит.

Ответы [ 2 ]

1 голос
/ 25 апреля 2020

Скорее всего, сбой возобновления происходит в том случае, если родитель поставляет SIGCONT ребенку, который еще не остановил себя. Такой сигнал будет игнорироваться, так как процесс не останавливается во время обработки.

Ничто в вашей программе не предотвращает этого; вместо этого вы просто полагаетесь на то, что дети успевают остановиться быстрее, чем родители, чтобы дать им сигнал - состояние гонки. Тот факт, что вы можете возобновить застрявший процесс, отправив ему (дополнительный) SIGCONT, согласуется с этим диагнозом, и вполне вероятно, что strace влияет на время, достаточное для того, чтобы дети всегда выигрывали свои гонки.

1 голос
/ 25 апреля 2020

Похоже, что существует состязание между дочерними процессами, отправляющими себя SIGSTOP, и родительским процессом, отправляющим SIGCONT. Иногда родительский процесс отправляет SIGCONT до того, как дочерний объект отправил SIGSTOP, следовательно, дочерний процесс зависает.

...