嵌入式Linux进程间通信——信号

一、信号概述

        信号是在软件层面上对中断机制的一种模拟,是一种异步通信方式。信号可以直接进行用户空间进程和内核空间进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。它可以再任何时候发给某一进程,而无需知道该进程的状态。如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它为止;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。

        一个完整的信号生命周期:信号在内核中产生,信号在进程中注册,信号在进程中注销,执行信号处理函数。

用户进程对信号响应的3种方式
忽略信号 即对信号不做任何处理,但是有两个信号不能忽略,即SIGKILL和SIGSTOP。
捕捉信号 定义信号处理函数,当信号发生时,执行响应的自定义处理函数。
执行默认操作 Linux中对每种信号都规定了默认操作(如下表)
常见信号的含义及其默认操作
信号名 含义 默认操作
SIGHUP 该信号在用户终端连接(正常或非正常)结束时发出,通常是在终端的控制进程结束时,通知同一会话内的各个作业与控制终端不再关联 终止
SIGINT 在信号在用户键入INTR字符(通常是Ctrl-C)时发出,终端驱动程序发送此信号并送到前台进程中的每一个进程 终止
SIGQUIT 该信号与SIGINT类似,但由QUIT字符(通常是Ctrl-\)来控制 终止
SIGILL 该信号在一个进程企图执行一条非法指令时(可执文件本身出现错误,或者试图执行数据段、堆栈溢出时)发出 终止
SIGFPE 该信号在发生致命的算术运算错误时发出。这里不仅包括浮点运算错误,还包括溢出及除数为0等其他所有的算术错误 终止
SIGKILL 该信号用来立即结束程序的运行,并且不能被阻塞、处理或忽略 终止
SIGALRM 该信号当一个定时器到时的时候发出 终止
SIGSTOP 该信号用于暂停一个进程,且不能被阻塞、处理或忽略 暂停进程
SIGSTP 该信号用于交互停止进程,用户键入SUSP字符(通常是Ctrl-Z)发出这个信号 停止进程
SIGCHLD 子进程改变状态时,父进程会受到这个信号 忽略
SIGABORT 进程异常终止时发出  

二、信号的发送和捕捉

1.kill()和raise()

(1)函数说明:kill()函数和raise()函数都可以向进程或进程组发送信号,而raise()还允许进程向自身发送信号。

(2)语法要点

kill()函数语法要点
所需头文件

#include <signal.h>

#include <sys/types.h>

函数原型 int kill(pid_t pid,int sig)
函数传入值 pid: 正数:要发送信号的进程号
0:信号被发送到所有和当前进程在同一个进程组的进程
-1:信号发送给所有在进程表中的进程(除了进程号最大的进程外)
<-1:信号发送给进程组号为-pid的每一个进程
sig:信号
函数返回值 成功:0
出错:-1
raise()函数语法要点
所需头文件

#include <signal.h>

#include <sys/types.h>

函数原型 int raise(int sig)
函数传入值 sig:信号
函数返回值 成功:0
出错:-1

(3)函数实例

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

int main()
{
    pid_t pid;

    pid = fork();
    if(pid == -1)
    {
        printf("fork error.\n");
        exit(1);
    }
    else if(pid == 0)
    {
        printf("Child %d:Waiting for any signal....\n",getpid());
        raise(SIGSTOP);
        exit(0);
    }
    else
    {
        //waitpid()使用WNOHANG且子进程未退出则返回0(非阻塞)
        if(waitpid(pid,NULL,WNOHANG) == 0)  
        {
            sleep(1);//延时1秒
            if(kill(pid,SIGKILL) == 0)      //向子进程发送KILL信号
            {
                printf("Parent %d kill Child %d.\n",getpid(),pid);
            }
        }
        //阻塞等待子进程结束
        waitpid(pid,NULL,0);                
        exit(0);
    }
}

2.alarm()和pause()

(1)函数说明

        alarm()也称为闹钟函数,它可以在进程中设置一个定时器,当定时器指定的时间到时,它就向进程发送SIGALARM信号。注意一个进程只能有一个闹钟时间,如果在调用alarm()之前已设置过闹钟时间,则任何以前的闹钟时间都被新值所代替。

         pause()是用于将调用进程挂起直至捕捉到信号为止。

(2)函数语法

