003_ipc概述及信号

【背景】

程序运行起来后,每个模块都有自己的进程,那么不同的模块如何进行通讯或者数据交换呢?

上面这张图说明了linux的ipc是继承最初的Unix 的IPC逻辑的,那么具体关系和概述讲解,请参考此链接的原文:https://www.cnblogs.com/52php/p/5877691.html 这里不再赘述

那么对于linux 下的ipc方法都有哪些呢?如下:

Linux下进程间通信的几种主要手段简介:

  1. 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;
  2. 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;Linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数);
  3. 报文(Message)队列(消息队列):消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  4. 共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
  5. 信号量(Semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
  6. 套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字

一般来说,Linux下的进程包含以下几个关键要素:

  • 有一段可执行程序;
  • 有专用的系统堆栈空间;
  • 内核中有它的控制块(进程控制块),描述进程所占用的资源,这样,进程才能接受内核的调度;
  • 具有独立的存储空间

进程和线程有时候并不完全区分,而往往根据上下文理解其含义

【信号】
 

这里为什么要再次说明呢?因为我们这次主要是基于linux c讲的,那么肯定有c的部分,最主要的也是Linux c本身的部分,针对c的信号部分,可以参考013_C标准库函数之<signal.h>-CSDN博客

下面我们主要说明下linux c的信号部分,当然大家也可以参考此链接,我觉得这个作者讲的很不错:https://www.cnblogs.com/52php/p/5813867.html

1.接收并处理信号

【sigaction】

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

sigaction()会依参数signum指定的信号编号来设置该信号的处理函数act。参数signum可以指定SIGKILL和SIGSTOP以外的所有信号,该函数与signal()函数一样,用于设置与信号signum关联的动作

入参:

int signum:具体信号值

const struct sigaction *act:用于设置指定信号的动作(也就是具体信号handler函数实现)

 struct sigaction *oldact:不是空指针的话,就保存原先对该信号的动作的位置

结构体:

struct sigaction {

void (*sa_handler)(int);

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

sigset_t sa_mask;

int sa_flags;

void (*sa_restorer)(void);

};

  • sa_handler:这是一个函数指针,指向处理信号的函数。如果设置为SIG_IGN,则忽略该信号;如果设置为SIG_DFL,则执行信号的默认动作。

  • sa_sigaction:这是一个可选的函数指针,当sa_flags中包含SA_SIGINFO标志时,使用这个字段来指定一个带有额外信息的信号处理器。这个处理器接收三个参数:信号编号、一个指向siginfo_t的指针(包含了信号的额外信息),以及一个指向信号上下文的指针。

  • sa_mask:这是一个信号集,用于指定在信号处理器执行期间要被阻塞的信号集合。这可以防止在处理一个信号时被其他信号中断。

  • sa_flags:这个字段包含了一系列标志,用于改变信号处理的行为。例如,SA_RESTART可以使某些被打断的系统调用在信号处理函数返回后自动重新发起。

  • sa_restorer:这个字段通常不需要,它是为了兼容某些老版本的系统,在现代系统中一般不使用。

【testcode00】

1.基础使用

2.sa.mask使用

此外,现在有一个这样的问题,我们使用signal()或sigaction()函数来指定处理信号的函数,但是如果这个信号处理函数建立之前就接收到要处理的信号的话,进程会有怎样的反应呢?它就不会像我们想像的那样用我们设定的处理函数来处理了。sa_mask就可以解决这样的问题,sa_mask指定了一个信号集,在调用sa_handler所指向的信号处理函数之前,该信号集将被加入到进程的信号屏蔽字中,设置信号屏蔽字可以防止信号在它的处理函数还未运行结束时就被接收到的情况,即使用sa_mask字段可以消除这一竞态条件。

2.发送信号

关于信号,我们上面说的signal, sigaction都是接收到信号,然后做出处理动作,那么,假如需要发出一个信号给其他进程呢?

【kill】

在Linux和Unix系统中,kill命令用于发送信号到程序或进程。信号是一种软件中断,可以用来通知接收进程某个事件已经发生

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

  • <pid>:进程ID,即您想要发送信号的进程的ID。
  • <signal>:信号的名称或编号。如果不指定,默认发送的是SIGTERM(终止信号)。

它的作用把信号sig发送给进程号为pid的进程,成功时返回0。kill()调用失败返回-1,调用失败通常有三大原因:

1、给定的信号无效(errno = EINVAL)
2、发送权限不够( errno = EPERM )
3、目标进程不存在( errno = ESRCH )

进程可以通过kill函数向包括它本身在内的其他进程发送一个信号,一般向别人进程发送信号可能会失败,极大概率原因是因为没有权限!但是要注意,如果是root用户的话,那就是无极的存在,肯定会成功,不会失败

【注意事项】

  • 您需要有足够的权限来发送信号到其他用户的进程。通常,只有root用户或进程的所有者才能发送信号到该进程。
  • 发送信号前,请确保您知道该信号的作用,因为某些信号(如SIGKILL和SIGSTOP)会立即终止或停止进程,且这些进程无法恢复。
  • 在发送信号前,您可以通过ps命令来查看进程的状态和PID。
  • 如果进程已经退出,kill命令可能会返回错误消息,如“没有这样的进程”。
  • 在使用kill命令时,确保您使用的是正确的进程ID,以避免意外终止重要的系统进程。

以上就是kill命令的基本用法。在使用时,请谨慎操作,以避免对系统稳定性造成影响。

【testcode】

在代码中使用fork()调用复制了一个新进程,在子进程中,5秒后向父进程中发送一个SIGALRM信号,父进程中捕获这个信号,并用sig_handler_00()函数来处理,变改flag_sig的值,然后退出循环。从结果中我们也可以看到输出了5个There is a string...之后,程序就收到一个SIGARLM信号,然后结束了进程

注:如果父进程在子进程的信号到来之前没有事情可做,我们可以用函数pause()来挂起父进程,直到父进程接收到信号。当进程接收到一个信号时,预设好的信号处理函数将开始运行,程序也将恢复正常的执行。这样可以节省CPU的资源,因为可以避免使用一个循环来等待。以本例子为例,则可以把while循环改为一句pause();

【alarm】

在Unix和Linux系统中,alarm函数是一个用于设置定时器(闹钟)的函数,它会在指定的秒数后发送SIGALRM信号到调用进程。如果进程没有处理该信号的处理器,那么默认的行为是终止进程

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

  • seconds:指定多少秒后发送SIGALRM信号。如果值为0,则取消任何已设置的闹钟。 

【返回值】

alarm函数返回上一个闹钟设置的剩余秒数,如果没有设置过闹钟,则返回0。如果返回值是非零,这意味着之前已经设置了一个闹钟,而且它还没有超时。

进程可以调用alarm()函数在经过预定时间后向发送一个SIGALRM信号。

alarm()函数用来在seconds秒之后安排发送一个SIGALRM信号,如果seconds为0,将取消所有已设置的闹钟请求。alarm()函数的返回值是以前设置的闹钟时间的余留秒数,如果返回失败返回-1

【testcode】

在这个例子中,程序将设置一个3秒的闹钟,并在闹钟超时时打印一条消息。然而,由于主循环是无限循环,如果没有其他方式终止程序,它将永远不会退出。实际上,SIGALRM的默认行为是终止进程,但由于我们注册了一个信号处理函数,所以它将调用sig_handler_02函数而不是终止进程。

需要注意的是,由于alarm函数是基于实时时间的,如果进程在睡眠状态(例如调用了sleeppause),SIGALRM信号会在进程恢复运行时立即触发。此外,在一个进程中只能设置一个闹钟,调用alarm会覆盖之前的设置。

3.信号函数集

在C语言中,信号处理是通过特定的函数集来实现的,这些函数在 <signal.h> 头文件中声明。以下是在C语言中处理信号的常用函数:

  1. signal() 

    • 用途:用于设置一个信号的处理函数。
    • 原型:void (*signal(int sig, void (*func)(int)))(int);
  2. sigaction() 

    • 用途:用于检查或修改与指定信号相关联的处理动作。
    • 原型:int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);
  3. kill() 

    • 用途:用于向指定进程发送信号。
    • 原型:int kill(pid_t pid, int sig);
  4. raise() 

    • 用途:用于向调用进程发送信号。
    • 原型:int raise(int sig);
  5. alarm() 

    • 用途:用于设置一个定时器,在指定的秒数后发送 SIGALRM 信号给调用进程。
    • 原型:unsigned int alarm(unsigned int seconds);
  6. pause() 

    • 用途:用于使调用进程挂起,直到收到一个信号。
    • 原型:int pause(void);
  7. sigpause() 

    • 用途:用于在挂起进程的同时设置信号掩码。
    • 原型:int sigpause(int sigmask);
  8. sigpending() 

    • 用途:用于获取当前被阻塞的信号集。
    • 原型:int sigpending(sigset_t *set);
  9. sigprocmask() 

    • 用途:用于改变进程的信号掩码。
    • 原型:int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
    • 调用这个函数才能真正改变进程的屏蔽字,之前的函数都是为改变一个变量的值而已,并不会真正影响进程的屏蔽字。
  10. sigemptyset() 

    • 用途:用于初始化信号集,将其所有信号清零。
    • 原型:int sigemptyset(sigset_t *set);
  11. sigfillset() 

    • 用途:用于将信号集的所有信号置位。
    • 原型:int sigfillset(sigset_t *set);
  12. sigaddset() 

    • 用途:用于将一个信号添加到信号集中。
    • 原型:int sigaddset(sigset_t *set, int sig);
  13. sigdelset() 

    • 用途:用于从信号集中删除一个信号。
    • 原型:int sigdelset(sigset_t *set, int sig);
  14. sigismember() 

    • 用途:用于检查一个信号是否在信号集中。
    • 原型:int sigismember(const sigset_t *set, int sig);

