【Linux学习】进程间通信 (2) —— 信号

下面是有关进程通信中信号的相关介绍,希望对你有所帮助!

小海编程心语录-CSDN博客

目录

1. 信号 

1.1 概念 

1.2 信号的产生 

1.3 信号的处理方式 

2. 函数 

2.1 kill() 函数 

2.2 signal()函数 

2.3 sigaction()函数 

2.4 sigprocmask()函数 

2.5 sigqueue()函数 


1. 信号 

1.1 概念 

信号是进程间通信 UNIX IPC 中的其中一种通信方式,信号是事件发生时对进程的通知机制,也可以把它称为软件中断。大多数情况下,是无法预测信号达到的准确时间,所以,信号提供了一种处理异步事件的方法—— kill 命令查看所有信号。

ps -aux 指令可以看执行语句的pid等信息

1.2 信号的产生 

目的信号的目的是用来通信,当发生某种情况 下,通过信号将情况“告知”相应的进程,从而达到同步、通信的目的

信号由谁发出:

  1. 硬件发生异常,即硬件检测到错误条件并通知内核,随即再由内核发送相应的信号给相关进程。(异常包括执行一条异常的机器语言指令,诸如,除数为 0、数组访问越界导致引用了无法访问的内存区域等,这些异常情况都会被硬件检测到)
  2. 用于在终端下输入了能够产生信号的特殊字符。 譬如在终端上按下 CTRL + C 组合按键可以产生中断信号(SIGINT),通过这个方法可以终止在前台运行的进程
  3. 进程调用 kill()系统调用可将任意信号发送给另一个进程
  4. 用户可以通过 kill 命令将信号发送给其它进程,譬如在终端下执行"kill -9 xxx"来杀死 PID 为 xxx 的进程。 kill 命令其内部的实现原理便是通过 kill()系统调用来完成的。
  5. 发生了软件事件,即当检测到某种软件条件已经发生。(进程所设置的定时器 已经超时、进程执行的 CPU 时间超限、进程的某个子进程退出等等)

1.3 信号的处理方式 

当进程接收到内核或用户发送过来的信号之后,根据具体信号可以采取不同的处理方式:忽略信号、捕获信号或者执行系统默认操作。 Linux 系统提供了系统调用signal()和 sigaction()两个函数用于设置信号的处理方式

  1. 忽略信号:当信号到达进程后,该进程并不会去理会它、直接忽略,就好像是没有出现该 信号,信号对该进程不会产生任何影响。事实上,大多数信号都可以使用这种方式进行处理,但有 两种信号却决不能被忽略,它们是 SIGKILL 和SIGSTOP,因为它们 向内核和超级用户提供了使进程终止或停止的可靠方法
  2. 捕获信号:当信号到达进程后,执行预先绑定好的信号处理函数。Linux 系统提供了 signal()系统调用可用于注册信号的处理函数
  3. 执行系统默认操作:进程不对该信号事件作出处理,而是交由系统进行处理,每一种信号都会有其 对应的系统默认的处理方式,对大多数信号来说,系 统默认的处理方式就是终止该进程

 

2. 函数 

2.1 kill() 函数 

//函数原型
#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

//pid:接收信号的进程PID,称为目标进程
//sig:信号的编号

2.2 signal()函数 

//函数原型
#include <signal.h>

typedef void (*sig_t)(int);
sig_t signal(int signum, sig_t handler);

/*
signum: 此参数指定需要进行设置的信号,可使用信号名或信号的数字编号

handler: sig_t 类型的函数指针,指定要关联的响应函数,也可以设 置为 SIG_IGN 或 SIG_DFL, SIG_IGN 表示此进程需要忽略该信号, SIG_DFL 则表示设置为系统默认操作

返回值: 此函数的返回值也是一个 sig_t 类型的函数指针,成功情况下的返回值则是指向在此之前的信 号处理函数;如果出错则返回 SIG_ERR,并会设置 errno。
*/

signal()函数是 Linux 系统下设置信号处理方式最简单的接口, 可将信号的 处理方式设置为捕获信号、 忽略信号以及系统默认操作

示例代码 

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

typedef void (*sig_t)(int);
sig_t signal(int signum, sig_t handler);

