Убить дочерний процесс при выходе родительского процесса в C - PullRequest
1 голос
/ 11 февраля 2012

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

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

Спасибо

Ответы [ 3 ]

3 голосов
/ 11 февраля 2012

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

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

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

2 голосов
/ 11 февраля 2012

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

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

2 голосов
/ 11 февраля 2012

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

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

  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)) {
    ...
}

Предупреждение о приведенном выше коде: Установка глобального флага работает только таким образом, поскольку основной поток only читает глобальный флаг и ничего больше. Если вы для передачи более одного слова между потоками вам понадобится какой-то механизм синхронизации, например мьютекс, семафор или 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;
}

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

$ <b>./a.out</b>
counter = 5
counter = 4
counter = 3
counter = 2
counter = 1
counter = 0
thread 1 exiting (finished)
$ <b>./a.out</b>
counter = 5
counter = 4
<b>exit</b>
thread 1 exiting (canceled)
...