首页 > 系统相关 >【Linux学习】进程间通信 (2) —— 信号

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

时间:2024-05-27 20:00:43浏览次数:29  
标签:函数 int 间通信 sig 信号 Linux 进程 sigaction

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

小海编程心语录-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博客

 

标签:函数,int,间通信,sig,信号,Linux,进程,sigaction
From: https://blog.csdn.net/qq_59715605/article/details/139245498

相关文章

  • Windows 迁移至 Linux(debian12) 实验
    〇、前言最近用的Windows出现太多恶性故障,重装系统到怀疑人生,打算彻底抛弃栊子平台了,无法得知是内网被攻击还是细软作祟,反正彻底生气了,不能转换也必须得转换至linux了,宁愿多折腾Linux也不想在闭源的windows上干等干受折磨了。目前大概整理了以下表格,用于替换,之后会进行......
  • Linux如何在目录下灵活创建、浏览、删除百万个文件
    目录一、创建百万级小文件1、单核CPU情况2、多核CPU情况3、执行效率对比3.1、单核的顺序执行3.2、多核的并发执行二、如何列出/浏览这些文件1、查看目录下文件的数量2、列出?3、ls-f(关闭排序功能)3.1、执行效率对比4、通过重定向导入到文件中浏览对应的文件名三、如何快速删除目录......
  • 《TCP/IP网络编程》(第十章)多进程服务器端(2)
    基于进程的并发服务器我们将扩展之前的回声服务器,使其可以同时向多个客户端体提供服务,实现模型如下图所示即每当有客户端向服务器请求服务时,服务器端都创建一个子进程为其提供服务,比如有5个客户端请求服务,则创建个5子进程。通过fork()复制的文件描述符下图是父进程调用......
  • 进程间通信(管道)、多线程理论、开设多线程的两种方式、threading介绍、线程之间共享数
    【一】进程间通信(管道)借助于消息队列,进程可以将消息放入队列中,然后由另一个进程从队列中取出。这种通信方式是非阻塞的,即发送进程不需要等待接收进程的响应即可继续执行。multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的进程间通信(IPC)有两种方式:队列......
  • 互斥锁、进程间通信(IPC)、队列(queue)模块、队列实现进程间通信、生产者和消费者模型
    【一】互斥锁【1】什么是进程同步(互斥锁)互斥锁(Mutex)是一种用于多线程编程中控制对共享资源访问的机制。其作用是保证在同一时刻只有一个线程在访问共享资源,从而避免多个线程同时读写数据造成的问题。互斥锁的基本原理是在对共享资源进行访问前加锁,使得其他线程无法访问该......
  • 僵尸进程和孤儿进程、守护进程
    【一】僵尸进程和孤儿进程【1】引入我们知道在unix/linux中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。当一个进程完成它的工作终止之后,它的父进程需要调用wait()或......
  • Linux用docker安装Elasticsearch&&SpringBoot整合ES
    一. 部署Elasticsearch1.docker查询docker容器中的esdockersearchelasticsearch2. 安装(PS:查看自己的springBoot的版本号 对应的es版本安装)dockerpullelasticsearch:7.6.23.查看已安装的docker镜像dockerimages4.创建挂在目录mkdir-p/data/elk/es/{......
  • postgressq——四种进程间锁(4)
    进程间锁在PostgreSQL里有四种类型的进程间锁:Spinlocks:自旋锁,其保护的对象一般是数据库内部的一些数据结构,是一种轻量级的锁。LWLocks:轻量锁,也是主要用于保护数据库内部的一些数据结构,支持独占和共享两种模式。Regularlocks:又叫heavyweightlocks,也就是我们常说的表锁......
  • Linux内存占用分析的几个方法
    内存管理是一个非常重要的任务,Linux内存占用分析的方法不只以上几种,用户可以根据需要选择合适的方法进行使用。对于使用Linux进行开发和运维的人员,熟练使用这些命令和方法能够帮助他们更好的管理系统资源,提高系统性能和稳定性。1.free命令free命令可以查看Linux系统的内......
  • 十个对初学者特有用的 Linux 命令(非常详细)零基础入门到精通,收藏这一篇就够了
    21CTO导读:作为一名Linux管理员,或者即使是刚刚开始使用Linux的新手,充分了解解决网络问题时有用的命令也是至关重要的。我们将探讨用于诊断和解决常见网络问题的10个基本Linux命令。每个命令都将附有实际示例,以说明其用法与有效性。1.ping例如:pingwww.21cto.com......