04进程间的通信-信号

1、信号的种类:

每个信号名都以SIG开头,信号名的定义都在头文件<signal.h>中,使用命令查看所有信号 kill -l
在这里插入图片描述
Linux系统中有许多信号,其中前面31个信号都有一个特殊的名字,并且对应着一个特殊的事件,这些信号都是从Unix系统继承下来的,他们还有个名称叫不可靠信号,特点如下:

  • 1、非实时信号不排队,信号的响应会相互嵌套(其他的信号可以打断正在执行的信号)
  • 2、如果目标进程没有及时响应非实时信号,那么随后到达的该信号会被丢弃
  • 3、每一个非实时信号都对应一个事件,当事件发生时,将产生这个信号
  • 4、如果进程的挂起信号中含有实时和非实时信号,那么进程优先响应实时信号并且是从大到小一次响应,而非实时信号没有固定的次序

后面的31个信号(从34到64)是Linux系统新增的实时信号,也被称为可靠信号,特点如下:

  • 1、实时信号的响应次序按接受顺序排队,不嵌套
  • 2、即使相同的实时信号被同时发送对此,也不会被丢弃,而会依次挨个响应
  • 3、实时信号没有特殊的事件与之对应

在这里插入图片描述
在这里插入图片描述

对以上信号需要注意的是:

  • 1、表中罗列出来的,在X86、PowerPC和Arm平台下是有效的。但是其他的平台的值也许和这个表里面的值不一样。
  • 2、备注中注明的事件发生时会产生相应的信号,但并不是说该信号的产生就一定发生了这个事件。事实上,任何进程都可以使用函数keill()来产生任何信号。
  • 3、信号:SIGKILLSIGSTOP是两个特殊的信号,他们不能被忽略、阻塞或则捕捉,只能按照缺省的动作来响应信号。除了这两个信号的其他信号,接收信号的目标进程按照下面的顺序作出反应:
    • A)、如果该信号被阻塞,那么将信号挂起,不对其做任何处理,等到为其做解除阻塞为止。否则进入B)

    • B)、如果该信号被捕捉,判断捕捉的类型:否则进入C)

      • B1)、如果设置了响应函数,那么执行响应函数
      • B2)、如果设置为丢弃,那么忽略该信号
    • C)、执行该信号的缺省动作

2、相关函数

kill函数-发送信号

在这里插入图片描述

signal函数-捕获信号

在这里插入图片描述

练习:父进程发送信号,子进程接收信号去执行响应函数

#include "stdio.h"
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

void func(int a)
{
    printf("响应函数-》捕捉信号:%d", a);
}

int main()
{
    int pid = fork();
    int i = 0;

    if(pid > 0)
    {
        while(1)
        {
            printf("父进程%d\n", i++);
            sleep(5);//每隔五秒,父进程向子进程发送4号命令
            printf("执行4号命令 \n " );
            kill(pid, 4);
        }
    }
    else if(pid == 0)
    {
        signal(4, func);//子进程接收到4号命令之后开始执行func函数
        while(1)
        {
            printf("子进程\n");
            sleep(1);
        }
    }
    else
    {
        perror("Pid");
    }
}

raise函数

在这里插入图片描述

pause函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

练习:
尝试设置所有信号阻塞,然后一次性全部解除,留意处理的先后顺序

#include "stdio.h"
#include <signal.h>
#include "stdlib.h"
#include <unistd.h>
void func(int argc)
{
    printf("响应函数:%d\n", argc);
}

int main(void)
{
    //设置信号的响应函数
    signal(3, func);
    signal(4, func);

    //初始化信号集
    sigset_t set;
    sigemptyset(&set);//将信号集情况
    sigaddset(&set, 3);//将一个指定的信号添加到信号集中
    sigaddset(&set, 4);//将一个指定的信号添加到信号集中

    //设置阻塞信号集中的信号
    sigprocmask(SIG_SETMASK, &set, NULL);

    //给自己发送信号
    raise(3);
    raise(4);

    sleep(2);//延时两秒解除阻塞

    //解除阻塞
    sigprocmask(SIG_UNBLOCK, &set, NULL);

    return 0;
}

3、给进程发送信号,并携带额外的数据

在这里插入图片描述
额外携带的数必须是一个联合体

union sigval
{
	int sigval_int;
	void * sigval_prt
};

利用siqqueue()发送信号的同时可以携带一个整形数据或者一个void型指针,目标进程想要想要额外获得这些数据
在这里插入图片描述
以上的act参数比较复杂,其类型结构体struct sigaction如下:

stract sigaction
{
	void (*sa_handler)(int);
	void (*sa_sigaction)(int, siginfo_t *, void *);
	sigset_t sa_mask;
	int sa_flags;
	void (*sa_restorer)(void);
}

void (*sa_sigaction)(int, siginfo_t *, void *);函数

  • 第一个参数:int类型,就是触发该函数的信号

  • 第二个参数:siginfo_t型指针,指向如下结构体:

      siginfo_t
      {
      	int si_signo; // si_signo、si_errno 和 si_code 对所有信号都有效
      	int si_errno;
      	int si_code;
      	int si_trapno; // 以下成员只对部分情形有效,详情见下面的注解
      	pid_t si_pid;
      	uid_t si_uid;
      	int si_status;
      	clock_t si_utime;
      	clock_t si_stime;
      	sigval_t si_value;
      	int si_int; // 整型参数
      	void *si_ptr; // 指针参数
      	int si_overrun;
      	int si_timerid;
      	void *si_addr;
      	long si_band;
      	int si_fd;
      	short si_addr_lsb;
      } 
    

示例:

#include "stdio.h"
#include <signal.h>
#include <strings.h>
#include <sys/types.h>
#include <unistd.h>

void func(int sig, siginfo_t *info, void *arg)
{
    printf("sig:%d, info:%s", sig, (char *)info->si_ptr);
}

int main(void)
{

    //定义ACT结构体并设置其信息
    struct sigaction act;
    bzero(&act, sizeof(act));//清空结构体
    act.sa_sigaction = func;
    act.sa_flags |= SA_SIGINFO;

    //设置捕获的信号响应函数
    if(sigaction(3, &act, NULL))
    {
        printf("设置捕获失败\n");
        return 0;
    }

    //设置携带参数
    union sigval value;
    value.sival_int = 1024;
    value.sival_ptr = "Hello Even";

    //发送信号
    pid_t pid = getpid();
    if(sigqueue(pid, 3, value))
    {
        printf("发送信号失败\n");
        return 0;
    }
    printf("发送信号成功\n");
    sleep(1);

    return 0;

}

这段代码的作用是在当前进程内设置信号捕获函数,并向自己发送信号3(SIGQUIT),同时携带附加信息。当接收到信号后,会调用信号捕获函数 func 来处理,并输出信号编号和信号携带的信息。这样实现了进程内部的通信。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值