wake-up signal SIGALRM from alarm() or setitimer(). SIG_DFL & SIG_IGN

简介:
SIGALRM信号, 一般可以由alarm或者setitmer来发出。 可以用于定多长时间触发一个事件.
例如在等待用户输入时, 超过多少秒就触发这个信号. 在触发后, 用户输入被中断, 跳转到信号处理函数, 信号处理函数结束后, 接着用户输入的下一个语句执行, 用户输入不再执行. 如下 :
[root@db-172-16-3-150 zzz]# cat a.c
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#define TIMEOUT 5

void error(char * msg) {
  fprintf(stdout, "%s: %s\n", msg, strerror(errno));
  exit(1);
}

void alrm_handler(int sig) {
  fprintf(stdout, "sig:%i, Sorry, TIME IS UP. Please enter your name within %i second.\n", sig, TIMEOUT);
  alarm(TIMEOUT);
  //exit(1);
}

int reg_handler(int sig, void (*handler)(int)) {
  struct sigaction action;
  action.sa_handler = handler;
  sigemptyset(&action.sa_mask);
  action.sa_flags = 0;
  return sigaction(sig, &action, NULL);
}

int main() {
  char fname[80];
  char lname[80];
  alarm(TIMEOUT);
  if( reg_handler(SIGALRM, alrm_handler) == -1 ) {
    error("reg_handler error");
  }
  fprintf(stdout, "please enter your first name: ");
  fgets(fname, 80, stdin);
  // 第一次超时后, 从下面开始执行.
  fprintf(stdout, "please enter your last name: ");
  fgets(lname, 80, stdin);
  // 第二次超时后, 从下面开始执行.
  fprintf(stdout, "Hello, %s.%s\n", lname, fname);
  return 0;
}

执行 : 
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./a.c -o a
[root@db-172-16-3-150 zzz]# ./a
please enter your first name: sig:14, Sorry, TIME IS UP. Please enter your name within 5 second. 
please enter your last name: sig:14, Sorry, TIME IS UP. Please enter your name within 5 second.
Hello, ?

注意这个程序有问题, 因为每隔5秒就会触发这个信号.
应该改成 : 

[root@db-172-16-3-150 zzz]# cat a.c
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#define TIMEOUT 5

void error(char * msg) {
  fprintf(stdout, "%s: %s\n", msg, strerror(errno));
  exit(1);
}

void alrm_handler(int sig) {
  fprintf(stdout, "sig:%i, Sorry, TIME IS UP. Please enter your name within %i second.\n", sig, TIMEOUT);
  // 一般在信号处理函数中使用exit, 这么做的话程序就直接退出了. 当然你可以选择不用. 例如定时执行其他任务的, 就不需要exit. 
  exit(1);
}

int reg_handler(int sig, void (*handler)(int)) {
  struct sigaction action;
  action.sa_handler = handler;
  sigemptyset(&action.sa_mask);
  action.sa_flags = 0;
  return sigaction(sig, &action, NULL);
}

int main() {
  char fname[80];
  char lname[80];
  if( reg_handler(SIGALRM, alrm_handler) == -1 ) {
    error("reg_handler error");
  }
  alarm(TIMEOUT);
  fprintf(stdout, "please enter your first name: ");
  fscanf(stdin, "%80s", fname);
  // 时间在这里剩余2秒时, 重新调用alarm(TIMEOUT) 又会把计时器调整为5秒,  覆盖前面的剩余时间. 
  alarm(TIMEOUT);
  fprintf(stdout, "please enter your last name: ");
  fscanf(stdin, "%80s", lname);
  fprintf(stdout, "Hello, %s.%s\n", lname, fname);
  return 0;
}


在程序中要忽略某些信号或者要还原信号处理函数怎么办呢?
sa_handler specifies the action to be associated with signum and may be SIG_DFL for the default action, SIG_IGN
       to ignore this signal, or a pointer to a signal handling function.  This function receives the signal number as
       its only argument.


如果在程序中要忽略对信号的响应. 使用如下方法 : 
reg_handler(SIGALRM, SIG_IGN), 表示遇到SIGALRM信号不作任何响应.
例如在以上代码中添加

  if( reg_handler(SIGINT, SIG_IGN) == -1 ) {
    error("reg_handler error");
  }

则在程序执行过程中使用 "kill -INT 进程号", 或者"键入CTRL+C" 都无响应.

如果在程序中要还原原来的signal handler, 使用如下 : 
reg_handler(SIGINT, SIG_DFL);

注意, 

NAME
       sigaction - examine and change a signal action
