【问题标题】:How to resume stopped process with a user defined signal handler and not with SIGCONT, in C?如何在 C 中使用用户定义的信号处理程序而不是使用 SIGCONT 来恢复停止的进程?
【发布时间】:2021-09-09 05:58:11
【问题描述】:

我正在尝试使用 2 个进程和 2 个用户定义的信号处理程序来玩“乒乓”。

问题是由于某种原因,在我raise(SIGSTOP)子进程为了让父进程启动之后,我无法继续运行子进程,即使我发送了一个kill(getppid(), SIGUSR1 ,在我看来,应该重新运行该过程。

这里有什么问题?

void handle_siguser1(int signal_id);

void handle_siguser2(int signal_id);


enum {CHILD = 0, PARENT = 1};

/************************* Functions  Implementations *************************/
int main()
{
    /*  create a child process  */
    pid_t pid = fork();
    
    while (1)
    {
        /*  returned to the newly created child process. */
        if (CHILD == pid)
        {
            printf("Child registers its signal handler\n");
            signal(SIGUSR1, handle_siguser1);
            
            printf("Child stops to return the control to the parent\n");
            raise(SIGSTOP);
        }
        
        /* returned to parent */
        else
        {
            printf("Parent process started running\n");
            
            printf("Parent stops to let child to register signal\n");
            sleep(1);
            
            printf("Parent registers its signal handler\n");
            signal(SIGUSR2, handle_siguser2);
            
            printf("Parent waits child to be stopped\n");
            waitpid(getpid(), NULL, WUNTRACED);
            
            printf("Parent starts child process\n");
            kill(getpid(), SIGUSR1);
        }
    }
        
    return 0;   
}
/******************************************************************************/
void handle_siguser1(int signum)
{
    printf("PONG\n");
    sleep(2);
    kill(getppid(), SIGUSR2);
}
/******************************************************************************/
void handle_siguser2(int signum)
{
    printf("PING\n");
    sleep(2);
    kill(getpid(), SIGCONT);
}
/******************************************************************************/

这段代码的输出是:

Parent process started running
Parent stops to let child to register signal
Child registers its signal handler
Child stops to return the control to the parent
Parent registers its signal handler
Parent waits child to be stopped
Parent starts child process
User defined signal 1

谢谢。

【问题讨论】:

  • 您只能使用 SIGCONT 信号重新启动已停止的进程。
  • 这正是我想要做的。子进程确实被raise(sigstop) 停止了
  • @NoobCoder 你如何协调“这正是我想要做的”与标题中的短语:“而不是 SIGCONT”?两者似乎是矛盾的。

标签: c process signals fork system-calls


【解决方案1】:

你有各种各样的问题。忽略you should not use printf() in a signal handler 的问题,事实证明使用 SIGUSR1 和 SIGUSR2 是不必要且令人困惑的,尤其是因为错误的进程被发出信号。使用所示的信号处理,您需要将pid 变量设为全局变量,并向其发送信号,而不是getpid()getppid()(尤其是getppid() 将返回启动程序的进程的PID)。

事实上,SIGUSR1 和 SIGUSR2 是无关紧要的。这是一些工作代码。它使用我在 GitHub 上的 SOQ(堆栈溢出问题)存储库中提供的一些代码,作为 src/libsoq 子目录中的文件 stderr.cstderr.h。我使用了err_setlogopts(),以便通过err_remark()err_sysrem() 写入消息的进程识别PID 以及调用时的毫秒时间。这非常有帮助。它向我展示的一件事是从未调用过信号处理程序。

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include "stderr.h"

enum {CHILD = 0, PARENT = 1};

static pid_t pid;

int main(void)
{
    err_setarg0("sigcont43");
    err_setlogopts(ERR_PID|ERR_MILLI);

    pid = fork();

    for (int i = 0; i < 3; i++)
    {
        if (CHILD == pid)
        {
            err_remark("Child stops to return the control to the parent\n");
            err_remark("Child about to invoke raise(SIGSTOP)\n");
            raise(SIGSTOP);
            err_remark("Child back from raise(SIGSTOP)\n");
        }
        else
        {
            err_remark("Parent process started running\n");
            err_remark("Parent stops to let child to register signal\n");
            sleep(1);
            err_remark("Parent returns from sleep(1)\n");

            err_remark("Parent waits child to be stopped\n");
            int status = 0;
            int corpse = waitpid(pid, &status, WUNTRACED);

            err_remark("Parent collects stopped child: corpse = %d, status = 0x%.4X\n", corpse, status);
            err_remark("Parent starts child process\n");
            err_remark("Parent sends SIGCONT to PID %d\n", pid);

            errno = 0;
            int rc = kill(pid, SIGCONT);
            err_sysrem("Parent sent SIGCONT to PID %d (rc = %d): ", pid, rc);
        }
    }

    return 0;
}

代码保留了与信号处理程序无关的原始消息(因为不再有任何信号处理程序),但添加了更多信息性消息。它还捕获和报告许多系统函数的返回值。这有助于诊断一些问题。我还将无限 while 循环更改为 3 次迭代 for 循环。

示例输出(测试程序名称sigcont43):

$ sigcont43
sigcont43: 2021-06-28 11:40:03.305 - pid=83078: Parent process started running
sigcont43: 2021-06-28 11:40:03.306 - pid=83078: Parent stops to let child to register signal
sigcont43: 2021-06-28 11:40:03.305 - pid=83079: Child stops to return the control to the parent
sigcont43: 2021-06-28 11:40:03.306 - pid=83079: Child about to invoke raise(SIGSTOP)
sigcont43: 2021-06-28 11:40:04.306 - pid=83078: Parent returns from sleep(1)
sigcont43: 2021-06-28 11:40:04.306 - pid=83078: Parent waits child to be stopped
sigcont43: 2021-06-28 11:40:04.306 - pid=83078: Parent collects stopped child: corpse = 83079, status = 0x117F
sigcont43: 2021-06-28 11:40:04.306 - pid=83078: Parent starts child process
sigcont43: 2021-06-28 11:40:04.306 - pid=83078: Parent sends SIGCONT to PID 83079
sigcont43: 2021-06-28 11:40:04.307 - pid=83079: Child back from raise(SIGSTOP)
sigcont43: 2021-06-28 11:40:04.307 - pid=83079: Child stops to return the control to the parent
sigcont43: 2021-06-28 11:40:04.307 - pid=83078: Parent sent SIGCONT to PID 83079 (rc = 0): error (0) Undefined error: 0
sigcont43: 2021-06-28 11:40:04.307 - pid=83078: Parent process started running
sigcont43: 2021-06-28 11:40:04.307 - pid=83078: Parent stops to let child to register signal
sigcont43: 2021-06-28 11:40:04.307 - pid=83079: Child about to invoke raise(SIGSTOP)
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent returns from sleep(1)
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent waits child to be stopped
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent collects stopped child: corpse = 83079, status = 0x117F
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent starts child process
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent sends SIGCONT to PID 83079
sigcont43: 2021-06-28 11:40:05.309 - pid=83079: Child back from raise(SIGSTOP)
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent sent SIGCONT to PID 83079 (rc = 0): error (0) Undefined error: 0
sigcont43: 2021-06-28 11:40:05.309 - pid=83079: Child stops to return the control to the parent
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent process started running
sigcont43: 2021-06-28 11:40:05.309 - pid=83079: Child about to invoke raise(SIGSTOP)
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent stops to let child to register signal
sigcont43: 2021-06-28 11:40:06.309 - pid=83078: Parent returns from sleep(1)
sigcont43: 2021-06-28 11:40:06.309 - pid=83078: Parent waits child to be stopped
sigcont43: 2021-06-28 11:40:06.309 - pid=83078: Parent collects stopped child: corpse = 83079, status = 0x117F
sigcont43: 2021-06-28 11:40:06.309 - pid=83078: Parent starts child process
sigcont43: 2021-06-28 11:40:06.309 - pid=83078: Parent sends SIGCONT to PID 83079
sigcont43: 2021-06-28 11:40:06.309 - pid=83078: Parent sent SIGCONT to PID 83079 (rc = 0): error (0) Undefined error: 0
sigcont43: 2021-06-28 11:40:06.309 - pid=83079: Child back from raise(SIGSTOP)
$

【讨论】:

  • 嘿,谢谢。我能以某种方式避免err_remark("Parent stops to let child to register signal\n");吗? sleep?因为我们不能确定(如果我错了,请纠正我)X 进程将在 Y 进程执行sleep 之后获得控制权。我的意思是,是的,99.9% 的时间都会发生这种情况,但我们不能确定这一点。如果....系统中有 Z 个进程在当前时间运行。你怎么能 100% 确定子进程是在其父进程进入睡眠状态后立即获得控制权的进程?
  • 你为什么不用pause
  • 我没有使用pause(),因为您的代码没有使用pause()。您可以消除对err_remark() 函数的任何或所有调用,尽管您可能需要一些来跟踪正在发生的活动。直到处理的很晚,我让您的printf() 调用保持不变并添加了err_remark() 调用,但我最终将所有printf() 调用映射到err_remark() 以获得输出上的统一程序/时间/PID 前缀线。您可以轻松推断出应该将哪些调用转换回printf() 以匹配您的原始代码,并且可以删除其他调用。
  • 可以围绕此处运行的一对进程的活动安排各种其他进程。这些过程不会严重影响这些过程。在孩子停止自己之前,父母不会从waitpid() 醒来;在父母向孩子发送 SIGCONT 之前,孩子不会醒来。这两个进程之间的同步是确定性的(除了其他进程可能导致的延迟)。您可以在末尾添加一个wait() 循环(在for 循环之后)以在父级退出之前等待子级。
猜你喜欢
  • 1970-01-01
  • 2020-06-26
  • 2018-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多