Interprocess communication - 2 signal handler

内核通过信号来控制程序. 程序接收到信号后, 停止所有事务, 调用handler funciton来响应对应的信号.

如图 : 


 

进程有自己的signal mappings table : 


 

正因为有了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.

常用信号如下 : 


 

【注意】

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

【参考】

man 7 signal

http://en.wikipedia.org/wiki/Interrupts

http://en.wikipedia.org/wiki/Signal_handler

时间: 2024-08-30 03:16:42

Interprocess communication - 2 signal handler的相关文章

linux 之进程间通信-------------InterProcess Communication

进程间通信至少可以通过传送打开文件来实现,不同的进程通过一个或多个文件来传递信息,事实上,在很多应用系统里,都使用了这种方法.但一般说来,进程间 通信(IPC:InterProcess Communication)不包括这种似乎比较低级的通信方法.Unix系统中实现进程间通信的方法很多,而且不幸的是,极少方法能在所有的Unix系 统中进行移植(唯一一种是半双工的管道,这也是最原始的一种通信方式).而Linux作为一种新兴的操作系统,几乎支持所有的Unix下常用的进程间通信 方法:管道.消息队列.

c#进程间通信(Inter-Process Communication)

原文:c#进程间通信(Inter-Process Communication) c#进程间通信(IPC, Inter-Process Communication) 接收端: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using S

Interprocess communication - 1 data stream

当进程间可以共享数据, 并且可以等待其他进程是否执行完毕时, 程序将变得更加强大. 显然共享数据和等待都是内部进程通信的范畴, 这里主要讲一下 data stream. 每个进程都有一个descriptor table, 这个表包含了file descriptor(a number) 和 data stream的映射关系. 如图 :  A file descriptor is a number that represents a data stream. 默认会有3条映射关系, 0, 1 , 2

use setitimer raise signal interval-granuated by seconds and microseconds

在程序中如果要设置定时器, 简单的可以使用sleep, alarm. 他们的时间单位都是秒. NAME alarm - set an alarm clock for delivery of a signal SYNOPSIS #include <unistd.h> unsigned int alarm(unsigned int seconds); NAME sleep - Sleep for the specified number of seconds SYNOPSIS #include &

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

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

linux signal 用法和注意事项

http://blog.chinaunix.net/uid-9354-id-2425031.html 所以希望能用相同方式处理信号的多次出现,最好用sigaction.信号只出现并处理一次,可以用signal.   signal函数每次设置具体的信号处理函数(非SIG_IGN)只能生效一次,每次在进程响应处理信号时,随即将信号处理函数恢复为默认处理方式.所以如果想多次相同方式处理某个信号,通常的做法是,在响应函数开始,再次调用signal设置,如下图: int sig_int(); //My s

Python标准库07 信号 (signal包,部分os包)

原文:Python标准库07 信号 (signal包,部分os包) 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢!   在了解了Linux的信号基础之后,Python标准库中的signal包就很容易学习和理解.signal包负责在Python程序内部处理信号,典型的操作包括预设信号处理函数,暂停并等待信号,以及定时发出SIGALRM等.要注意,signal包主要是针对UNIX平台(比如Linux, MAC OS),而Windo

Linux signal那些事儿【转】

转自:http://blog.chinaunix.net/uid-24774106-id-4061386.html Linux编程,信号是一个让人爱恨交加又不得不提的一个领域.最近我集中学习了Linux的signal相关的内容,分享出来,也为防止自己忘记.     信号的本质是异步.异步一这个词,听着高端大气上档次,又让人云山雾绕,其则不然.其实我们想想,我们这个世界是异步的,每个人干事儿,并不总是A->B->C->D这种.比如我在网上买了东西,我其实并不知道快递几时能到.我可能在公司里

【Android平台】 Alljoyn学习笔记四 Android Core API参考

CORE API GUIDE - ANDROID Prerequisites Install dependencies for the Windows platform, or for the Linux platform. A device running Android OS version 2.2 (Froyo) or greater and running a chip based on the ARM 5 (or greater) instruction set. Importing