进程通信——信号

信号的概念及类型

信号是Linux系统中用于进程之间通信或操作的一种机制,信号可以在任何时候发送给某一进程,而无须知道该进程的状态。如果该进程并未处于执行状态,则该信号就由内核保存起来,直到该进程恢复执行并传递给他为止。如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。
收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:

  1. 第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。
  2. 第二种方法是,忽略某个信号,对该信号不做任何处理,就象未发生过一样。
  3. 第三种方法是,对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得进程终止。进程通过系统调用signal来指定进程对某个信号的处理行为。

在linux下我们可以使用 kill -l 命令查看当前系统支持的信号,需要注意的是不同的系统支持的信号是不一样的:

在上面的信号中,我们常见或用到的是:

信号编号 信号名 信号说明 默认动作
2 SIGINT Ctrl+c按键终止程序运行的信号 程序终止
4 SIGILL 非法的指令 程序终止
7 SIGBUS 运行非本CPU相关编译器编译的程序 程序终止
9 SIGKILL 强制杀死程序信号,任何程序都不可以捕捉该信号 程序终止,不可被捕捉
10 SIGUSR1 用户自定义信号1 程序终止
11 SIGSEGV 段错误系统给程序发送的信号 程序终止
12 SIGUSR2 用户自定义信号2 程序终止
13 SIGPIPE 管道破裂信号 程序终止
14 SIGALRM alarm()系统调用发送的信号 程序终止
15 SIGTERM kill命令默认发送的信号,默认动作是终止信号 程序终止
17 SIGCHLD 子进程退出信号 忽略该信号

信号的安装

linux下主要有两个函数实现信号的安装:signal()、sigaction()。
1、signal()

#include <signal.h>  //头文件

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

函数说明:注册一个信号捕捉函数
参数说明:① 第一个参数signum指定信号的值。②第二个参数handler制定针对前面函数的处理,可忽略该信号(参数设为SIG_IGN),可以采用系统默认方式处理信号(参数设为SIG_DFL),也可以自己实现处理方式(参数指定一个函数地址),即当指定信号到底时,就会跳转到handler指定的函数执行。
返回值:signal()调用成功时,返回最后一次为安装信号signum而调用signal()时的handler值,失败返回SIG_ERR。

2、sigaction()

#include <signal.h>

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

函数说明:函数可对发来的信号做排队处理(通常在Linux用其来注册一个信号的捕捉函数)
参数说明:①第一个参数signum为要操作的信号,可以为SIGKILL及SIGSTOP外的任何一个特定有效的信号(这两个信号定义自己的处理函数,导致安装错误)。②第二个参数act为指向sigaction结构体的一个指针,在结构体中指定了对信号新的处理方式,当为NULL时,进程会按缺省的方式对信号处理。③第三个参数oldact参数输出先前信号的处理方式,也可指为NULL。若第二个参数和第三个参数都为NULL则该函数用来检查信号的有效性。

struct 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);
}

 sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数。
 sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置
 sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。

①SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
②SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
③SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。若SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号。

signal()代码示例

/*********************************************************************************
 *      Copyright:  (C) 2020 makun<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  signal.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2020年03月20日)
 *         Author:  makun <[email protected]>
 *      ChangeLog:  1, Release initial version on "2020年03月20日 14时19分06秒"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
int     g_sigstop = 0;

void signal_stop(int signum)
{

    if( SIGTERM == signum )
    {
        printf("SIGTERM signal detected\n");
    }
    else if( SIGALRM == signum )
    {
        printf("SIGALRM signal detected\n");
        g_sigstop = 1;
    }
}
void signal_code(int signum)
{
    if(SIGBUS == signum)
    {
        printf("SIGBUS signal detected\n");
    }
    else if(SIGILL == signum)
    {
        printf("SIGILL signal detected\n");
    }
    else if(SIGSEGV == signum)
    {
        printf("SIGSEGV signal detected\n");
    }
    exit(-1);
}

int main (int argc, char **argv)
{
    char    *ptr=NULL;
    

    signal(SIGTERM,signal_stop);
    /*安装SIGTERM信号,SIGTERM这是由kill(1)命令发送的系统默认终止信号,由于该信号是由应用程序捕获的使用SIGTERM也让程序有机会在退出之前做好清理工作,从而优雅的终止*/
    signal(SIGALRM,signal_stop);
    /*安装SIGALRM,SIGALRM,当用alarm函数设置的定时器超时时,产生此信号,若由setitime(2)函数设置的间隔时间已经超时时,也产生此信号。*/ 
    signal(SIGBUS,signal_code);
    /*指示一个实现定义的硬件故障,当出现某些类型的的内存故障时,实现常常产生此种信号*/
    signal(SIGILL,signal_code);
    /* 此信号表示进程已执行一条非法硬件指令 */
    signal(SIGSEGV,signal_code);
    /*指示进程进行了一次无效的内存引用(通常说明程序有错,比如访问了一个未初始的指针)*/ 


    printf("program start running for 20 seconds...\n");
    alarm(20);//程序执行到这会打印SIGALRM signal detected
    while(!g_sigstop)
    {
        ;
    }
    printf("program start stop running...\n");
    printf("invalid pointer operator will raise SIGSEGV signal\n");

    *ptr = 'h';//程序执行到这会打印SIGSEGV signal detected

    return 0;
}

代码运行结果:
在这里插入图片描述

sigaction()代码示例

/*********************************************************************************
 *      Copyright:  (C) 2020 makun<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  sigaction.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2020年03月20日)
 *         Author:  makun <[email protected]>
 *      ChangeLog:  1, Release initial version on "2020年03月20日 14时19分06秒"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>

void sig_usr(int signum)
{

    if( SIGUSR1 == signum )
    {
        printf("SIGUSR1 signal detected\n");
    }
    else if( SIGUSR2 == signum )
    {
        printf("SIGUSR2 signal detected\n");
        
    }
}

    

int main (int argc, char **argv)
{
    char    buf[512];
    int     n;
    
    struct  sigaction   sa_usr; 
    sa_usr.sa_flags =0;
    sa_usr.sa_handler = sig_usr;    //信号处理函数

    sigaction(SIGUSR1, &sa_usr, 0);
    sigaction(SIGUSR2, &sa_usr, 0);


     printf("My PID is %d\n", getpid());
         
     while(1)
     {
         if((n = read(STDIN_FILENO, buf, 511)) == -1)
         {
             if(errno == EINTR)
             {
                 printf("read is interrupted by signal\n");
             }
         }
         else
         {
             buf[n] = '\0';
             printf("%d bytes read: %s\n", n, buf);
         }
     }

     return 0;
}
  1. 第一次运行结果:进程在这里阻塞,等待另一个进程发送信号

在这里插入图片描述

  1. 进程2给进程1-9709发送信号

在这里插入图片描述 3.进程1收到信号后在这里插入图片描述

发布了28 篇原创文章 · 获赞 44 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/makunIT/article/details/104873497
今日推荐