linux进程间通信之信号

1:信号

信号是UNIX中所使用的进程间通信比较古老的一种方法,linux直接继承而来。信号的本质其实就是一种软中断。在有些程序中需要用到此种方法:比如最为经典的是在终端中输入kill命令,杀死某个进程,该命令就是通知内核来产生SIGKILL signal,来终止一个进程。这是一种比较典型的异步通信方式。
信号可以直接进行用户空间进程和内核空间进程之间的交互,内核进程也可以利用它来通知用户进程发生了哪些系统事件。可以在任何时候发给某个进程,而无需知道进程的状态。如果该进程当前并未处于执行状态,则该信号就由内核保存起来,知道该进程恢复执行再传递给它为止;如果一个信号被该进程设置为堵塞,则该信号的传递被延迟,知道其堵塞被取消时才被传递。

 kill -l命令行可以列出,系统支持的所有signal,它是由内核来定义的
 $ kill -l
 1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP
 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR
31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX    

signal的缺点就是只支持有限的几种,用户无法定义,以及无法传递数据。

一个完整的信号生命周期可以分为3个重要阶段,这3个阶段可以由4个重要事件来刻画:信号产生,信号在进程中注册,信号在进程中注销,以及执行信号处理函数。见下图(来自于linux应用程序开发标准教程):

这里写图片描述

信号分为不可靠信号和可靠信号。
不可靠信号:如果发现该信号已经在进程中注册,那么久该忽略该信号。因此若前一个信号还未注销又产生了相同的信号就会产生信号丢失。发生不可靠的原因就是在上图中内核发生进程到信号处理函数之间, 有一个用户进程(上图)的信号注册和注销的时间窗口,造成就由可能在这个时间窗口之间,有信号又重新产生。
可靠信号:发生一个信号给进程时,不管该信号是否已经在进程中注册,都会被再注册一次,因此信号就不会丢失。
所有可靠信号都支持排队,而所有不可靠信号都不支持排队。

用户对信号的响应有3种方式:
忽略信号:即对信号不做任何处理。但有两个信号不能忽略,即SIGKILL以及SIGTOP
捕捉信号:定义信号处理函数,当信号发生时,执行响应的自定义处理函数
执行缺陷操作,linux对每种信号都规定了默认操作。

2:信号发送API

linux提供给信号发送函数主要有kill(), raise(), alarm()以及pause()

kill()函数,可以发生信号给进程或进程组
函数原型:
    int kill(pid_t pid, int sig)

 raise()可以运行进程向自身发生信号。
     int raise(int sig)

 pause()函数使调用进程挂起至到捕捉到一个信号
    int pause()

 下面是一个signal的例子
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>


int main()
{
    pid_t pid;
    int ret;


    if((pid= fork()) <0) 
    {   
        printf("Fork error\n");
        exit(1);
    }   

    if (0 == pid)
    {   

        printf("Child process is %d\n", getpid());
        raise(SIGSTOP);
        exit(0);
    } else 
    {        sleep(1);
        if ((waitpid(pid, NULL, WNOHANG)) == 0)
        {

            if (kill(pid, SIGKILL) == 0)
            {
                printf("Parent kill %d\n", pid);
            }
         }
         waitpid(pid,NULL, 0);
         exit(0);
     }
}

上述例子来自于《linux应用程序开发标准教程》:首先fork()创建一个子进程,接着为了保证子进程不在父进程调用kill()之前推出,在子进程中使用raise()函数向自身发生SIGSTOP信号,使子进程暂停。同时稍微做了修改,在父进程中增加了1s延迟,

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值