Linux系统编程系列之进程间通信(IPC)-信号

一、什么是信号

        信号是进程间通信的一种方式,它是异步通信的。而异步的意思就是不同步,事件的发生和处理没有协同。

二、信号的特性

             Linux/Unix系统下,信号总共分成两大类,一类是最常用的标准信号,另一类是后面的引入的实时信号。一共有62个信号,前31个是标准信号,后面31个是实时信号。注意没有32,33号。

Linux系统编程系列之进程间通信(IPC)-信号_第1张图片

        1、标准信号

        (1)不排队,信号的响应会相互嵌套。

        (2)如果目标进程没有及时响应,那么随后到达的相同信号将会被丢弃。

        (3)每个信号都对应一个系统事件(除了SIGUSR1和SIGUSR2),当这个事件发生时,将产生这个信号。

        (4)在进程的挂起信号中,进程会优先响应实时信号。

        2、实时信号

        (1)实时信号的响应次序按接收顺序排队。如果收到相同的信号的则不会嵌套,但是如果是不同的信号则会导致嵌套。

        (2)即使相同的实时信号被同时发送多次,也不会被丢弃,而会依次按个响应。

        (3)实时信号没有特殊的系统事件与之对应。

        (4)实时信号在挂起队列中信号值越大,优先级越高。

三、信号的生命周期

        所谓信号的生命周期,指的是信号从产生到被响应完毕的整个过程,这个过程可被描述为:

Linux系统编程系列之进程间通信(IPC)-信号_第2张图片

        1、信号的产生

        信号既可以由特定的事件产生(比如发生了内存访问异常导致产生信号SIGSEGV),也可以由用户主动发起(比如调用了kill()函数),不管是哪种方式产生的信号,其本质都是触发了内核的信号发生器,并向特定进程(即目标进程)传递的过程。

// kill()函数接口介绍
#include 
#include 

int kill(pid_t pid, int sig);

参数:
    pid: 要接收信号的进程号
    sig: 信号的编号
返回值:成功返回0, 失败返回-1.

        2、信号的挂起

        每个进程都保留有一个挂起信号集,所有被发送到这个进程的信号首先被放入到这个信号集(进程处于非执行状态),挂起信号集存储了进程的待处理信号,这些信号必须要等到进程被系统调度(占用CPU执行的时候),真正执行的时候才能被进一步响应。

        3、信号的响应(处理)

        信号的响应总共有如下四种方式:

        (1)屏蔽(阻塞):延缓对信号的响应,直到解除对该信号的屏蔽为止。

        (2)捕捉:执行一个预先设置的与信号相关联的响应函数。

        (3)默认:按信号默认的情况处理。

        (4)忽略:直接丢弃该信号。

注意:系统中9号,19号信号是不允许被挂起或捕获的,只允许对他们进行默认处理

四、信号的响应(处理)        

       1、信号的捕捉

        给函数指定关联函数的接口是:        

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

// 翻译过来就是
void (*signal(int sig, void (*func)(int)))(int);

// 函数名为:signal
// 参数为:
    int sig, 信号的编号
    void (*func)(int), 关联的函数,函数的参数是int, 返回值是void
// 返回值:void (*)(int);

        2、信号的默认处理

        如果程序没有对信号做任何预先准备,那么当信号到达时,则会按照信号的默认规则进行响应,具体默认规则可使用如下命令查阅:

man 7 signal

Linux系统编程系列之进程间通信(IPC)-信号_第3张图片

     列表中的Action 一列就是系统对信号的默认处理规则,默认规则如下:

        (1)Term:中断目标进程

        (2)Core:中断目标进程,且产生核心转储文件core

        (3)Stop:暂停目标进程,直到收到信号SIGCONT

        (4)Cont:恢复目标进程运行

        (5)Ign:忽略信号

Linux系统编程系列之进程间通信(IPC)-信号_第4张图片

          3、信号的忽略

信号的忽略就是直接将收到的信号丢弃

signal(SIGINT, SIG_IGN);

          4、信号的屏蔽

屏蔽信号实际上就是暂缓对信号的响应,采用如下函数进行对信号的屏蔽