void sig_handler(int sig)
{
    printf("Received signal: %d\n", sig);
}

int main(int argc, char *argv[])
{
    sig_t ret = NULL;
    ret = signal(SIGINT, sig_handler);
    if (SIG_ERR == ret)
    {
        perror("signal error");
        exit(-1);
    }
    
    while(1)
    {

    }
    exit(0);
}

2.3 sigaction()函数 

//函数原型
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
/*
signum: 需要设置的信号,除了 SIGKILL 信号和 SIGSTOP 信号之外的任何信号。
act: act 参数是一个 struct sigaction 类型指针,指向一个 struct sigaction 数据结构,该数据结构描述了信
号的处理方式,如果参数 act 不为 NULL,则表示需要为信号设置新的处理方式;如
果参数 act 为 NULL,则表示无需改变信号当前的处理方式。
oldact: oldact 参数也是一个 struct sigaction 类型指针,指向一个 struct sigaction 数据结构。如果参数
oldact 不为 NULL, 则会将信号之前的处理方式等信息通过参数 oldact 返回出来;如果无意获取此类信息,
那么可将该参数设置为 NULL。
返回值: 成功返回 0;失败将返回-1,并设置 errno
*/

除了signal()之外, sigaction()系统调用是设置信号处理方式的另一选择,虽然sigaction()更为复杂,但 sigaction()更具灵活性以及移植性。

sigaction()允许单独获取信号的处理函数而不是设置,并且还可以设置各种属性对调用信号处理函数时 的行为施以更加精准的控制

2.4 sigprocmask()函数 

 内核为每一个进程维护了一个信号掩码,当进程接收到一个属于 信号掩码中定义的信号时,该信号将会被阻塞、无法传递给进程进行处理。

//函数原型
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

函数参数和返回值含义如下:

  1. how: 参数 how 指定了调用函数时的一些行为。
  2. set: 将参数 set 指向的信号集内的所有信号添加到信号掩码中或者从信号掩码中移除;如果参数 set 为 NULL,则表示无需对当前信号掩码作出改动。
  3. oldset: 如果参数 oldset 不为 NULL,在向信号掩码中添加新的信号之前,获取到进程当前的信号掩码, 存放在 oldset 所指定的信号集中;如果为 NULL 则表示不获取当前的信号掩码
  4. 返回值: 成功返回 0;失败将返回-1,并设置 errno

示例代码 

实现对SIGINT信号的屏蔽

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

static void sig_handler(int sig)
{
    printf("Received signal: %d\n", sig);
}
int main(void)
{
    struct sigaction sig = {0};
    sigset_t sig_set;
    // 注册信号处理函数 
    sig.sa_handler = sig_handler;
    sig.sa_flags = 0;

    if (-1 == sigaction(SIGINT, &sig, NULL))
        exit(-1);

    // 信号集初始化 
    sigemptyset(&sig_set);
    //添加SIGINT信号
    sigaddset(&sig_set, SIGINT);

    // 向信号掩码中添加信号 
    if (-1 == sigprocmask(SIG_BLOCK, &sig_set, NULL))
        exit(-1);

    printf("正在休眠,此时对SIGINT信号屏蔽\n");
    sleep(5);
    printf("休眠结束\n");
    // 从信号掩码中移除添加的信号 
    if (-1 == sigprocmask(SIG_UNBLOCK, &sig_set, NULL))
        exit(-1);

    while(1)
    {

    }
    exit(0);
}

2.5 sigqueue()函数 

该函数的功能与kill一样,都是向指定进程发送信号,但sigqueue在发送信号的同时会携带很多额外信息。

//函数原型
#include <signal.h>

int sigqueue(pid_t pid, int sig, const union sigval value);

函数参数和返回值含义如下:

  1. pid: 指定接收信号的进程对应的 pid,将信号发送给该进程
  2. sig: 指定需要发送的信号
  3. value: 参数 value 指定了信号的伴随数据, union sigval 数据类型
  4. 返回值: 成功将返回 0;失败将返回-1,并设置 errno

如果喜欢请不吝给予三连支持!

小海编程心语录-CSDN博客

 

  • 40
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值