这些函数提供了C语言中信号处理的强大工具集,可以用于信号的产生、发送、阻塞、处理等操作。在使用这些函数时,需要注意信号的安全性和原子性,避免竞态条件和不可预料的行为。

在Linux操作系统中,信号是用于进程间通信(IPC)的一种机制,它们用于通知接收进程某个事件已经发生。Linux的信号函数集主要涉及信号的发送、接收和处理。以下是Linux中常用的信号处理相关函数:

  1. setitimer()

    • 设置间隔定时器,可以更精确地控制信号的发送。
  2. sigqueue()

    • 发送信号给指定进程,并可以附带一个整数值。
  3. sigsuspend()

    • 用于在临时替换信号屏蔽字的同时挂起进程,直到捕捉到一个信号。
  4. pthread_kill()

    • 用于向线程发送信号。
  5. sigwait()

    • 用于等待一个或多个信号的发生。

以下是信号处理相关的结构和类型:

  • sigset_t

    • 用于表示信号集的数据类型。
  • siginfo_t

    • 用于描述信号的信息。
  • struct sigaction

    • 用于描述信号的处理动作。

一些常用的信号及其默认行为:

  • SIGABRT:进程中止信号。
  • SIGALRM:定时器超时信号。
  • SIGFPE:浮点异常信号。
  • SIGHUP:挂起信号。
  • SIGILL:非法指令信号。
  • SIGINT:中断信号。
  • SIGKILL:杀死信号。
  • SIGPIPE:管道破裂信号。
  • SIGQUIT:退出信号。
  • SIGSEGV:段错误信号。
  • SIGTERM:终止信号。
  • SIGUSR1SIGUSR2:用户定义的信号。

