Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Linux进程通信——信号

Linux进程通信——信号

作者头像
zy010101
发布于 2019-05-25 11:57:01
发布于 2019-05-25 11:57:01
2.4K00
代码可运行
举报
文章被收录于专栏:程序员程序员
运行总次数:0
代码可运行

版权声明:本文为博主原创文章,转载请注明博客地址: https://cloud.tencent.com/developer/article/1433356

信号是在软件层面对中断机制的一种模拟,信号的出现使得进程直接的通信不在是被动的,不在向之前那样,read()操作往往需要等待write()操作结束。因为信号是对中断的一种模拟。既然是中断,那么它的发生就是不确定。就不会发生一个进程阻塞在这里等待另一个进程执行的结果。这样的异步性通信机制无疑是更加强大的。

在终端输入kill -l可以查看当前系统所支持的所有信号。(我这个是Ubuntu

可以看到有64个信号,其中有两个较为特殊的信号是SIGRTMIN和SIGRTMAX。Linux下的通信机制是遵从POSIX标准的。34号信号SIGRTMIN信号之前的是早期UNIX操作系统的。它们是不可靠的信号。它的主要问题是:进程每次处理信号后,会设置对该信号的默认处理动作,有时候我们不想让他这么处理了(按照默认处理),这时候就需要调用signal()函数重新安装一次信号。这样会形成新的默认动作。还有更加讨厌的是,信号有可能会丢失。

Linux对不可靠信号做了一些改进,现在的主要问题变成了“信号会丢失”。

后来POSIX仅仅只对可靠信号做了标准化。信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号。可靠信号它不会丢失。

可靠信号都是实时信号,不可靠信号都是非实时信号。可靠信号都支持队列处理,不可靠信号不支持队列处理。在UNIX时代就定义好了前面的不可靠信号的功能,而后来增加的可靠信号是让用户自定义使用的。

信号处理的三种方式:

  1. 忽略信号:对信号不做任何处理,就当做没发生任何事情一样。(SIGKILL和SIGSTOP这两个不能忽略)
  2. 捕捉信号:定义信号处理函数,当信号发出的时候,执行相应的操作。(这个和Qt的信号槽差不多)
  3. 执行默认动作:Linux对每一个信号都规定了默认操作(可靠信号的默认操作是进程终止)。

发送信号

发送信号的函数有kill(),raise(),sigqueue(),alarm(),setittimer(),abort()。常用的是kill()。它们依赖的头文件是#include<signal.h>和#include<sys/types.h>

函数原型:int kill(pid_t pid,int sig);

函数功能:用来将sig所指定的信号发送到pid所指定的进程。

pid有下面几种情形,分别对应于不同情况下应用。

  • pid > 0:把信号传递到进程ID为pid的进程
  • pid == 0:把信号传送给当前进程所在组的所有进程
  • pid == -1:将信号以广播的形式传送给系统内所有进程
  • pid < -1: 讲信号传递给进程组识别码为pid绝对值的所有进程

函数执行成功返回0,否则返回-1.

测试代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<signal.h>
#include<wait.h>
#include<unistd.h>

int main()
{
    pid_t pid;
    int statu;
    pid = fork();
    if(0 == pid)
    {
        printf("son\n");
        sleep(5);
        printf("I am son!\n");
        exit(0);
    }
    if(0 < pid)
    {
        sleep(2);
        printf("father\n");
        kill(pid,SIGABRT);      //SIGABRT是终止子进程
        wait(NULL);
        printf("My son GG\n");
        exit(0);
    }
    return 0;
}

让子进程先执行,打印出son。然后让子进程挂起。轮到父进程执行,父进程执行到kill()函数的时候给子进程发了个SIGABRT信号,让子进程终止了。然后wait()回收子进程,打印My son GG.

执行结果如下:

可以看到,子进程收到SIGABRT信号后,终止了。没有向屏幕打印I am son.关于信号的详解,看这里:https://cloud.tencent.com/developer/article/1433358

上面的kill函数发送的信号是不可靠信号,它执行默认操作。即:终止进程。如果我们需要自定义信号处理方式,那么就需要安装信号。Linux安装信号主要由signal()和sigaction()完成。signal是在可靠信号系统调用的基础上实现的,是库函数。

signal()的原型很复杂,我们还是从signal.h这个头文件来看一下吧!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
extern __sighandler_t signal (int __sig, __sighandler_t __handler)

__THROW;

可以看到signal有两个参数,一个是信号值,另一个我们再来看看

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef void (*__sighandler_t) (int);

另一个是这样的一个函数指针变量,那么说明__handler代表了一个函数的入口地址(实际就是函数)。另外,这个函数指针指向的函数需要一个int类型的参数。signal函数的返回值也是一个函数指针。

注意:__handler如果不是函数指针,它只能是SIG_IGN或者是SIG_DFL.

SIG_IGN:忽略参数指定的信号。(忽略该信号)

SIG_DFL:将参数指定的信号重新设置为内核默认的处理方式。

返回值:signal函数本身在成功时返回NULL,它的参数__handler则会返回处理信号的函数的地址(函数指针)。失败返回:SIG_ERR.

所以这就要求自定义的信号处理函数的函数原型是这样的:

void 函数名(int 参数名);即:函数必须有一个int类型的参数。

signal()函数只是定义了将指定信号传送到指定进程。还需要一个用于捕捉信号的函数。在Linux下pause()函数用于捕捉信号,如果没有信号发生,pause函数将会一直等待。直到有信号发生。

函数原型:int pause();

当pause函数捕捉到信号的时候返回-1(注意不是捕捉到的信号的值)。

测试程序如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<wait.h>

//自定义的信号处理函数
void My_Fun(int sig)
{
    if(SIGRTMIN == sig)
    {
        printf("MIN!\n");
    }
    if(SIGRTMAX == sig)
    {
        printf("MAX!\n");
    }
}

int main()
{
    //注册信号处理函数
    signal(SIGRTMIN,My_Fun);
    signal(SIGRTMAX,My_Fun);
    //挂起10s
    sleep(3);
    //发出信号
    kill(getpid(),SIGRTMIN);    //getpid()函数用于获取当前进程的pid.
    kill(getpid(),SIGRTMAX);
    return 0;
}

输出结果如下:

这样就完成了自定义信号的使用。使用自定义信号有两个关键点。一是必须注册自定义信号的处理函数,二是必须发送自定义信号。怎么样发送自定义信号由你自己来定义,这为程序设计带来了极大的便利。比如上面我们只是直接了当的发送两个信号。你也可以使当满足一定条件的时候才发送信号。比如下面这样。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<wait.h>

//自定义的信号处理函数
void My_Fun(int sig)
{
    if(SIGRTMIN == sig)
    {
        printf("MIN!\n");
    }
    if(SIGRTMAX == sig)
    {
        printf("MAX!\n");
    }
}

int main()
{
    //注册信号处理函数
    signal(SIGRTMIN,My_Fun);
    signal(SIGRTMAX,My_Fun);
    //发出信号
    char c;
    while(1)
    {
        scanf("%c",&c);
        getchar();                      //吸收回车
        if('a' == c)
        {
            kill(getpid(),SIGRTMIN);    //getpid()函数用于获取当前进程的pid.
        }
        else
        {
            kill(getpid(),SIGRTMAX);
        }
    }

    return 0;
}

运行结果如下:

这样就实现了发送信号的控制。可以想象,键盘,鼠标等发送的信号很有可能就是被系统采取这样的方式处理的。

另外一个函数是sigaction()函数。

函数原型:int sigaction(int sig,const struct sigaction *newact,const sigaction *oldact);

函数功能:sigaction函数根据参数sig指定的信号来处理信号。参数可以是SIGKILL和SIGSTOP以为的其他信号。newact是新的信号处理方式,oldact是旧的信号处理方式。这个结构体包含如下成员

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct sigaction()
{
    void(*sa_handler)(int);        //指向信号处理函数的函数指针
    sigset_t sa_mask;              //用来设置处理该信号的时候暂时屏蔽sa_mask指定的信号
    int sa_flags;                  //设置信号处理的方式
    int (*sa_restorer)(void);      //输出参数, 指向struct sigaction 结构的指针
}

在C语言里结构体的名字可以和函数名相同。(其实是C语言的结构体名称应该是struct xxx。带上关键字才是真正的结构体名)。

信号集

信号集被定义为一种数据类型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef struct
{
    unsigned long sig[_NSIG_WORDS];
}sigset_t;

信号集主要配合一下的信号阻塞函数来使用。

  1. sigemptyset()函数: 函数原型:int sigemptyset(sigset_t *set); 函数功能:用来将set信号集给初始化并清空。
  2. sigaddset()函数: 函数原型:int sigaddset(sigset_t *set,int sig); 函数功能:将参数sig指定的信号加入信号集set。
  3. sigfillset()函数: 函数原型:int sigfillset(sigset_t *set); 函数功能:把所有信号加入到set中。
  4. sigdelset()函数: 函数原型:int sigdelset(sigset_t *set,ing sig); 函数功能:将参数sig指定的信号从set中删除

使用信号注意的问题:

  1. 注意信号是否会丢失这个问题,尽量使用可靠信号。
  2. 注意信号的可移植性,POSIX标准指定的信号函数和信号
  3. 信号处理函数应当是一个可重入函数。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018年11月11日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Linux内核编程--进程通信信号
信号是 Linux 进程间通信的最古老的方式。信号是软件中断,它是在软件层次上对中断机制的一种模拟。
Coder-ZZ
2022/05/09
2.9K0
Linux内核编程--进程通信信号
【Linux】:进程信号(再谈信号保存和信号捕捉)
🌈 当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止
IsLand1314
2024/11/26
2540
【Linux】:进程信号(再谈信号保存和信号捕捉)
进程信号
kill命令是调用kill函数实现的。kill函数可以给一个指定的进程发送指定的信号。raise函数可以给当前进程发送指定的信号(自己给自己发信号)。
ljw695
2024/11/21
970
进程信号
Linux笔记(15)| Linux的信号
今天要分享的是Linux中的信号机制,信号是一种软件中断,是一种处理异步事件的方法,可以很好地在多个进程之间进行同步和简单的数据交换。
飞哥
2020/11/13
2.6K0
Linux笔记(15)| Linux的信号
【Linux】进程信号
Linux进程信号是一种进程间通信的机制,它允许一个进程通知另一个进程某个事件已经发生。以下是关于Linux进程信号的详细介绍:
大耳朵土土垚
2024/12/24
1320
【Linux】进程信号
Linux:进程信号(二.信号的保存与处理、递达、volatile关键字、SIGCHLD信号)
上次介绍了:(Linux:进程信号(一.认识信号、信号的产生及深层理解、Term与Core))[https://blog.csdn.net/qq_74415153/article/details/140624810]
是Nero哦
2024/07/26
1950
Linux:进程信号(二.信号的保存与处理、递达、volatile关键字、SIGCHLD信号)
【Linux探索学习】第二十八弹——信号(下):信号在内核中的处理及信号捕捉详解
https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482
GG Bond1
2025/02/05
1260
【Linux探索学习】第二十八弹——信号(下):信号在内核中的处理及信号捕捉详解
linux中sigaction函数详解
一、函数原型:sigaction函数的功能是检查或修改与指定信号相关联的处理动作(可同时两种操作)
全栈程序员站长
2022/09/01
1.6K0
linux中sigaction函数详解
进程间通讯(三).signal
UNIX/Linux 是多任务的操作系统,通过多个进程分别处理不同事务来实现,如果多个进程要进行协同工作或者争用同一个资源时,互相之间的通讯就很有必要了
franket
2021/09/15
1.6K0
Linux进程信号详解【下】
  当时在 进程等待 这一章节里我们并没有详细说明 Core dump标志,而我们通过man手册查看signal,会发现大部分的信号的作用都是 终止进程,而终止进程的动作却又分为 Core 和 Term(termination) 两个动作。
用户11029129
2024/08/06
1350
Linux进程信号详解【下】
【Linux进程信号】Linux信号机制深度解析:保存与处理技巧
🔍前言:在Linux操作系统的广阔天地中,信号机制无疑是一个充满挑战与机遇的领域。信号,作为进程间通信的一种重要方式,不仅承载着丰富的信息,还扮演着进程控制与管理的重要角色。然而,对于许多初学者而言,信号的保存与处理往往是一个难以逾越的障碍
Eternity._
2024/10/15
1850
【Linux进程信号】Linux信号机制深度解析:保存与处理技巧
【linux学习指南】详解Linux进程信号保存
如果在进程解除对某信号的阻塞之前这种信号产⽣过多次,将如何处理?POSIX.1允许系统递送该信 号⼀次或多次。Linux是这样实现的:常规信号在递达之前产⽣多次只计⼀次,⽽实时信号在递达之 前产⽣多次可以依次放在⼀个队列⾥。本章不讨论实时信号。
学习起来吧
2024/12/01
1230
【linux学习指南】详解Linux进程信号保存
Linux——进程信号
生活中的信号:红绿灯,手机的来电通知等。 为什么这些是信号呢?因为我们知道这些信号的意义代表着什么。 例如:红绿灯 有人教育过我们,让我们的大脑记住了红绿灯属性对应的行为。 但是,我们就算知道这个信号,也不一定要立刻去处理,因为可能正在做另一间更重要的事情。 所以我们也会有对应的三个动作: 默认动作(看到红灯停),自定义动作(看到红灯不是立刻停下,而而是后退一步或者是其他操作),忽略动作(看到红灯不停)。
有礼貌的灰绅士
2023/05/10
2.8K0
Linux——进程信号
【Linux】信号>信号产生&&信号处理&&信号保存&&信号详解
SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump,我们来验证一下
用户10925563
2024/06/04
2840
【Linux】信号>信号产生&&信号处理&&信号保存&&信号详解
异步通信之 信号
在软件层次上对中断机制的一种模拟,是一种异步通信的方式 。信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。
看、未来
2021/10/09
1.2K0
异步通信之 信号
Linux进程信号总结
不难看出上面的死循环在代码层面是永远无法结束程序的,那是否还有别的办法?对于死循环来说,最好的方式就是使用Ctrl+C对其进行终止。
咬咬
2024/07/20
990
Linux进程信号总结
Linux进程间通信(三) - 信号
什么是信号 软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。 收到信号的进程对各种信号有不同的
三丰SanFeng
2018/01/16
2.6K0
【Linux信号】三:信号的捕捉
信号捕捉主要是为了防止进程意外结束,并得到异常信息,捕捉信号后可以执行我们想要的动作。
mindtechnist
2024/08/08
2310
【Linux信号】三:信号的捕捉
【Linux】进程信号
生活中有很多的信号,比如闹钟、消息提醒、手机铃声,红绿灯。但是人是怎么识别红绿灯的,识别信号的?通过认识产生行为:有人通过教育的手段让我们在大脑中记住了对应的红绿灯属性或者行为;但是当信号到来的时候,我们不一定会马上去处理这个信号:信号可以随时产生(异步),而我们可能会做更重要的事情;信号到来的时候在到信号被处理一定会有时间窗口,必须得记住这个信号;
平凡的人1
2023/10/15
2350
【Linux】进程信号
信号
好,看完上面这些处理函数,其实这几个函数真的就是对信号集进行操作而已,而不会对具体信号有什么动作。 别急
看、未来
2020/08/26
1.3K0
信号
相关推荐
Linux内核编程--进程通信信号
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验