当父进程在C中退出时,终止子进程

时间:2012-02-11 00:51:57

标签: c multithreading fork parent-child

我正在尝试创建一个执行某些任务并退出的程序。但是,如果用户在终端上输入一些字符串,我想退出程序。

我想解决的方法: 创建执行主任务的父进程。主要任务有一个循环。 创建一个子进程,它等待用户的输入。如果用户输入退出字符串,则子进程将更新变量并自行终止。父进程中的循环将检查变量的值,如果子进程更改了该值,它也将退出。如果用户未输入退出字符串,则父进程将完成任务并自行终止。此刻我也想杀死孩子的过程。 我怎么能这样做?

由于

3 个答案:

答案 0 :(得分:3)

当主进程退出时,您可以使用atexit()运行清理功能。

但是,进程(假设您正在讨论POSIX和经典进程)不与子进程共享数据,因此您的标志变量的更改将不会在父进程中显示。

更简洁的解决方案是使用系统提供的交互(即用户按Ctrl + C,这将导致tty向会话负责人(前台进程)发送SIGINT,您可以陷阱以便干净地退出)或使用主应用程序中的select()/poll()检查用户输入。

答案 1 :(得分:2)

感谢您说出您在这里想要实现的目标。

您可能会发现创建单个多线程进程要容易得多,而不是创建两个单线程进程。

  1. 线程#1在循环中工作并定期检查变量exit_flag。如果设置了变量,则调用exit。如果它完成了工作,则会调用exit

  2. 线程#2等待来自用户的输入。如果获得输入,则设置变量exit_flag

  3. 这样,当你调用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_releaseAO_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
    thread 1 exiting (finished)
    $ ./a.out
    counter = 5
    counter = 4
    exit
    thread 1 exiting (canceled)
    

答案 2 :(得分:2)

当您分叉子进程时,父进程将接收该进程的进程ID。如果您希望按需终止,可以通过SIGTERM功能向孩子发送kill()信号。确保在发送wait()信号后致电SIGTERM,以防止子进程成为&#34; zombie&#34;过程

如果要做的只是从子进程获取用户输入的字符串,您可以使用popencat启动子进程,使用{ {1}},回显用户在命令行上输入的内容。为了避免阻塞读取到从管道创建的流,您可以使用cat获取与流关联的文件描述符,然后使用fileno()poll超时值。完成父项或查看用户输入的终止字符串后,它只需调用select即可终止pclose()进程。