SYNOPSIS
       #include <signal.h>
       int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
DESCRIPTION
       The sigaction() system call is used to change the action taken by a process on receipt of a specific signal.
       signum specifies the signal and can be any valid signal except SIGKILL and SIGSTOP.

因为SIGKILL和SIGSTOP信号不能调用sigaction注册handler function. 所以也就不存在忽略这两个信号的可能了.

下面的例子, 问问题, 如果5秒内未答出则超时, 并raise(SIGINT) , 调用end_game结束程序. 或者直接ctrl+c 发出SIGINT信号调用end_game结束程序 : 

[root@db-172-16-3-150 zzz]# cat a.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <signal.h>

#define TIMEOUT 5

int score = 0;

void end_game(int sig) {
  printf("\nsig:%i, Final score: %i\n", sig, score);
  exit(0);
}

int catch_signal (int sig, void (*handler)(int)) {
  struct sigaction action;
  action.sa_handler = handler;
  sigemptyset(&action.sa_mask);
  action.sa_flags = 0;
  return sigaction (sig, &action, NULL);
}

void times_up(int sig) {
  fprintf(stdout, "\nsig:%i, TIME'S UP!", sig);
  raise(SIGINT);
}

void error(char *msg) {
  fprintf(stderr, "%s: %s\n", msg, strerror(errno));
  exit(1);
}
int main() {
  catch_signal(SIGALRM, times_up);
  catch_signal(SIGINT, end_game);
  srandom (time (0));
  while(1) {
    int a = random() % 11;
    int b = random() % 11;
    char txt[4];
    alarm(TIMEOUT);
    printf("\nWhat is %i times %i? ", a, b);
    fgets(txt, 4, stdin);
    int answer = atoi(txt);
    if (answer == a * b)
      score++;
    else
      printf("\nWrong! Score: %i\n", score);
  }
  return 0;
}


其他 :
Q: Are signals always received in the same order they are sent?
A: Not if they are sent very close together. 
The operating system might choose to reorder the signals if it thinks one is more important than the others.
Q: Is that always true?
A: It depends on the platform. 
On most versions of Cygwin, for example, the signals will always be sent and received in the same order. 
But in general, you shouldn’t rely on it.
Q: If I send the same signal twice, will it be received twice by the process?
A: Again, it depends. 
On Linux and the Mac, if the same signal is repeated very quickly, the kernel might choose to only send the signal once to the process. 
On Cygwin, it will always send both signals. 
But again, you should not assume that just because you sent the same signal twice, it will be received twice


【参考】
setitimer : 

相关文章
|
5月前
|
设计模式 Unix Shell
ECF机制:信号 (Signal)
ECF机制:信号 (Signal)
97 0
|
9月前
|
消息中间件 Unix Linux
进程通信 软中断 signal()解读
进程通信 软中断 signal()解读
|
9月前
|
Unix Linux C语言
软中断通信及signal()解读
软中断通信及signal()解读
|
10月前
|
存储 固态存储 Linux
深入学习tombstone和signal
深入学习tombstone和signal
129 0
深入学习tombstone和signal
|
11月前
|
C++
boost之signal的使用
boost是C++的一个扩展库,被称为C++准标准库,里面的组件很丰富,并且引用方便,85%的组件只需要引用头文件即可使用。在嵌入式系统也可以很方便的使用,这里介绍一下signal的使用,有点类似Qt里的信号槽。 可以接收静态函数、类成员函数、labmda表达式。 下面这个是使用signal封装的一个事件注册处理模板,使用起来还是很方便的。
98 0
|
物联网 Linux 开发者
signal 函数1|学习笔记
快速学习 signal 函数1
84 0
signal 函数1|学习笔记
Interrupt
Thread#interrupt()的作用其实也不是中断线程,而是通知线程应该中断了 Thread#interrupted()检查当前中断标识(即查看当前中断信号是true还是false),并清除中断信号 Thread#isInterrupted()检查当前中断标识
3769 0
|
iOS开发
iOS Mach异常和signal信号
本着探究下iOS Crash捕获的目的,学习了下Crash捕获相关的Mach异常和signal信号处理,记录下相关内容,并提供对应的测试示例代码。Mach为XNU的微内核,Mach异常为最底层的内核级异常,在iOS系统中,底层Crash先触发Mach异常,然后再转换为对应的signal信号。
9075 0
|
Linux
关于signal和fork的思考
fork可以在linux中创建子进程。先看man手册里面的东西: SYNOPSIS       #include        pid_t fork(void);DESCRIPTION       fork()  creates  a new process by duplicating the calling process.
1183 0