C 使用信号来停止子进程

发布于 2024-12-10 14:47:06 字数 1337 浏览 0 评论 0原文

我当前的程序正在创建子进程并给它们工作(CPU 密集型工作)。 main() 坐在那里等待子进程通过管道发送数据(使用 select)。

我想做的是,当程序正在处理数据时,我可以按 CTRL+C 来停止子进程工作,并询问用户是否要退出或恢复工作。

如果用户想退出,程序将杀死所有进程。如果用户想恢复工作,它会告诉子进程恢复计算。

我已经有了代码,但它不太正常工作。

在 main 中,我有 signal(SIGINT, pausar); 来处理 SIGINT (CTRL+C)。

这是 pausar() 函数:

void pausar(int signum){
    signal(SIGINT, pausar);

    int i;
    // pid[] contains all the child processes
    for(i = 0; i<CORES; i++)
    {
        kill(pid[i], SIGSTOP);
    }

    char option[2];
    printf("\n Computacao pausada.\n'S' para sair ou 'C' para continuar: ");

    scanf("%1s", option);
    if (option[0] == 's' || option[0] == 'S') {
        printf("A desligar...\n");

        //if user wants to quit, kill all the child processes
        for(i = 0; i<CORES; i++)
        {
            kill(pid[i], SIGKILL);
        }

        exit(0);
    }
    else
    {
        printf("[%d] A resumir computacao...\n",getpid());
        kill(getpid(), SIGCONT);

        //if user wants to resume work, send signal to continue
        for(i = 0; i<CORES; i++)
        {
            kill(pid[i], SIGCONT);
            printf("%d resumiu\n", pid[i]);
        }
    }
}

问题是有时我按 CTRL+C 并且控制台中没有任何显示(但进程停止,因为我正在关注进程管理器)。另一个问题是,在我输入“C”恢复工作后,我在 select() 中收到错误,并且孩子们永远不会恢复工作。

My current program is creating child processes and giving them work (CPU intensive work). The main() sits there and waits for the child processes to send data via pipes (using select).

What I wanted to do is when the program is processing data I could press CTRL+C to stop the child processes from working and asking the user if he wants to quit or resume work.

If user wants to quit, the program would kill all the processes. If user wants to resume work, it would tell the child processes to resume the computation.

I already have the code in place but it's not quite working right.

In main I have signal(SIGINT, pausar); to handle SIGINT (CTRL+C).

This is the pausar() function:

void pausar(int signum){
    signal(SIGINT, pausar);

    int i;
    // pid[] contains all the child processes
    for(i = 0; i<CORES; i++)
    {
        kill(pid[i], SIGSTOP);
    }

    char option[2];
    printf("\n Computacao pausada.\n'S' para sair ou 'C' para continuar: ");

    scanf("%1s", option);
    if (option[0] == 's' || option[0] == 'S') {
        printf("A desligar...\n");

        //if user wants to quit, kill all the child processes
        for(i = 0; i<CORES; i++)
        {
            kill(pid[i], SIGKILL);
        }

        exit(0);
    }
    else
    {
        printf("[%d] A resumir computacao...\n",getpid());
        kill(getpid(), SIGCONT);

        //if user wants to resume work, send signal to continue
        for(i = 0; i<CORES; i++)
        {
            kill(pid[i], SIGCONT);
            printf("%d resumiu\n", pid[i]);
        }
    }
}

The problem is that sometimes I press CTRL+C and nothing shows in the console (but the processes STOP because I'm paying attention to the process manager). The other problem is that after I enter 'C' to resume work, I get errors in select() and the children never resume work.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

心病无药医 2024-12-17 14:47:06

同时使用 select() 和信号处理程序很容易出现竞争条件 - 在 select() 调用期间可能会出现信号,但也可能在每行中出现代码。

如果您使用的是 Linux:使用 signalfd()并将此套接字添加到传递给 select() 的读取集中。然后,信号将在代码中的固定点进行处理,您无需担心竞争条件。

Using select() and signal-handler at the same time is prone to race conditions - a signal could occur during the select() call, but also in every other line of code.

If your are on linux: create an event socket with signalfd() and add this socket to the read set passed to select(). Signals are then handled at a fixed point in your code and you do not need to worry about race conditions.

裸钻 2024-12-17 14:47:06

首先,对于您想要做的事情,您的信号处理程序太复杂了。其次,在信号处理程序中调用 signal() 不是一个好主意......它不是 异步信号安全函数

您可以执行以下操作:

  1. 在 main 中,像您所做的那样使用 signal() 设置信号处理函数。
  2. 通过 sigprocmask() 阻止 SIGINT 信号。这可以防止虚假信号在调用 pselect() 之前到达。
  3. 在信号处理程序中设置一个简单的全局标志,即 sig_atomic_t
  4. 使用 pselect() 而不是 select()。这将允许您更改进程信号掩码以允许 SIGINT 信号到达,并且它将以相对于信号的原子方式执行此操作。否则,您的 SIGINT 可能会在调用 select() 之前到达,然后您就“丢失”了该信号,即使它确实在处理程序中设置了标志。
  5. pselect()调用返回时,检测该标志是否已设置。
  6. 如果设置了全局 sig_atomic_t 标志,并且由于捕获信号而从 pselect 返回,则启动另一个函数,该函数实际上将完成所有子进程的结束,并且提示用户等。

执行这些步骤将简化信号处理代码,并减少由于信号到达的异步性质而出现竞争条件或其他意外结果的可能性。

如果您想了解有关 pselect() 的更多信息,这里有一篇好文章就在这里

First, for what you're trying to-do, your signal handler is way too complex. Secondly, calling signal() inside your signal handler is not a good idea ... it's not an asynchronous signal-safe function.

What you can do is the following:

  1. In your main, set the signal handler function using signal() like you've done.
  2. Block the SIGINT signal via sigprocmask(). This prevents a spurious signal from arriving before the call to pselect().
  3. Inside your signal handler only set a simple global flag that is a sig_atomic_t
  4. Use pselect() instead of select(). This will allow you to change the process signal mask to allow a SIGINT signal to arrive, and it will do-so in an atomic manner with respect to signals. Otherwise, you could have your SIGINT arrive before the call to select(), and then you have "lost" that signal, even though it does set the flag in the handler.
  5. When the pselect() call returns, detect whether the flag has been set.
  6. If the global sig_atomic_t flag was set, and you returned from pselect because of a caught signal, then launch another function that will actually do all the ending of the child-processes and prompt the user, etc.

Doing these steps will simplify your signal-handling code and reduce the chances of race-conditions or other unexpected results because of the asynchronous nature of signal arrival.

If you'd like some more information on pselect(), you there is a nice article on that here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文