这些函数和结构在 <signal.h> 头文件中声明,是Linux信号处理的核心组成部分。在使用这些函数时,应当注意信号的同步和异步特性,以及信号处理函数中应避免调用的那些不可重入的函数。

【注】以上c和linux信号函数集大部分是通用的,所以可以一起来看,我并没有将各自的一一列出,总的来看就行了

【testcode_00】

首先,我们能过sigaction()函数改变了SIGINT信号的默认行为,使之执行指定的函数sig_handler_03,所以输出了语句:Welcome to sig_handler_03... 然后,通过sigprocmask()设置进程的信号屏蔽字,把SIGINT信号屏蔽起来,所以过了6秒之后,用sigpending()函数去获取被阻塞的信号集时,检测到了被阻塞的信号SIGINT,输出The SIGINT signal has ignored...最后,用函数sigdelset()函数去除 先前用sigaddset()函数加在sigset上的信号SIGINT,再调用函数sigsuspend(),把进程的屏蔽字再次修改为sigset(不包含SIGINT),并挂起进程。由于先前的SIGINT信号停留在待处理状态,而现在进程已经不再阻塞该信号,所以进程马上对该信号进行处理,从而在最后,你不用输入 Ctrl+C 也会出现后面的处理语句,最后过了3秒程序就成功退出了。

