【问题标题】:How to wait for a signal for a certain time only, in C language?如何在 C 语言中只等待一段时间的信号?
【发布时间】:2020-06-09 13:00:31
【问题描述】:

我正在尝试使用 C 编程语言中的信号在 2 个进程(linux 中的父进程和子进程)之间进行通信。

第一个过程进行一些计算并提供数据。然后,它向处于挂起状态的第二个进程发送一个信号,等待信号唤醒并使用共享内存收集第一个进程共享的数据。

如何让第二个进程等待一定时间或者说一段时间?

在此期间,如果第一个进程提供数据并向第二个进程发送信号,则一切正常。否则,如果在这段时间内没有收到第一个的任何信号,它会做另一件事。

我怎样才能让第二个流程响应这种需求?

我应该使用哪些算法和信号来实现这个?

【问题讨论】:

  • 没有目标平台的任何信息,这个问题无法得到满意的回答
  • sigtimedwait() 是一种可能的方法。
  • 使用select 来执行此操作。
  • 选择没有文件描述符等待超时或信号stackoverflow.com/a/52310127/1216776
  • 你为什么要为此使用信号?这是不必要的复杂。 select 有一个超时选项。最简单的方法是通过管道而不是共享内存发送数据。如果您确实想使用共享内存,只需通过管道发送一个字节而不是发送信号。如果您真的想使用信号,请使用signalfd 之类的东西。在任何一种情况下,仍然使用select 超时。

标签: c signals fork parent-child concurrent-processing


【解决方案1】:

POSIX 为您的目的定义了一个函数:sigtimedwait()。它将暂停调用进程的执行,直到指定集合中的任一个信号变为未决,或指定的超时到期。它的返回值指示发生了哪些事件。

在此期间,如果第一个进程提供数据并向第二个进程发送信号,则一切正常。否则,如果在这段时间内没有收到第一个的任何信号,它会做另一件事。

我怎样才能让第二个流程响应这种需求?

第二个过程为sigtimedwait 设置参数。然后它会阻塞预期的信号(通过sigprocmask),以便在接收到该信号时不会发生默认处置,然后调用sigtimedwait。后者的返回值指示是否接收到指定的信号之一(如果是,是哪个),或者函数是否由于其他原因返回 - 通常是因为超时到期。在后一种情况下,可以检查errno 变量以确定它是否确实是超时。上面链接的手册页提供了一些额外的使用说明。

具体来说,您需要两个主要参数:

  • a sigset_t 定义要阻塞和等待的信号。手册页末尾的“另请参阅”部分为您提供相关和类似功能的文档,包括 sigsetops(3),它告诉您如何操作此类对象。例如,

    #include <signal.h>
    
    // ...
    
    sigset_t sigs;
    sigemptyset(&sigs);
    sigaddset(&sigs, SIGUSER2);
    
  • struct timespec,其定义在手册页中提供。这个定义应该足以让你想出类似的东西

    struct timespec timeout = { .tv_sec = 10 };
    

根据文档,您可以将 sigtimedwait 的第二个参数指定为 NULL,如果您不需要可以通过这种方式传回给您的信息,而您不需要。

您可以使用上面已经准备好的信号集来设置信号阻塞,例如:

sigprocmask(SIG_BLOCK, &sigs, NULL);

这似乎有点违反直觉,因为您确实想接收信号,但其效果是阻止已为信号建立的任何处置发生,这与通过sigtimedwait 处理它是不同的路径。

此时,您已准备好拨打sigtimedwait()

int sig_num = sigtimedwait(&sigs, NULL, &timeout);

如果在指定时间内收到指定信号之一(本例中仅SIGUSER2),则结果将是一个信号编号,否则为-1。因此,

if (sig_num == SIGUSER2) {
    // I received the signal
    // do something ...
} else {
    // assert(sig_num == -1);
    // I did not receive the signal within the alotted time
    // do something else ...
}

您可以选择假设在未收到信号的情况下发生超时,但最可靠的代码会检查 errno 以验证:

    if (errno == EAGAIN) {
        // it was a timeout
    } else if (errno = EINTR) {
        // a signal outside the set was received
        // ... might want to calculate how much wait time is left and try again
        //     (left as an exercise)
    } else {
        // some other error occurred
        // ... aborting with an error might be best:
        perror("signal wait failed unexpectedly");
        exit(1);
    }

【讨论】:

  • 我尝试按照手册页上的说明进行操作,但我很难按照说明进行操作,我需要的只是在 10 秒内第二次等待信号 SIGUSER2。我不知道如何实现。
  • @Gismail,阅读我链接的手册页等文档是一项重要技能,如果您想成为一名高效的程序员,您绝对需要学习。但一开始可能有点令人生畏,特别是如果你一开始就不精通这门语言。我添加了大量的评论和示例代码,希望对这种情况有所帮助。
  • 我还要提一下,在调用sigtimedwait 之前应该阻止等待的信号。这可能是您在手册页中引用“一些额外的使用说明”的意思,但我建议它足够重要,可以在这里调用。
  • 很公平,@pilcrow,我已经扩展了我的 cmets 和示例代码,以明确解决所需的信号阻塞问题。
【解决方案2】:

呼叫睡眠直到 10 秒或信号将是:

struct timeval t = {10, 0};
int rc = select(0, NULL, NULL, NULL, &t);
if (rc == 0) {
    // timeout
} else if (errno == EINTR) {
    // signal
} else {
    // some error

【讨论】:

  • 我得到了这个:错误:'{' token int rc = select(0, NULL, NULL, NULL, {5, 0}); 之前的预期表达式;
  • 你修复警告了吗?添加#include &lt;sys/time.h&gt;#include &lt;sys/select.h&gt;
  • 如果信号在调用select之前到达会发生什么? OP 是否可以接受丢失信号和完全超时?
  • @stark,即使包含 2 个库,我仍然遇到同样的问题
  • @pilcrow,我真的没明白说了什么,对不起。
【解决方案3】:

您可以将代码的第一个进程包含在无限 while 循环中(例如 -;while(1){}),并且一旦第一个进程计算的数据使用函数将数据发送到第二个进程。在第二个进程中使用 sleep() 函数。例如;


main()
{
   printf("Sleep for 10 milisecond to exit.\n");
   sleep(0.10);
   return ;
}
you may change the time.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-09
    • 1970-01-01
    • 1970-01-01
    • 2021-05-08
    相关资源
    最近更新 更多