c setitimer在呼叫范围之外时不发送信号

时间:2015-03-25 16:13:40

标签: c signals setitimer

我有以下案例

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运行需要超过2秒),2秒后foo确实被调用但是之后没有其他召唤直到foo返回。我尝试使用信号掩码(因此使用sigfillset),但是当简单地调用signal(SIGVTALRM, foo)时,结果中没有进行任何更改。我也试过在main之外声明itimerval和sigactions变量,它并没有对任何事情产生任何影响。

是我试图做的事情吗?

谢谢!

2 个答案:

答案 0 :(得分: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-Unsafe比赛:sigprocmask / bsd(SIG_UNBLOCK)| AS-Unsafe lock / hurd | AC-Unsafe lock / 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

     

参数如何无效。

     

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

     

请记住,阻塞程序错误信号(如SIGFPE)会导致实际程序错误产生的信号产生不良结果(与使用raise或kill发送的信号相反)。这是因为您的程序可能太破碎而无法继续执行到再次解除阻塞信号的位置。请参阅程序错误信号。

答案 1 :(得分:0)

我知道这已经得到了回答和接受,但我根据我的评论对OP的问题进行了如下微小的改动,并取得了成功(每2秒调用一次, ad infinitum

请注意,添加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;
}