【问题标题】:how to forward a signal to child process from parent process如何将信号从父进程转发给子进程
【发布时间】:2020-04-21 23:20:03
【问题描述】:

我正在实现我自己的 shell,它要求按 CTRL-Z 来暂停前台进程。为了不暂停我的主进程而只暂停子进程,我必须通过主进程中的处理程序捕获 SIGTSTP 并将其重定向到我的子进程(需要停止)。但是 kill 函数永远不会返回。

#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/signal.h>
#include <sys/mman.h>



void handler(){
  pid_t pid;
  int status;
  while((pid = waitpid(-1, &status, 0))>0){
    printf("pid: %d has been reaped\n", pid);
  }
}
  void send_signal(int signum){
     kill(pid, signum);
  }

  void init_signals(){
   //signal(SIGINT, send_signal);
    signal(SIGCHLD, handler);
    signal(SIGTSTP, send_signal);
  }


  void start_foreground_job(char** argv)
  {   
    if( (fork()) ==0)
    {   
        signal(SIGTSTP, SIG_DFL);

        if((execve(argv[0], argv, NULL))<0)//envp
        {
            fprintf(stderr, "%s: Command not found.\n", argv[0]);
            exit(0);
         }
     }

     pause();

     return;
 }

  int main(){
    init_signals();
    char* argv[] ={"/bin/sleep", "10", NULL};

    start_foreground_job(argv);

     printf("This line is what I expected after pressing CTRL-Z\n");

    return 0;
  }

【问题讨论】:

  • printf 不是异步信号安全的,因此您不能从信号处理程序中调用它。
  • 您应该显示您为signal/sigaction 所做的代码。由于您必须在 calling 程序的父进程 before 中执行一项操作,因此在执行 execvp 之前的fork [in the child] 之后,您可能想要将信号重置为其默认操作(例如)signal(SIGTSTP,SIG_DFL);
  • @Craig Estey 按照你说的方式还是不行,你能看看我的代码吗?

标签: c linux


【解决方案1】:

当我在我的环境中尝试您的代码时,结果与您相同。 Ctrl+Z后查看子进程的状态,

在 Ctrl + Z 之前

ps -aux | grep sleep
16586  0.0  0.0   2016   412 pts/1    S+   10:42   0:00 /bin/sleep 10

Ctrl+Z后

ps -aux | grep sleep
16586  0.0  0.0   2016   412 pts/1    T+   10:42   0:00 /bin/sleep 10

子进程的状态已经从S -> T. 表示父进程已成功发送信号给子进程。

但是子进程没有退出,所以你的waitpid(-1, &status, 0) 不能返回。

选项1: 向子进程发送kill信号,waitpid会得到一个返回。

void send_signal(int signum)
{
    if (pid)
    {
        printf("kill %d, signum %d\n", pid, signum);
        //kill(pid, signum);
        kill(pid, SIGKILL );
    }
}

选项2: waitpid api 有一个选项可以等待停止状态:

void handler()
{
    pid_t pid;
    int status;
    while ((pid = waitpid(-1, &status, WSTOPPED)) > 0) {
        printf("pid: %d has been reaped\n", pid);
        break;
    }
}

请检查以下 sw 版本,它适用于我的工作站。

int pid = 0;
void handler()
{
    pid_t pid;
    int status;
    while ((pid = waitpid(-1, &status, WSTOPPED)) > 0) {
        printf("pid: %d has been reaped\n", pid);
        break;
    }
}
void send_signal(int signum)
{
    if (pid)
    {
        printf("kill %d, signum %d\n", pid, signum);
        kill(pid, signum);
        //kill(pid, SIGKILL );
    }
}

void init_signals()
{
    signal(SIGCHLD, handler);
    signal(SIGTSTP, send_signal);
}


void start_foreground_job(char** argv)
{
    pid = fork();
    if (pid == 0) {
        signal(SIGTSTP, SIG_DFL);

        if ((execve(argv[0], argv, NULL)) < 0) { //envp
            fprintf(stderr, "%s: Command not found.\n", argv[0]);
            exit(0);
        }
    }
    printf("son process pid is %d\n", pid);
    pause();

    return;
}

int main()
{
    init_signals();
    char* argv[] = {"/bin/sleep", "10", NULL};

    start_foreground_job(argv);

    printf("This line is what I expected after pressing CTRL-Z\n");

    return 0;
}

以下是我的测试结果:

./temp
son process pid is 29249
^Zkill 29249, signum 20
pid: 29249 has been reaped
This line is what I expected after pressing CTRL-Z

【讨论】:

  • 谢谢!我在我的代码中将 WUNTRACED 替换为 WSTOPPED,它对我有用。这两个选项有什么区别?
  • 我在我的机器上运行了你的代码,但似乎还有一点不同,这是我的输出
  • 子进程 pid 是 47780 ^Zkill 47780, signum 18 这行是我按 CTRL-Z 后的预期
  • 我检查了你的原始源代码,你调用“waitpid(-1, &status, 0)”。在我的 Centos 工作站中,WUNTRACED 和 WSTOPPED 都可以正常工作。在 linux 内核 4.4 中,'#define WSTOPPED WUNTRACED',我认为两者都应该没问题。如果 WUNTRACED 在您的工作站上不正常,我建议为此重新编辑您的问题。我可以再检查一下。
  • printf 应该在信号句柄中。我提供给你的只是一个临时版本。我认为差异可能来自 gcc 构建系统。对于这种情况,有两个关键点: 1. 父进程可以向子进程发送信号。 2.父进程可以查看子进程的状态变化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-28
  • 1970-01-01
  • 1970-01-01
  • 2019-04-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多