Interprocess communication - 2 signal handler

内核通过信号来控制程序. 程序接收到信号后, 停止所有事务, 调用handler funciton来响应对应的信号.
如图 : 
Interprocess communication - 2 signal handler - 德哥@Digoal - The Heart,The World.
 
进程有自己的signal mappings table : 
Interprocess communication - 2 signal handler - 德哥@Digoal - The Heart,The World.
 
正因为有了signal mappings table, 所以可以创建自己的handler function , 响应各种信号.
1. 首先要创建handler函数.
2. 然后创建一个sigaction结构体, 记录handler function, mask(在执行handler function时不响应其他信号的掩码)等, sa_flag. 具体参考man sigaction.
结构体如下 : 
struct sigaction {
                  void (*sa_handler)(int);
                  void (*sa_sigaction)(int, siginfo_t *, void *);
                  sigset_t sa_mask;
                  int sa_flags;
                  void (*sa_restorer)(void);
              }

因此自定义的handler函数需要符合 返回值void, 参数int 的要求.

3. 使用sigaction函数注册第二步创建的结构体以及信号到signal mappings table.
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
RETURN VALUE
       sigaction() returns 0 on success and -1 on error.


oldact不关心的话直接使用NULL替代. (If  act is non-null, the new action for signal signum is installed from act.  If oldact is non-null, the previous action is saved in oldact.)

例如 : 
写一个 handler function 并注册 SIGINT信号 . 使用 Ctrl+C 或者kill -INT 向进程发送SIGINT信号. 
[root@db-172-16-3-150 zzz]# cat d.c
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

// 错误输出函数
void error(char * msg) {
  fprintf(stdout, "%s: %s\n", msg, strerror(errno));
  exit(1);
}

// 自定义的sigint函数.
void sigint (int sig) {
  fprintf(stdout, "sig:%i, signal sigint received.\n", sig);
  exit(1);
}

// 注册signal table的函数.
int reg_signal(int sig, void (*handler)(int) ) {
  struct sigaction action;
  action.sa_handler = handler;
  // 以下表示在handler function执行过程中同样接收其他信号.
  sigemptyset(&action.sa_mask);
  // 不设置任何sa_flags
  action.sa_flags = 0;
  // 注册signal table.
  return sigaction(sig, &action, NULL);
}

int main() {
  char name[80]; 
  // 注册一个处理的SIGINT信号的自定义函数.
  if( reg_signal(SIGINT, sigint) == -1 ) {
    error("reg_signal error");
  }
  fprintf(stdout, "please enter your name:");
  fgets(name, 80, stdin);
  fprintf(stdout, "hello, %s\n", name);
  return 0;
}

执行如下 : 
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./d.c -o d
[root@db-172-16-3-150 zzz]# ./d
please enter your name:
此时键盘按键ctrl+c, 发出一个SIGINT信号, 结果如下 : 
sig:2, signal sigint received.

当然也可以使用 "kill -INT 进程号". 如kill -INT 2999 , 这里不进行测试.

signal handler 函数体内如果没有exit()的话, 那么handler函数执行完后, 进程将会接着收到信号开始的那刻继续往后处理. 如果handler函数体内有exit()则会直接退出进程.
另外就是初始化sigaction 结构体的时候, 有一个设置sa_mask的地方, 如果这里没有设置, 那么signal handler函数处理过程中可以接收其他的信号, 也就是说也可以被打断. 例如下面的例子写了一个无限循环的signal handler过程.

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

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

void sig_handler (int sig) {
  sleep(1);
  switch(sig) {
    case SIGALRM:
      fprintf(stdout, "sig:%i, signal sigalrm received.\n", sig);
      raise(SIGINT);
      break;
    case SIGINT:
      fprintf(stdout, "sig:%i, signal sigint received.\n", sig);  
      raise(SIGALRM);
      // fprintf(stdout, "if exit(1) not commeted, this line will printed.");
      break;
    default:
      fprintf(stdout, "sig:%i, this handler cann't handler the singal.\n", sig);
      break;
  }
  fprintf(stdout, "sig:%i outside the switch code.\n", sig);
  // 这里要注意, 如果把exit(1)的注释去掉就会有意想不到的结果. 这个无限循环将被打破. 
  //exit(1);
}