【总结】

关于信号的学习及简单练习就先这么着,我们只是简单的演示和操作了一些接口,关于信号的复杂运用及注意事项,大家可以自行练习,由于篇幅关系我们没有将所有接口一一举例进行演示,但是大部分都演示了,大家可以根据此思维继续去演练,加油

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/55443.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mac 桌面版docker no space left on device

报错信息 docker pull镜像时报&#xff1a; failed to register layer: Error processing tar file(exit status 1): write /home/admin/oceanbase_bak/bin/observer: no space left on device 解决 增加 docker 虚拟磁盘大小。 调整完点击重启即可。

助力语音技术发展,景联文科技提供语音数据采集服务

语音数据采集是语音识别技术、语音合成技术以及其他语音相关应用的重要基础。采集高质量的语音数据有助于提高语音识别的准确性&#xff0c;同时也能够促进语音技术的发展。 景联文科技作为专业的数据采集标注公司&#xff0c;支持语音数据采集。可通过手机、专业麦克风阵列、专…

两个案例全面阐述全链路测试怎么做

首先我们先针对全链路功能测试部分进行一下讲解。去年的时候&#xff0c;有一家电商公司可能知道我一直在帮银行做相关的测试&#xff0c;就请我帮他们去做一些规划。这个平台有虚拟订单&#xff0c;也有实体订单&#xff0c;方式不太一样。 还涉及到分账分佣以及跟银行的对接…

大数据-174 Elasticsearch Query DSL - 全文检索 full-text query 匹配、短语、多字段 详细操作

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

计算机网络基础(1)

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 计算机网络基础 收录于专栏【计算机网络】 本专栏旨在分享学习计算机网络的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. 计算机网…

简易CPU设计入门:验证取指令模块【未完成】

项目代码下载 还是请大家首先准备好本项目所用的源代码。如果已经下载了&#xff0c;那就不用重复下载了。如果还没有下载&#xff0c;那么&#xff0c;请大家点击下方链接&#xff0c;来了解下载本项目的CPU源代码的方法。 下载本项目代码 准备好了项目源代码以后&#xff…

MySQL详细学习攻略 MySQL基础非常全面教程 MySQL安装教程

MySQL安装教程 章节目录 一、MySQL简介与安装前准备 二、Windows系统下MySQL的安装 三、Linux系统下MySQL的安装 四、MySQL安装后的基本配置 五、MySQL服务的启动与停止 六、MySQL客户端工具的使用 七、MySQL安装常见问题与解决方案 一、MySQL简介与安装前准备 重点内容知识…

Flythings学习(四)串口通信

文章目录 1 串口编程基本步骤1.1 打开串口1.2 配置串口 1.3 读串口1.4 发送串口1.5 关闭串口 2 综合使用3 如何在软件上保证串口稳定通信4 flythings中的串口通讯5 协议接收部分使用和修改方法6 通讯协议数据怎么和UI控件对接 1 串口编程基本步骤 串口通信有5个步骤 1.打开串口…

YOLOv11模型改进-注意力机制-引入自适应稀疏自注意力ASSA

随着目标检测领域的快速发展&#xff0c;YOLO系列模型凭借其端到端、高效的检测性能逐渐成为工业界和学术界的标杆。然而&#xff0c;如何进一步优化YOLOv11的特征提取能力&#xff0c;减少冗余信息并提升模型对复杂场景的适应性&#xff0c;仍是一个值得深入探讨的问题。为此&…

Android:记录一个打包发布版的release包以后闪退的问题

个人感觉其实release闪退的问题挺难排查的&#xff0c;因为release包运行起来as捕获不到相应的应用程序进程&#xff0c;从而不易查看到日志&#xff0c;也是我玩得不溜&#xff0c;大家有不同的方法可以评论区探讨&#xff0c;我也定期回复一些评论一起讨论。以下是我遇到的情…

