进程间的通信—信号

限于喜欢 提交于 2020-01-01 00:21:37

一、信号及信号源

1、信号本质

  信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。

  信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。

2、信号来源

  信号事件的发生有两个来源:

  • 硬件来源(比如我们按下了键盘或者其它硬件故障);
  • 软件来源,最常用发送信号的系统函数是kill, raise, alarm和setitimer以及sigqueue函数,软件来源还包括一些非法运算等操作。

二、进程对信号的响应

  进程可以通过三种方式来响应一个信号:

  • 忽略信号,即对信号不做任何处理,其中,有两个信号不能忽略:SIGKILL及SIGSTOP;
  • 捕捉信号。定义信号处理函数,当信号发生时,执行相应的处理函数;
  • 执行缺省操作,Linux对每种信号都规定了默认操作。注意,进程对实时信号的缺省反应是进程终止。

  Linux究竟采用上述三种方式的哪一个来响应信号,取决于传递给相应API函数的参数。

三、信号的安装

  如果进程要处理某一信号,那么就要在进程中安装该信号。安装信号主要用来确定信号值及进程针对该信号值的动作之间的映射关系,即进程将要处理哪个信号;该信号被传递给进程时,将执行何种操作。

  Linux主要有两个函数实现信号的安装:signal()sigaction()

  其中signal()在可靠信号系统调用的基础上实现,是库函数。它只有两个参数,不支持信号传递信息,主要是用于前32种非实时信号的安装;

  而sigaction()是较新的函数(由两个系统调用实现:sys_signalsys_rt_sigaction),有三个参数,支持信号传递信息,主要用来与 sigqueue()系统调用配合使用,当然,sigaction()同样支持非实时信号的安装。sigaction()优于signal()主要体现在支持信号带有参数。

signal
  typedef void (*sighandler_t)(int);
  sighandler_t signal(int signum, sighandler_t handler));
    
    第一个参数指定信号的值;
    第二个参数指定针对前面信号值的处理,
        - 设为SIG_IGN:忽略该信号
        - 设为SIG_DFL:采用系统默认方式处理信号
        - 指定一个函数地址:采用自己实现处理方式
 
  如果signal()调用成功,返回最后一次为安装信号signum而调用signal()时的handler值;失败则返回SIG_ERR。

sigaction
  int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
  
  sigaction函数用于改变进程接收到特定信号后的行为。
  
  第一个参数 signum :信号的值,可以是除SIGKILL及SIGSTOP外的任何一个特定有效的信号
          (因为这两个信号定义自己的处理函数,将导致信号安装错误)。
  第二个参数 act: 指向结构sigaction的一个实例的指针,在结构 sigaction的实例中,
          指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理。
  第三个参数 oldact:指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。
  
  如果把第二、第三个参数都设为NULL,那么该函数可用于检查信号的有效性。

  第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些函数等等。

四、信号的发送

  发送信号的主要函数有:kill()、raise()、sigqueue()、alarm()、setitimer()以及abort()。
  

五、实例

闹钟:

#include<unistd.h>
#include<stdio.h>
#include<signal.h>

void handler()
{
    printf("hello\n");
}

//延时5S 打印 hello
int main(void)
{
    int i;
    signal(SIGALRM, handler);
    alarm(5);
    
    for(i=1;i<7;i++){
        printf("sleep %d....\n",i);
        sleep(1);
    }
    
    return 0;
}
信号的设置:

#include <unistd.h>  
#include <signal.h>  
#include <sys/types.h>  
#include <stdlib.h>  
#include <stdio.h>  

void handler(int sig)  
{  
    printf("Handler the signal %d\n", sig);  
}  
  
int main(void)  
{  
    sigset_t sigset;//用于记录屏蔽字  
    sigset_t ign;//用于记录被阻塞的信号集  
    struct sigaction act; 
    
    //清空信号集  
    sigemptyset(&sigset);  //初始化信号集
    sigemptyset(&ign);  
    //向信号集中添加信号SIGINT  
    sigaddset(&sigset, SIGINT);  
  
    //设置处理函数和信号集      
    act.sa_handler = handler;  
    sigemptyset(&act.sa_mask);  
    act.sa_flags = 0;  
    sigaction(SIGINT, &act, 0);  
  
    printf("Wait the signal SIGINT...\n");  
    pause();//挂起进程,等待信号  
  
    //设置进程屏蔽字,在本例中为屏蔽SIGINT   
    sigprocmask(SIG_SETMASK, &sigset, 0);     
    printf("Please press Ctrl+c in 10 seconds...\n");  
    sleep(10); 
    
    //测试SIGINT是否被屏蔽  
    sigpending(&ign);  
    
    if(sigismember(&ign, SIGINT))  
        printf("The SIGINT signal has ignored\n"); 
    
    //在信号集中删除信号SIGINT  
    sigdelset(&sigset, SIGINT);  
    printf("Wait the signal SIGINT...\n"); 
    
    //将进程的屏蔽字重新设置,即取消对SIGINT的屏蔽  
    //并挂起进程  
    sigsuspend(&sigset);  
  
    printf("The app will exit in 5 seconds!\n");  
    sleep(5);  
    exit(0);  
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!