int reg_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);
}

int main() {
  char name[80]; 
  if( reg_signal(SIGINT, sig_handler) == -1 ) {
    error("reg_signal error");
  }
  if( reg_signal(SIGALRM, sig_handler) == -1 ) {
    error("reg_signal error");
  }
  fprintf(stdout, "please enter your name:");
  fgets(name, 80, stdin);
  fprintf(stdout, "hello, %s\n", name);
  return 0;
}

执行结果 : 
[root@db-172-16-3-150 zzz]# ./d
please enter your name: 键入CTRL+C
// 1 second elapsed
sig:2, signal sigint received.
// 1 second elapsed
sig:14, signal sigalrm received.
sig:14, outside the switch code.
sig:2, outside the switch code.
// 1 second elapsed
sig:2, signal sigint received.
// 1 second elapsed
sig:14, signal sigalrm received.
sig:14, outside the switch code.
sig:2, outside the switch code.
// 1 second elapsed
sig:2, signal sigint received.

持续循环
// 从上面的结果看出, kernel在处理多个信号同时触发时.
// 同时这里还使用了计时器, 考虑到内核的效率问题, 内核给一个进程只分配一个计时器. 如果给一个进程可以有多个计时器的话, 内核管理计时器会非常复杂, 内核变得很慢.

如果使用kill -ALRM 进程号 会是什么情况呢?
结果如下 : 

[root@db-172-16-3-150 zzz]# ./d
please enter your name: 另一个会话输入kill -ALRM ./d进程号
// 1 second elapsed 
sig:14, signal sigalrm received.
// 1 second elapsed 
sig:2, signal sigint received.
sig:2, outside the switch code.
sig:14, outside the switch code.
// 1 second elapsed 
sig:14, signal sigalrm received.
// 1 second elapsed 
sig:2, signal sigint received.
sig:2, outside the switch code.
sig:14, outside the switch code.
// 1 second elapsed 
sig:14, signal sigalrm received.

如此循环.
从两次发出的信号得到的结果来看, 内核没有对哪个信号有优先处理的权力.

将sig_handler函数中的exit(1)注释去掉, 重新编译测试 : 

[root@db-172-16-3-150 zzz]# ./d
please enter your name: 键入CTRL+C
// 1 second elapsed 
sig:2, signal sigint received.
// 1 second elapsed 
sig:14, signal sigalrm received.
sig:14, outside the switch code.
[root@db-172-16-3-150 zzz]# ./d
please enter your name:另一个SESSION键入kill -ALRM ./d进程号
// 1 second elapsed
sig:14, signal sigalrm received.
// 1 second elapsed 
sig:2, signal sigint received.
sig:2, outside the switch code.

从这个结果返回去推算前面没有注释exit(1)的执行结果, 其实信号是一个一个来处理的, 不是并行处理的. 别看前面是3个输出一起出来的. 其实是在一个信号结束后另一个信号才触发的. 如下
[root@db-172-16-3-150 zzz]# ./d
please enter your name: 另一个会话输入kill -ALRM ./d进程号
// 1 second elapsed
sig:14, signal sigalrm received.
// 1 second elapsed
sig:2, signal sigint received.
sig:2, outside the switch code.
// sig:2 sig_handler处理结束
sig:14, outside the switch code.
// sig:14 sig_handler处理结束
// 1 second elapsed
sig:14, signal sigalrm received.
// 1 second elapsed
sig:2, signal sigint received.
sig:2, outside the switch code.
// sig:2 sig_handler处理结束
sig:14, outside the switch code.
// sig:14 sig_handler处理结束
// 1 second elapsed
sig:14, signal sigalrm received.



常用信号如下 : 
Interprocess communication - 2 signal handler - 德哥@Digoal - The Heart,The World.
 




【注意】
1. 有两个信号不能自定义handler function , 那就是 SIGSTOP 和 SIGKILL . 




【参考】
man 7 signal
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值