当使用execv()执行进程时,如何杀死进程及其所有子进程?

3
我正在尝试在基于Unix的操作系统上实现类似timeout命令的功能,代码如下:

int                  pid;
timer_t              timer_id;
struct sigevent      timer_event;
struct itimerspec    timer_value;

void timeout_signal_handler(int sig_no)
{
    kill(pid, SIGKILL);
}

int create_timer()                        { /* implementation */ }
int start_timer_oneshot(int interval_ms)  { /* implementation */ }

int main(int argc, char* argv[])
{

    int   status, pid_return;
    void  *signal_return;

    if (argc < 2)
        return EXIT_FAILURE;

    signal_return = signal(SIGUSR1, timeout_signal_handler);
    if (signal_return == SIG_ERR)
        return EXIT_FAILURE;

    create_timer();
    start_timer_oneshot(TIMEOUT);

    if ((pid = fork()) == 0)
    {
        execv(argv[1], &argv[1]);
        return EXIT_FAILURE;
    }
    else
    {
        status = -1;
        while (status == -1)
            status = wait(&pid_return);
    }

    return EXIT_SUCCESS;

}

我使用此实用工具的方法如下:

./timeout example
example程序运行了几秒钟并分叉出几个进程。当计时器在timeout时到期,只有example的父进程被杀死,而其子进程继续在控制台上打印。
当我在没有timeout的情况下运行example并按下Ctrl+C时,父进程和所有子进程都能够成功被杀死。
请问有人可以告诉我如何在我的timeout程序中解决这个问题吗?

1
向进程发送终止信号不会对子进程产生任何影响。您可以使用进程组来杀死所有子进程。尝试查看 setsid - kuroi neko
谢谢你的提示。如果我在fork出来的进程中调用setsid,然后再执行execv,那么当定时器到期时,父进程如何向新的进程组或者fork出来的进程发送信号呢? - moorara
"pid" 是在 #include <unistd.h> 中定义的 "pid_t"。 - user3629249
1个回答

5
你想在进程id为0上调用kill()。这会向调用进程的进程组中的所有成员发送信号。
但是,仅当通过fork()/exec*()(或其子级)创建的进程没有自行更改其(它们的)进程组时,才能实现此目标。
来自man 2 kill

如果pid为0,则将sig发送到进程组ID等于发送者的进程组ID的所有进程(不包括一组未指定的系统进程),并且该进程有权限发送信号。


2
请注意,它还会发出超时进程的信号。子进程可以将自己变成一个进程组长,然后让父进程通过setpgid()向该进程组发送信号。父进程知道这是哪个进程组(它是与子进程PID相同的进程组ID),因此它可以向子进程的进程组发送负PID信号以指示向子进程的进程组发送信号。 - Jonathan Leffler
ж„ҹи°ўжҫ„жё…гҖӮй—®йўҳд»Қ然жҳҜkill(0, SIGKILL);е°ҶдјҡжқҖжӯ»timeoutзҡ„зҲ¶иҝӣзЁӢпјҢеӣ жӯӨжҲ‘ж— жі•д»Һstatus = wait(&pid_return);дёӯиҝ”еӣһгҖӮ - moorara
@moorara: Jonathan Leffer 在他上面的评论中提到了解决此问题的方法 (https://dev59.com/ro3da4cB1Zd3GeqP2Jmk#oLvmnYgBc1ULPQZFi3Tr)。顺便说一句,阅读相关函数的文档会有所帮助... - alk
请注意,即使使用了Jonathan的解决方案,如果任何一个分叉/执行的进程调用setpgid()创建一个新组或setsid()创建一个新会话,它将不再属于同一组,因此也不会被杀死。 - Filipe Gonçalves

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接