0

我有以下情况

void foo() {
   printf("hi\n");  
   while(1);

}


int main(void)
{
  struct sigaction temp;
  temp.sa_handler = &foo;
  sigfillset(&temp.sa_mask);
  sigdelset(&temp.sa_mask, SIGVTALRM);
  sigdelset(&temp.sa_mask, SIGINT );
  sigaction(SIGVTALRM, &temp, NULL);
  struct itimerval tv;
  tv.it_value.tv_sec = 2;  /* first time interval, seconds part */
  tv.it_value.tv_usec = 0; /* first time interval, microseconds part */
  tv.it_interval.tv_sec = 2;  /* following time intervals, seconds part */
  tv.it_interval.tv_usec = 0; /* following time intervals, microseconds part */

  if (setitimer(ITIMER_VIRTUAL, &tv, NULL)){
    perror(NULL);
  }
  while(1);
  return 0;
}

我想要的是每 2 秒 foo 将被调用(foo 实际上除了 while(1) 还做了一些其他的事情,假设 foo run 需要超过 2 秒),在 2 秒后 foo 确实被调用了,但是没有其他调用是直到 foo 返回。我尝试使用信号掩码(因此是 sigfillset),但也尝试在简单调用时signal(SIGVTALRM, foo)对结果进行任何更改。我还尝试在 main 外部声明 itimerval 和 sigactions 变量,但它并没有完全影响任何事情。

我正在尝试做的事情甚至可能吗?

谢谢!

4

2 回答 2

2
reference: <http://www.gnu.org/software/libc/manual/html_node/Signals-in-Handler.html>

 24.4.4 Signals Arriving While a Handler Runs

如果在信号处理函数运行时另一个信号到达会发生什么?

当调用特定信号的处理程序时,该信号会自动阻塞,直到处理程序返回。这意味着如果两个相同类型的信号靠近到达,第二个信号将被保留,直到第一个信号被处理。(如果您想允许更多这种类型的信号到达,处理程序可以使用 sigprocmask 显式解除对信号的阻塞;请参阅处理信号掩码。)

但是,您的处理程序仍可能因传递另一种信号而中断。为避免这种情况,您可以使用传递给 sigaction 的操作结构的 sa_mask 成员来明确指定在信号处理程序运行时应阻止哪些信号。这些信号是对调用处理程序的信号以及通常被进程阻塞的任何其他信号的补充。请参阅阻止处理程序。

当处理程序返回时,被阻塞的信号集恢复到处理程序运行之前的值。因此,在处理程序内部使用 sigprocmask 只会影响处理程序本身执行期间可以到达的信号,而不影响处理程序返回后可以到达的信号。

可移植性注意:如果您希望您的程序在 System V Unix 上正常工作,请始终使用 sigaction 为您希望异步接收的信号建立处理程序。在这个系统上,处理由信号建立的处理程序的信号会自动将信号的操作设置回 SIG_DFL,并且处理程序必须在每次运行时重新建立自身。这种做法虽然不方便,但在信号无法连续到达时确实有效。但是,如果另一个信号可以立即到达,它可能会在处理程序重新建立之前到达。然后第二个信号将接收默认处理,这可能会终止进程。

reference:<http://www.gnu.org/software/libc/manual/html_node/Process-Signal-Mask.html#Process-Signal-Mask>

24.7.3 过程信号屏蔽

当前被阻塞的信号集合称为信号掩码。每个进程都有自己的信号掩码。当您创建一个新进程时(请参阅创建进程),它会继承其父进程的掩码。通过修改信号掩码,您可以完全灵活地阻止或取消阻止信号。

sigprocmask 函数的原型在 signal.h 中。

请注意,您不能在多线程进程中使用 sigprocmask,因为每个线程都有自己的信号掩码,并且没有单个进程的信号掩码。根据 POSIX,sigprocmask 在多线程进程中的行为是“未指定的”。相反,请使用 pthread_sigmask。

功能:int sigprocmask (int how, const sigset_t *restrict set, sigset_t *restrict oldset)

初步:| MT-不安全竞赛:sigprocmask/bsd(SIG_UNBLOCK) | AS-不安全锁/hurd | AC-不安全锁/hurd | 请参阅 POSIX 安全概念。

sigprocmask 函数用于检查或更改调用进程的信号掩码。how 参数确定信号掩码如何更改,并且必须是以下值之一:

SIG_BLOCK

阻止集合中的信号——将它们添加到现有的掩码中。换句话说,新掩码是现有掩码和集合的并集。

SIG_UNBLOCK

解锁集合中的信号——将它们从现有掩码中移除。

SIG_SETMASK

使用 set 为掩码;忽略掩码的先前值。

最后一个参数 oldset 用于返回有关旧进程信号掩码的信息。如果您只想更改掩码而不查看掩码,请将空指针作为 oldset 参数传递。同样,如果您想知道掩码中的内容而不更改掩码,请为 set 传递一个空指针(在这种情况下,how 参数并不重要)。oldset 参数通常用于记住以前的信号掩码,以便以后恢复它。(由于信号掩码是通过 fork 和 exec 调用继承的,因此您无法预测程序开始运行时它的内容。)

如果调用 sigprocmask 导致任何未决信号被解除阻塞,则在 sigprocmask 返回之前,至少有一个信号被传递给进程。未指定传递未决信号的顺序,但您可以通过多次调用 sigprocmask 来明确控制顺序,以一次取消阻塞各种信号。

如果成功,sigprocmask 函数返回 0,返回 -1 表示错误。为此函数定义了以下 errno 错误条件:

EINVAL

how 参数无效。

您不能阻止 SIGKILL 和 SIGSTOP 信号,但如果信号集包含这些信号,sigprocmask 只会忽略它们而不是返回错误状态。

还要记住,阻塞程序错误信号(例如 SIGFPE)会导致由实际程序错误生成的信号(与使用 raise 或 kill 发送的信号相反)导致不良结果。这是因为您的程序可能已经损坏,无法继续执行到信号再次解除阻塞的地步。请参阅程序错误信号。

于 2015-03-25T17:03:50.203 回答
0

我知道这已经得到了回答和接受,但是我根据我的评论对 OP 的问题进行了微小的修改,并取得了成功的结果(每 2 秒调用一次 foo ,无限期

请注意,添加temp变量的 memset 以及从 SIGVTALRM 更改为 SIGALRM。

#include <stdio.h>
#include <sys/time.h>

void foo() {
   printf("hi\n");  
}


int main(int argc, char **argv)
{
  struct sigaction temp;
  memset(&temp, 0, sizeof(temp));
  temp.sa_handler = &foo;
  sigfillset(&temp.sa_mask);
  sigdelset(&temp.sa_mask, SIGALRM);
  sigdelset(&temp.sa_mask, SIGINT );
  sigaction(SIGALRM, &temp, NULL);
  struct itimerval tv;
  tv.it_value.tv_sec = 2;  /* first time interval, seconds part */
  tv.it_value.tv_usec = 0; /* first time interval, microseconds part */
  tv.it_interval.tv_sec = 2;  /* following time intervals, seconds part */
  tv.it_interval.tv_usec = 0; /* following time intervals, microseconds part */

  if (setitimer(ITIMER_REAL, &tv, NULL)){
    fprintf (stderr, "cannot start timer\n");
    perror(NULL);
  }

  while(1) {
    fprintf (stdout, "sleep 1\n");
    sleep (1);
  }
  return 0;
}
于 2015-03-25T18:21:32.117 回答