如何仅在 C 中将 SIGINT 发送到后台进程

Posted

技术标签:

【中文标题】如何仅在 C 中将 SIGINT 发送到后台进程【英文标题】:How to send SIGINT to background processes only in C 【发布时间】:2015-03-02 17:20:05 【问题描述】:

我正在编写一个简单的 shell 程序,我可以在其中执行一些后台进程。我希望使用像“killbg”这样的特定命令能够对我执行的每个后台进程进行 SIGINT。我试过了:

    使用setpgid(0,0)创建一个仅由后台进程组成的进程组 在 pid=fork() 为 0 的子句中,即当我要执行子进程时。 setpgid(0,0) 语句后得到的进程组 'pgid',我在 kill 函数中使用,例如 kill(pgid, 2)

不幸的是,整个程序被中断,后台任务仍在运行。

以下是相关代码:

while(1)
  fgets.. //user input for command

  // Kill background processes      
        if(strcmp(execArgs[0], "killbg") == 0)    //execArgs argument array for execve
            kill(pgid, 2); 
            perror("");
        
        pid=fork();

        // Error in fork
        if(pid <= -1)
            perror("Error with fork.\n");
         exit(EXIT_FAILURE);
               
        // Child process
        else if(pid == 0)
            if(execBG == TRUE)    // execBG true If user wants to run command in background
                setpgid(0,0);
                pgid = getpgid(pid);
            
            execve(execArgs[0], execArgs, NULL); 
            fprintf(stderr, "Not a valid command! Remember the full path.\n");  

        
        // Parent process       
        else
            if(execBG==FALSE)
                waitpid(-1, &status, NULL);
            
            else
                ;    // if background process, don't wait for the child
            
                       
      

我做错了什么?

【问题讨论】:

你在子进程中设置pgid,在父进程中对pgid执行kill(),父进程不知道子进程为pgid设置了什么。 【参考方案1】:

只是猜测... 1)您是否确保您的主要流程是不同组的一部分? 2)一旦您的孩子因 stdin/stdout/stderr 连接而死亡,您的父母可能会收到 SIGPIPE。 SIGPIPE 的默认信号动作是终止(参见 signal(7))。

【讨论】:

不,你不明白。父母被我的杀戮功能打断了。我是否确定它是否是不同的组?这正是我的问题的一部分。我不信,问怎么做。 @ot0:如果想确保不被视为粗鲁,可以用“我似乎表达得不够好”而不是“你不明白”。【参考方案2】:

查看 POSIX kill() 函数的规范,了解如何向进程组发送信号:

如果pid大于0,sig将被发送到进程ID等于pid的进程。

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

如果pid 为-1,则sig 应发送到所有进程(不包括未指定的系统进程集),该进程有权发送该信号。

如果pid 为负数,但不是-1,则sig 应发送给进程组ID 等于的所有进程(不包括未指定的系统进程集) pid 的绝对值,进程有权发送信号。

因此,要将SIGINT 发送到进程组,您需要调用kill(-pgrp, SIGINT)。您还应该始终检查系统调用的返回值,以便知道它何时失败。

我观察到如果execve() 失败(也就是返回),您会打印一条错误消息——这很好!此时退出程序通常也是正确的。否则,您往往会有一个额外的过程来尝试读取您的终端输入,这很容易造成混乱和混乱。

注意kill(pgrp, 2); perror(""); 不好,因为errno 可以在调用kill() 之前设置为非零值,或者通过kill() 设置,即使它不报告失败。您必须测试系统调用的返回值以了解errno 是否相关,并且您应该仅在相关时调用perror()。 (假设你应该调用perror();IMO,这是错误报告功能的一个相当蹩脚的借口,但如果使用得当,总比没有好。)

您的waitpid() 电话不安全。第三个参数是一个整数,而不是一个指针。此外,您可以在后台运行多个子进程;该代码只等待一个退出 - 任何子进程。您几乎可以肯定那里需要一个循环,并且可能也需要 WNOHANG。

【讨论】:

感谢您的评论。我正在尝试理解所有这些概念,以及在另一个 post 上阅读您的答案。我认为在 kill 函数之前我需要了解将子进程和父进程分成不同组的概念。

以上是关于如何仅在 C 中将 SIGINT 发送到后台进程的主要内容,如果未能解决你的问题,请参考以下文章

Nodejs:将 Ctrl+C 发送到 Windows 上的子进程

Python 在 Windows 上发送 SIGINT 信号子进程

linux信号 SIGINT SIGTERM SIGKILL

在 Windows 上将 ^C 发送到 Python 子进程对象

仅在 iPhone 后台工作的应用程序

如何在 Swift 中将任务发送到后台队列?