进程间通信--信号

来源:互联网 发布:落樱神斧华盛顿知乎 编辑:程序博客网 时间:2024/04/30 04:48

什么是信号

用过Windows的我们都知道,当我们无法正常结束一个程序时,可以用任务管理器强制结束这个进程,但这其实是怎么实现的呢?同样的功能在Linux上是通过生成信号和捕获信号来实现的,运行中的进程捕获到这个信号然后作出一定的操作并最终被终止。
信号是UNIX和Linux系统响应某些条件而产生的一个事件,接收到该信号的进程会相应地采取一些行动。通常信号是由一个错误产生的。但它们还可以作为进程间通信或修改行为的一种方式,明确地由一个进程发送给另一个进程。一个信号的产生叫生成,接收到一个信号叫捕获。


怎么处理信号

第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。

第二种方法是,忽略某个信号,对该信号不做任何处理,就象未发生过一样。

第三种方法是,对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得进程终止。进程通过系统调用signal来指定进程对某个信号的处理行为。


如何指定信号处理函数

1、信号安装
(1)、signal()

#include void (*signal(int signum, void (*handler))(int)))(int); 

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

(2)、sigaction()

#include int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));

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

下面是signal()的例子,最好在终端跑而不是xcode之类的编译器,我在编译器上跑一直没成功过


#include <stdio.h>#include <signal.h>#include <unistd.h>void signal_handler(int sig){    printf("\nINT signal received~~~ %d\n", sig);    (void)signal(SIGINT, SIG_DFL);}int main(){    if(signal(SIGINT,signal_handler) ==SIG_ERR) return 0;    while (1) {//        raise(SIGINT);        printf("\nHello Fantasy\n");        sleep(1);    }    return 0;}

当然想要有更好的健壮性也可以使用sigaction()这个方法

void signal_handler(int sig){    printf("\nINT signal received~~~ %d\n", sig);    (void)signal(SIGINT, SIG_DFL);}int main(){    struct sigaction act;    act.sa_handler = signal_handler;    sigemptyset(&act.sa_mask);    //使sigaction函数重置为默认行为    act.sa_flags = SA_RESETHAND;    sigaction(SIGINT, &act, 0);    while (1) {        //        raise(SIGINT);        printf("\nHello Fantasy\n");        sleep(1);    }    return 0;}

其中
act.sa_flags可疑有一下选项
哈哈第一次插图片

接受端的信号处理注册部分弄完了,那么如何向相应进程发送消息呢

发送信号

–KILL
先来看看kill函数,进程可以通过kill函数向包括它本身在内的其他进程发送一个信号,如果程序没有发送这个信号的权限,对kill函数的调用就将失败,而失败的常见原因是目标进程由另一个用户所拥有。想一想也是容易明白的,你总不能控制别人的程序吧,当然超级用户root,这种上帝般的存在就除外了。

#include <sys/types.h>#include <signal.h>int kill(pid_t pid, int sig);

kill调用失败返回-1,调用失败通常有三大原因:
1、给定的信号无效(errno = EINVAL)
2、发送权限不够( errno = EPERM )
3、目标进程不存在( errno = ESRCH )

– alarm

#include <unistd.h>unsigned int alarm(unsigned int seconds);

它的作用是在一段时间之后定时发送一个信号。

像这样~~~

#include <unistd.h>#include <sys/types.h>#include <stdlib.h>#include <stdio.h>#include <signal.h>static int child_flag = 0;void signal_handler(int sig){    child_flag = 1;}int main(){    pid_t pid;    pid = fork();    switch(pid)    {        case -1:            perror("fork failed\n");            exit(EXIT_FAILURE);        case 0:            //子进程            sleep(2);            //向父进程发送信号            kill(getppid(), SIGALRM);            exit(0);        default:;    }    //设置处理函数    signal(SIGALRM, signal_handler);    while(!child_flag)    {        printf("Hello Fantasy!\n");        sleep(1);    }    printf("\nFantasy is released %d\n", SIGALRM);    exit(0);}

安全性

我们的程序是要用在多进程环境的,一个程序可能会收到数个程序发来的信号,根据优先级执行相应的处理函数,那么信号处理函数的可重入性就显得至关重要,不能调用不可重入的函数
以下是一些可重入的函数:
这里写图片描述

Thats all~~~

1 0