【数据结构】宜宾大学-计院-实验三

线性表的应用——实现两多项式的相加 课前准备&#xff1a;实验学时&#xff1a;2实验目的&#xff1a;实验内容&#xff1a;实验结果&#xff1a;实验报告:&#xff08;及时撰写实验报告&#xff09;实验测试结果&#xff1a;代码实现&#xff1a;&#xff08;C/C&#xff09;…

安宝特方案 | AR技术在轨交行业的应用优势

随着轨道交通行业不断向智能化和数字化转型&#xff0c;传统巡检方式的局限性日益凸显。而安宝特AR眼镜以其独特的佩戴方式和轻便设计&#xff0c;为轨道交通巡检领域注入了创新活力&#xff0c;提供了全新的解决方案。 01 多样化佩戴方法&#xff0c;完美适应户外环境 安宝特…

访问控制列表(课内实验)

实验2&#xff1a;访问控制列表 实验目的及要求&#xff1a; 通过实验&#xff0c;进一步的理解标准ACL与扩展ACL的工作原理及执行过程。理解通配符的概念&#xff0c;熟练掌握标准ACL与扩展ACL的配置指令&#xff0c;掌握将访问控制列表应用VTY线路上&#xff0c;并且能够判断…

鸿蒙开发 四十五 鸿蒙状态管理(嵌套对象界面更新)

当运行时的状态变量变化&#xff0c;UI重新渲染&#xff0c;在ArkUI中称为状态管理机制&#xff0c;前提是变量必须被装饰器修饰。不是状态变量的所有更改都会引起刷新&#xff0c;只有可以被框架观测到的更改才会引起UI刷新。其中boolen、string、number类型&#xff0c;可观察…

Oracle漏洞修复 19.3 补丁包 升级为19.22

1.场景描述 上周末2024-10-12日,服务器扫出漏洞,希望及时修复。其中,oracle的漏洞清单如下,总结了下,基本都是 Oracle Database Server 的 19.3 版本到 19.20 版本和 21.3 版本到 21.11 版本存在安全漏洞,即版本问题。如: Oracle Database Server 安全漏洞(CVE-2023-22…

Spring AI Java程序员的AI之Spring AI(一)

SpringAI 基础使用 前言Spring AIChatClientImageClientOpenAiAudioTranscriptionClientEmbeddingClient 总结 前言 Spring AI&#xff0c;听着名字就感觉很好使用&#xff0c;快速上手&#xff0c;虽然功能没有太完善&#xff0c;但是社区活跃度很高&#xff0c;可以看看源码…

低代码框架参考

企业管理信息系统作为一类重要的应用软件系统&#xff0c;具有自己的特点&#xff0c;主要有两个方面&#xff1a; 1. 系统规模大&#xff0c;目前市场上常见的ERP系统一般都有几千个页面。 2. 页面逻辑相似性强。经过比较可以发现&#xff0c;大部分页面具有类似的功能&…

Docker新手必看:快速安装和配置BookStack在线文档系统

文章目录 前言1. 安装Docker2. Docker镜像源添加方法3. 创建并启动BookStack容器4. 登录与简单使用5. 公网远程访问本地BookStack5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定公网地址远程访问 前言 本文主要介绍如何在Linux系统使用Docker本地部署在线文档管理…

【c++篇】:初识c++--编程新手的快速入门之道(二)

文章目录 前言一.引用1.引用的概念2.引用的特性3.引用的使用场景4.常引用5.引用和指针的区别 二.内联函数1.C语言的宏函数2.内联函数的概念3.内联函数的特性 三.auto关键字1.auto的定义2.auto的使用规则3.auto不能推导的场景 四.基于范围的for循环1.范围for的语法2.范围for的使…

时间序列预测(七)——梯度消失(Vanishing Gradient)与梯度爆炸(Exploding Gradient)

目录 一、定义 二、产生原因 三、解决方法&#xff1a; 梯度消失与梯度爆炸是深度学习中常见的训练问题&#xff0c;它们主要发生在神经网络的反向传播过程中&#xff0c;使得模型难以有效学习。 一、定义 1、梯度消失&#xff08;Vanishing Gradient&#xff09;&#xf…