Убить дочерний процесс при выходе родительского процесса в C

Я пытаюсь создать программу, где она выполняет какую-то задачу и выходит. Тем не менее, я хочу выйти из программы, если пользователь вводит некоторую строку в терминале.

Как я думал, чтобы решить это: создать родительский процесс, где он выполняет основную задачу. главная задача имеет цикл. создайте дочерний процесс, где он ожидает ввода от пользователя. если пользователь вводит строку выхода, дочерний процесс обновит переменную и уничтожит себя. Цикл в родительском процессе проверит значение переменной, если это значение будет изменено дочерним процессом, он также завершится. Если пользователь не вводит строку выхода, родительский процесс завершит задачу и убьет себя. в этот момент я хочу также убить дочерний процесс. Как я могу это сделать?

Спасибо

3 ответа

Решение

Спасибо, что сказали, чего вы на самом деле пытаетесь достичь.

Вам может оказаться гораздо проще создать один многопоточный процесс, чем создавать два однопоточных процесса.

  1. Поток № 1 работает в цикле и периодически проверяет переменную exit_flag, Если переменная установлена, то она вызывает exit, Если он заканчивает работу, он вызывает exit,

  2. Поток № 2 ожидает ввода от пользователя. Если он получает ввод, он устанавливает переменную exit_flag,

Таким образом, когда вы звоните exitоба потока автоматически завершатся. Вам нужно будет использовать мьютекс / семафор или другой вид синхронизации, например, если у вас есть libatomic-ops:

#include "atomic_ops.h"

AO_t exit_flag;

// In thread #2
AO_store(&exit_flag, 1);

// In thread #1
while (!AO_load(&exit_flag)) {
    ...
}

Предупреждение о приведенном выше коде: установка глобального флага работает только таким образом, потому что основной поток читает только глобальный флаг и ничего больше. Если вам нужно отправить более одного слова между потоками, вам понадобится какой-то механизм синхронизации, например мьютекс, семафор или AO_store_release в паре с AO_store_acquire,

Если вы используете процессы вместо потоков: поскольку процессы по умолчанию не разделяют изменяемую память, это не так просто, как "обновление переменной". Каждый процесс имеет свой собственный набор личных переменных. Вам нужно будет настроить область разделяемой памяти или связаться каким-либо другим способом (например, сигнал, канал, сокет), что является дополнительной работой для вас.

Полный пример

Вот быстрый полный пример. Основной поток "работает", что включает обратный отсчет от 5. Когда он достигает 0, он выходит и печатает сообщение. Второй поток читает пользовательский ввод, когда он читает строку, начинающуюся с 'e' это устанавливает флаг, который заставляет основной поток выходить.

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

Если вы позвоните exitвсе потоки в вашей программе должны быть прерваны, если с вашей платформой что-то не так.

Примечание: этот пример требует libatomic-ops и предполагает, что вы используете потоки POSIX.

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <atomic_ops.h>
#include <errno.h>

static AO_t exit_flag;

#define fail(x) fail_func(x, __LINE__)

static void fail_func(int code, int line)
{
    fprintf(stderr, "line %d: failed with code %d\n", line, code);
    abort();
}

static void *input_thread(void *param)
{
    char buf[16], *p;
    (void) param;
    while (1) {
        p = fgets(buf, sizeof(buf), stdin);
        if (!p)
            break;
        if (buf[0] == 'e')
            break;
        else
            puts("unknown command");
    }
    AO_store(&exit_flag, 1);
    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_attr_t attr;
    pthread_t thread2;
    int r, c;
    struct timespec t;
    t.tv_sec = 1;
    t.tv_nsec = 0;
    r = pthread_attr_init(&attr);
    if (r) fail(r);
    // Note: making the thread detached is not actually necessary.
    r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    if (r) fail(r);
    r = pthread_create(&thread2, &attr, input_thread, NULL);
    if (r) fail(r);
    c = 5;
    while (1) {
        if (AO_load(&exit_flag)) {
            puts("thread 1 exiting (canceled)");
            exit(1);
        }
        printf("counter = %d\n", c);
        if (c == 0) {
            puts("thread 1 exiting (finished)");
            exit(0);
        }
        c--;
        r = nanosleep(&t, NULL);
        if (r) fail(errno);
    }
    return 0;
}

Пример вывода (пользовательский ввод выделен жирным шрифтом):

$ ./a.out counter = 5
counter = 4
counter = 3
counter = 2
counter = 1
counter = 0
выход 1 (закончен)
$ ./a.out counter = 5
counter = 4выход выход 1 (отменен)

Вы могли бы использовать atexit() запустить функцию очистки при выходе из основного процесса.

Однако процессы (если вы говорите о POSIX и классических процессах) не делятся данными со своими дочерними элементами, поэтому изменение переменной вашего флага не будет видно в родительском элементе.

Гораздо более чистым решением было бы либо просто использовать предоставляемое системой взаимодействие (а именно, пользователь нажимает Ctrl+C, что заставит tty отправлять SIGINT к руководителю сеанса (процесс переднего плана), который вы можете перехватить для чистого выхода) или проверить ввод пользователя с помощью select()/poll() в основном приложении.

Когда вы разветвляете дочерний процесс, родительский процесс получит идентификатор процесса дочернего процесса. Вы можете отправить SIGTERM сигнал ребенку через kill() Функция, если вы хотите, она прекращается по требованию. Не забудьте хотя позвонить wait() после отправки SIGTERM сигнал, чтобы предотвратить превращение дочернего процесса в процесс "зомби".

Если все, что вы хотите сделать, это получить строку ввода пользователя от дочернего процесса, вы можете просто использовать popen а также cat запустить дочерний процесс, который, используя catОтобразите все, что пользователь вводит в командной строке. Чтобы избежать блокировки чтения в поток, созданный из канала, вы можете получить дескриптор файла, связанный с потоком, используя fileno(), а затем использовать poll или же select со значением тайм-аута. Когда родительский объект сделан, или видит строку терминов, введенную пользователем, он просто вызывает pclose() и это прекратит cat процесс.

Другие вопросы по тегам