alarm()函数语法要点
所需头文件 #include <unistd.h>
函数原型 unsigned int alarm(unsigned int seconds)
函数传入值 seconds:指定秒级,系统经过seconds秒之后向该进程发送SIGALARM信号
函数返回值 成功:如果调用此alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0
出错:-1
pause()函数语法要点
所需头文件 #include <unistd.h>
函数原型 int pause(void)
函数返回值 -1,并且把error值设置为EINTR

(3)函数实例

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

int main()
{
    int ret = alarm(5);
    pause();
    printf("I have been waken up.\n");
    exit(0);
}
输出结果:Alarm clock
结果分析:当进程接收到alarm()函数发送的SIGALRM信号后进程直接退出,于是后面的printf语句不打印。

三、信号的处理

       信号的处理指一个进程可以选择忽略某些信号而只处理其他一些信号,也可以选择如何处理信号。信号处理的主要方式有两种:一种是使用简单的signal()函数,另一种是使用信号集函数组。

1.信号处理函数signal()

(1)函数说明:使用signal()函数处理时,只需要指出要处理的信号和处理函数即可。Linux还支持一个更健壮、更新的信号处理函数sigaction()。

(2)函数语法

signal()函数语法要点
所需头文件 #include <signal.h>
函数原型 void (*signal(int signum,void (*handler)(int )))(int)
函数传入值 signum:指定信号代码
handler: SIG_IGN:忽略该信号
SIG_DFL:采用系统默认方式处理信号
自定义的信号处理函数指针
函数返回值 成功:以前的信号处理配置
出错:-1
sigaction()函数语法要点
所需头文件 #include <signal.h>
函数原型 int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact)
函数传入值 signum:信号代码,可以为除SIGKILL和SIGSTOP外的任何一个特定有效信号
act:指向结果sigaction的一个实例的指针,指定对特定信号的处理
oldact:保存原理对相应信号的处理
函数返回值 成功:0
出错:-1

结构体sigaction定义如下:

struct sigaction
{
    void (*sa_handler)(int signo);
    //sa_handler是一个函数指针,指定信号处理函数,这里处理可以使用户自定义的处理函数外,还可以为
    SIG_DFL(采用系统默认的处理方式)或SIG_IGN(忽略信号)。它的处理函数只有一个参数,即信号量。    

    sigset_t sa_mask;
    //sa_mask是一个信号集,它可以指定在信号处理程序执行过程中哪些信号应当被屏蔽,在调用信号捕获函数之前,该信号集要加入到信号的信号屏蔽字中。    

    int sa_flags;
    //sa_flags中包含许多标志位,是对信号进行处理的各个选择项,常见值如下表。
 
    void (*sa_restore)(void);
}
常见信号的含义及其默认操作
选项 含义
SA_NODEFER \ SA_NOMASK 当捕捉到信号时,在执行其信号捕捉函数时,系统不会自动屏蔽此信号
SA_NOCLDSTOP 进程忽略子进程产生的任何SIGSTOP、SIGTSTP、SIGTTIN和SIGTTOU信号
SA_RESTART 另重启的系统调用起作用
SA_ONESHOT \ SA_RESETHAND 自定义信号只执行一次,在执行完毕后恢复信号的系统默认动作

(3)使用实例

/* signal.c */
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

void my_func(int signal)
{
    if(signal == SIGINT)
    {
        printf("get SIGINT.\n");
    }
    if(signal == SIGQUIT)
    {
        printf("get SIGQUIT.\n");
    }
}

int main()
{
    printf("Waiting for any signal...\n");

    signal(SIGINT,my_func);
    signal(SIGQUIT,my_func);
    pause();
    exit(0);
}
/* sigaction.c */
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void my_func(int signal)
{
    if(signal == SIGINT)
    {
        printf("get SIGINT.\n");
    }
    if(signal == SIGQUIT)
    {
        printf("get SIGQUIT.\n");
    }
}

int main()
{
    struct sigaction action;

    action.sa_handler = my_func;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;

    printf("Waiting for any signal...\n");

    sigaction(SIGINT,&action,0);
    sigaction(SIGQUIT,&action,0);
    pause();
    exit(0);
}

2.信号集函数组


未完待续

猜你喜欢

转载自blog.csdn.net/q1449516487/article/details/81484487