#include 

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

// 参数解析:
1、how:操作命令字,比如阻塞、解除阻塞等
    SIG_BLOCK:阻塞set中的信号(原有正在阻塞的信号保持阻塞)
    SIG_SETMASK:阻塞set中的信号(原有正在阻塞的信号自动解除)
    SIG_UNBLOCK:解除set中的信号

2、set:当前要操作的信号集
3、oldset:若为非空,则将原有阻塞信号集保留到该oldset中
注意:该函数的操作参数不是单个信号,而是信号集,
这意味着我们可以同时对多个信号设置阻塞或者解除阻塞


// 信号集操作函数组
int sigemptypset(sigset_t *set);    // 清空信号集set
int sigfillset(sigset_t *set);    // 将所有信号加入信号集set中
int sigaddset(sigset_t *set, int signum); // 将信号signum添加到信号集set中
int sigdelset(sigset_t *set, int signum); // 将信号signum从信号集set中剔除
int sigsimember(const sigset_t *set, int signum); // 测试信号signum是否在信号集set中

五、案例

        1、信号的捕捉

// 信号的捕捉操作示例

#include 
#include 
#include 

void func(int sig)
{
    printf("call func\n");
    printf("catch signal:%d\n", sig);
}

int main(int argc, char *argv[])
{
    // 设置信号响应函数,捕捉34号信号,当捕获到34号信号时,执行func函数
    // 注意设置后,不会执行func函数,signal()应该尽量写在前面
    signal(34, func);   

    printf("my pid is %d\n", getpid()); // 打印本进程ID号,方便从命令行发送信号
    printf("set signal 34...\n");

    while(1)
    {
        sleep(1);
    }
    
    return 0;
}

 

Linux系统编程系列之进程间通信(IPC)-信号_第5张图片

Linux系统编程系列之进程间通信(IPC)-信号_第6张图片

// 信号的捕捉操作示例

#include 
#include 
#include 

void func(int sig)
{
    printf("call func\n");
    printf("catch signal:%d\n", sig);
}

int main(int argc, char *argv[])
{
    printf("my pid is %d\n", getpid()); // 打印本进程ID号,方便从命令行发送信号
    printf("set signal 34...\n");

    while(1)
    {
        sleep(1);
    }

    // 如果放在这里注册信号响应函数,也能捕捉到信号,但是会有意想不到的结果
    signal(34, func);  
    
    return 0;
}

Linux系统编程系列之进程间通信(IPC)-信号_第7张图片

Linux系统编程系列之进程间通信(IPC)-信号_第8张图片

        2、信号的屏蔽

// 信号的阻塞(屏蔽)操作示例

#include 
#include 
#include 
#include 

void func(int sig)
{
    printf("call func\n");
    printf("catch signal %d\n", sig);
}

int main(int argc, char *argv[])
{
    // 注册信号响应函数
    signal(34, func);
    printf("my pid is %d\n", getpid());

    // 设置信号阻塞,阻塞34号信号
    sigset_t set;
    sigemptyset(&set);  // 清空信号集
    sigaddset(&set, 34);    // 把34信号添加到信号集中
    
    if(sigprocmask(SIG_SETMASK, &set, NULL) == -1)  // 把信号集中的所有信号设置为阻塞
    {
        perror("设置阻塞失败\n");
    }

    sleep(10);   // 10秒内不会对34号进行处理

    if(sigprocmask(SIG_UNBLOCK, &set, NULL))   // 解除信号集中的阻塞信号
    {
        perror("设置阻塞失败\n");
    }

    while(1)
    {
        sleep(1);
    }



    return 0;
}

Linux系统编程系列之进程间通信(IPC)-信号_第9张图片

Linux系统编程系列之进程间通信(IPC)-信号_第10张图片

六、总结

        信号是进程间异步通信的方式,Linux系统下有62个信号,1~31号是标准信号,34~64是实时信号,对信号的响应方式有四种,分别是屏蔽(阻塞),捕捉,默认处理和忽略。注册信号响应函数的语句应该放在主函数体内前面。

你可能感兴趣的:(Linux,C语言程序设计,linux,c语言)