Linux下进程间的通信--信号

信号的概念:

在Linux操作系统中,信号是一种软件中断机制,用于通知进程某个事件已经发生。信号是Linux进程间通信(IPC)的一种简单且快速的方式,它可以用来处理各种异步事件,如用户输入、硬件事件或软件条件。

信号的特点:

进程可以选择阻塞某些信号,阻止这些信号的传递。

每个信号都有一个默认行为,例如终止进程、忽略或停止进程等

信号有不同的优先级,高优先级的信号会打断低优先级的信号处理。

信号可以跨越进程边界,由一个进程发送给另一个进程

信号机制是由操作系统内核实现的,与操作系统的调度和资源管理紧密相关。

信号的来源:

在Linux操作系统中,信号可以由多种来源产生,用于通知进程发生了某些重要事件。

  1. 硬件异常

    • SIGSEGV:尝试访问未分配的内存或无效的内存区域。
    • SIGFPE:发生浮点异常,如除以零。
    • SIGILL:执行了非法指令。
  2. 软件条件

    • SIGINT:通常由用户通过按下Ctrl+C产生,用于中断正在运行的程序。
    • SIGTERM:默认信号,用于请求程序自己终止。
    • SIGALRM:由alarm函数设置的计时器到期时产生。
  3. 用户干预

    • SIGKILL:由kill命令发送,用于立即终止程序,无法被捕获或忽略。
    • SIGSTOP:由kill命令发送,用于停止程序的执行,无法被捕获、忽略或由用户生成。
  4. 系统调用

    • SIGCHLD:子进程结束时,父进程会收到此信号。
    • SIGHUP:当控制终端关闭时,如关闭或拔出调制解调器,相关进程会收到此信号。
  5. 资源限制

    • SIGPIPE:写入一个没有读进程的管道时产生。
    • SIGXCPU:超过CPU时间限制。
    • SIGXFSZ:超过文件大小限制。
  6. I/O操作和设备状态变化

    • SIGURG:有紧急数据可从套接字读取时产生。
    • SIGIO:文件描述符上的I/O操作现在可进行时产生。
  7. 调度器事件

    • 某些实时调度器可能会在特定事件发生时发送信号。
  8. 外部设备或硬件设备

    • 特定硬件设备可能会在检测到特定事件时发送信号

信号的种类:

在Linux系统可以通过 kill -l 命令查看

 常用信号及其编号:

SIGHUP (1):挂起信号,通常在终端关闭时发送给前台进程组。

SIGINT (2):中断信号,通常由用户通过按下Ctrl+C产生。

SIGILL (4):非法指令信号,当进程执行非法指令时发送。

SIGABRT (6):中止信号,由abort()函数调用产生,用于异常终止进程。

SIGBUS (7):总线错误信号,当硬件异常,如内存访问错误时发送。

SIGFPE (8):浮点异常信号,如算术溢出、除以零等。

SIGKILL (9):杀死信号,用于立即终止进程,无法被捕获或忽略。

SIGUSR1 (10):用户定义信号1,用途由用户自定义。

SIGSEGV (11):段错误信号,当访问无效内存段时发送。

SIGUSR2 (12):用户定义信号2,用途由用户自定义。

SIGPIPE (13):管道信号,当写入一个没有读进程的管道时发送。

SIGTERM (15):终止信号,用于请求程序自己终止,可以被捕获或忽略。

SIGCHLD (17):子进程结束信号,当子进程结束时发送给父进程。

SIGCONT (18):继续信号,用于唤醒一个被停止的进程。

SIGSTOP (19):停止信号,用于停止进程的执行,无法被捕获或忽略。

SIGTTIN (21):后台进程试图读终端时发送。

SIGTTOU (22):后台进程试图写终端时发送。

SIGIO (29):I/O信号,当文件描述符上有可进行的I/O操作时发送。

信号处理的流程:

信号处理流程包含以下两个方面:

1. 信号的发送

信号的发送可以由以下几种方式触发:

  • 用户操作:用户可以通过键盘产生信号,如按下Ctrl+C通常发送SIGINT(中断信号)。
  • 软件生成:程序可以通过kill系统调用或raise函数发送信号给其他进程或自身。
  • 硬件异常:当程序执行非法操作(如除零、内存访问违规)时,硬件会触发信号,如SIGFPE(浮点异常)或SIGSEGV(段错误)。
  • 系统条件:系统在特定条件下也会发送信号,如SIGHUP(挂起信号)可能在控制终端关闭时发送。
  • 定时器超时:使用alarmsetitimertimer等定时器函数设置的定时器超时后,会发送如SIGALRMSIGVTALRM信号。

2. 信号的投递与处理

信号的投递与处理涉及以下几个步骤:

  • 信号队列:当一个信号被发送给进程时,它首先被放入进程的信号队列中。
  • 信号屏蔽:进程可以通过设置信号掩码(使用sigprocmask函数)来阻止某些信号的投递。被屏蔽的信号不会立即投递,直到它们被进程从屏蔽列表中移除。
  • 信号投递:内核负责将信号从队列中取出并投递给进程。如果信号未被屏蔽,内核会根据信号处理函数的设置来处理信号。
  • 默认处理:如果进程没有为信号设置处理函数,或者信号被忽略(使用signalsigaction函数设置为SIG_IGN),内核将执行信号的默认操作,如终止进程或忽略信号。
  • 用户定义的处理:如果进程为信号定义了处理函数,内核在信号到达时调用该函数。处理函数可以执行任何清理或响应操作。
  • 处理完成:信号处理函数执行完毕后,进程恢复到信号到达前的状态,继续执行。如果信号处理函数调用了如exit_Exit等函数,进程将终止。

在 Linux 中对信号的处理方式如下:

 在内核中的用于管理进程的结构为 task_struct ,

1.忽略信号

即对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。

SIGKILL用于立即终止进程,而SIGSTOP用于停止进程的执行。这两个信号是为了保证系统管理员能够控制所有进程而设计的。

2.捕捉信号

  • 进程可以捕捉信号,并定义自己的信号处理函数。当信号发生时,如果进程为该信号注册了处理函数,内核会暂停进程的执行,转而执行该信号的处理函数。
  • 信号处理函数可以是标准函数,如signal()函数
signal()函数
函数头文件
#include <signal.h>函数原型
typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);函数参数
signum:指定要处理的信号的编号。例如,SIGINT(通常由Ctrl+C产生)。
handler:指向一个函数的指针,该函数将被调用以处理指定的信号。这个函数可以是程序自定义的,也可以是几个标准函数之一:
SIG_DFL:执行信号的默认操作。
SIG_IGN:忽略该信号函数返回值
signal函数返回之前为该信号设置的处理函数的指针。如果之前没有设置处理函数,或者设置的是默认操作,它将返回SIG_DFL。如果之前设置的是忽略该信号,它将返回SIG_IGN。
如果signal函数调用失败,它将返回SIG_ERR,errno将被设置以指示错误原因
示例代码:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>void signal_handler(int sig) {printf("Received signal %d\n", sig);// 执行必要的动作
}int main() {// 设置SIGINT信号的处理函数signal(SIGINT, signal_handler);while(1) {sleep(1);printf("Looping...\n");}return 0;
}

3.执行默认操作

如果进程没有特别处理某个信号,Linux系统会为该信号执行默认操作。默认操作通常是终止进程、忽略信号或停止进程。

  • SIGTERM(信号15):默认操作是终止进程。
  • SIGCHLD(信号17):默认操作是忽略,通常用于子进程结束时通知父进程。
  • SIGCONT(信号18):默认操作是继续执行之前被停止的进程

信号发送操作

在Linux中,kill()raise() 函数是用于发送信号的两个常用方法。它们允许进程之间相互发送信号,或者进程可以向自己发送信号

1.kill()函数

kill() 函数可以向一个进程或进程组发送信号。这是最常用的信号发送方式,尤其是在需要向其他进程发送信号时。

​函数头文件
#include <sys/types.h>
#include <signal.h>函数原型
int kill(pid_t pid, int sig);函数参数
pid:目标进程的进程ID(PID)。
如果pid为正,则信号信号被发送到具有pid指定的ID的进程。
如果pid等于0,则向调用进程的进程组中的每个进程发送sig。
如果pid等于-1,则sig将发送到调用进程有权发送信号的每个进程,进程1 (init)除外
如果pid小于-1,则向进程组中ID为-pid的每个进程发送sig
sig:要发送的信号。可以是任何有效的信号,如SIGINT、SIGTERM等。函数返回值
成功 返回0。
失败 返回-1,并设置errno以指示错误原因。​

2.raise()函数

函数头文件
#include <sys/types.h>
#include <signal.h>函数原型
int raise(int sig);函数参数
sig:要发送给调用进程的信号编号。这是一个整数,表示特定的信号,如SIGINT、SIGTERM等。函数返回值
成功 返回0。
失败 返回-1,并设置errno以指示错误原因

等待信号操作

在Linux系统中,pause() 函数用于使调用它的进程挂起,直到它接收到一个信号。这是一个简单的阻塞调用,常用于进程初始化后等待异步信号的情况

pause()函数

函数头文件
#include <unistd.h>函数原型
int pause(void);函数描述
pause() 函数使调用它的进程挂起,直到它捕获到一个信号。该进程将一直停留在pause()调用处,直到有信号到达并被处理。
该函数没有参数。
它返回时,通常意味着一个信号已经被接收并处理。函数返回值
pause() 在信号处理函数执行后返回。如果信号处理函数返回,pause() 将返回。
如果因错误而失败,它将返回-1,并设置errno以指示错误原因

总结:

示例代码:

下面程序展示了如何使用信号和fork来创建和协调多个进程。父进程创建两个子进程,然后向它们发送不同的信号。第一个子进程等待并接收SIGUSR1信号,第二个子进程等待并接收SIGUSR2信号。子进程使用pause挂起等待信号,接收到信号后继续执行并退出。父进程等待子进程结束后也退出。这个程序展示了信号在进程间通信中的应用,并且正确地处理了子进程的结束,使用了wait(NULL)来避免子进程成为僵尸进程。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>typedef void (*sighandler_t)(int);
void sighandler(int signum)
{printf("sighandle:%s\n",strsignal(signum));
}
int main()
{sighandler_t sig = signal(SIGUSR1,sighandler);if(sig==SIG_ERR){perror("signal sig");exit(EXIT_FAILURE);}sighandler_t sigt  = signal(SIGUSR2,SIG_DFL);if(sigt==SIG_ERR){perror("signal sigt");exit(EXIT_FAILURE);}pid_t pid = fork();if (pid == -1){perror("fork failed");return 1;}else if(pid == 0){printf("<1>chilb process pid :%d\n",getpid());pause();printf("<1>chilb process end\n");exit(EXIT_SUCCESS);}else{pid_t Apid = fork();if(Apid==-1){perror("fork1:");exit(EXIT_FAILURE);}else if(Apid==0){printf("<2>chilb process Apid :%d\n",getpid());pause();printf("<2>chilb process end\n");exit(EXIT_SUCCESS);}else{printf("parent process pid :%d\n",getpid());sleep(1);int result = kill(pid,SIGUSR1);if(result==-1){perror("kill pid");exit(EXIT_FAILURE);}int ret = kill(Apid,SIGUSR2);if(ret==-1){perror("kill Apid");exit(EXIT_FAILURE);}printf("parent process end\n");}}wait(NULL);return 0;
}

代码解读:

定义sighandler_t类型,它是一个指向函数的指针,该函数接受一个整数参数(信号编号)并且没有返回值。

定义sighandler函数,它是一个信号处理函数,当接收到信号时被调用。它使用strsignal函数将信号编号转换为信号名称的字符串,并打印出来。

main函数中,首先设置SIGUSR1信号的处理函数为sighandler。如果设置失败,打印错误信息并退出。

SIGUSR2信号的处理函数设置为默认操作(SIG_DFL)。如果设置失败,打印错误信息并退出。

 创建第一个子进程。如果fork失败,打印错误信息并返回1。

 如果当前是子进程(pid == 0),打印子进程的PID,然后调用pause挂起等待信号。接收到信号后,打印结束信息并退出。

如果当前是父进程,再创建第二个子进程。如果fork失败,打印错误信息并退出。

如果当前是第二个子进程(Apid == 0),打印子进程的PID,然后调用pause挂起等待信号。接收到信号后,打印结束信息并退出。

如果当前是父进程,等待1秒,然后向第一个子进程发送SIGUSR1信号,向第二个子进程发送SIGUSR2信号。如果发送信号失败,打印错误信息并退出。

父进程打印结束信息。

父进程调用wait(NULL)等待任一子进程结束。

结语:

无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力

 

 

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

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

相关文章

Redis (day 3)

一、通过jedis连接数据库 1.首先导入依赖 <!-- https://mvnrepository.com/artifact/redis.clients/jedis --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>5.1.0</version></de…

(第三十三天)

1. 设置主从从 mysql57 服务器 &#xff08; 1 &#xff09;配置主数据库 [rootmsater_5 ~] # systemctl stop filewalld [rootmsater_5 ~] # setenforce 0 [rootmsater_5 ~] # systemctl disable filewalld [rootmsater_5 ~] # ls anaconda-ks.cfg mysql-5.7.44-linux-g…

【Unity】通用GM QA工具 运行时数值修改 命令行 测试工具

GM工具使用: GM工具通常用于游戏运行时修改数值(加钱/血量)、解锁关卡等&#xff0c;用于快速无死角测试游戏。一个通用型GM工具对于游戏项目是非常实用且必要的&#xff0c;但通用不能向易用妥协&#xff0c;纯命令行GM门槛太高&#xff0c;对QA不友好。 这类运行时命令行工具…

进程的创建、终止

目录 前言1. 进程创建2. 进程终止3. exit && _exit 的异同3.1 相同点3.2 不同点 前言 紧接着进程地址空间之后&#xff0c;我们这篇文章开始谈论进程控制相关的内容&#xff0c;其中包括进程是如何创建的&#xff0c;进程终止的几种情况&#xff0c;以及进程异常终止的…

数学建模学习(115):主成分分析(PCA)与Python实践

文章目录 一.主成分分析简介1.1 数学背景与维度诅咒1.2 PCA的定义与应用二.协方差矩阵——特征值和特征向量三.如何为数据集选择主成分数量四.特征提取方法五.LDA——与PCA的区别六.PCA的应用七.PCA在异常检测中的应用八.总结一.主成分分析简介 1.1 数学背景与维度诅咒 主成成…

TOP10漏洞原理

## 本人为学习网安不久的新人&#xff0c;记一次学习笔记&#xff0c;有缺陷或者表述不对的地方欢迎大家指出&#xff0c;感谢&#xff01; ## 1、sql注入&#xff1a;web应用程序对用户输入的数据没有进行过滤&#xff0c;或者过滤不严&#xff0c;就把sql语句拼接进数据库…

Mac电脑遇到DNS解析失败,ip可以访问,域名无法访问

当Mac电脑遇到DNS解析失败的问题时&#xff0c;可以尝试以下几个解决方法‌&#xff1a; 1.检查网络连接‌&#xff1a;确保Mac已连接到可用的网络&#xff0c;并且网络连接正常。可以尝试重新连接Wi-Fi或使用有线连接来排除网络问题。 2.清除DNS缓存‌&#xff1a;打开终端应…

docker容器基本命令、docker进入容器的指令、容器的备份、镜像底层原理、使用commit命令制造镜像、将镜像推送到阿里云镜像仓库与私服仓库

除了exit 还有 ctrlpq exit退出停止 ctrlpq 退出不停止 将本地镜像推到阿里云 登入阿里云 容器镜像服务 实力列表 镜像仓库 创建镜像仓库 安装里面步骤来 这里192.168.10.145这部分用自己ifconfig地址

【Android 远程数据库操作】

按正常情况下&#xff0c;前端不应该直接进行远程数据库操作&#xff0c;这不是一个明智的方式&#xff0c;应该是后端提供对应接口来处理&#xff0c;奈何公司各方面原因需要前端这样做。 对此&#xff0c;我对远程数据库操作做了总结&#xff0c;便于自己复盘&#xff0c;同…

python绘制爱心代码

效果展示 完整代码 Python中绘制爱心的代码可以通过多种方式实现&#xff0c;高级的爱心代码通常指的是使用较复杂的算法或者图形库来生成更加精致的爱心图形。下面是一个使用Python的Turtle模块来绘制爱心的示例代码&#xff1a; import turtledef draw_love():turtle.speed…

[Other]-安装ruby、ascli、ascp

最近新接到这样一个需求&#xff0c;将生物原始数据上传到某中心&#xff0c;其中用到ascp命令&#xff0c;阴差阳错的装了ruby、ascli&#xff0c;这里就都一并介绍下安装方式&#xff0c;由于服务器老旧默认安装时ruby2.0&#xff0c;又 升级到2.7等引发的一系列问题&#xf…

XSS-DOM

文章目录 源码SVG标签Dom-Clobbringtostring 源码 <script>const data decodeURIComponent(location.hash.substr(1));;const root document.createElement(div);root.innerHTML data;// 这里模拟了XSS过滤的过程&#xff0c;方法是移除所有属性&#xff0c;sanitize…

AI工具革新:国内外设计艺术的融合

在人工智能的浪潮中&#xff0c;全球的创新者和开发者们推出了一系列令人惊叹的工具&#xff0c;它们正以前所未有的速度改变着我们的工作、学习和生活方式。从图像生成到语言处理&#xff0c;从数据分析到自动化设计&#xff0c;AI 作图工具展示了其强大的能力&#xff0c;帮助…

DRF——Filter条件搜索模块

文章目录 条件搜索自定义Filter第三方Filter内置Filter 条件搜索 如果某个API需要传递一些条件进行搜索&#xff0c;其实就在是URL后面通过GET传参即可&#xff0c;例如&#xff1a; /api/users?age19&category12在drf中也有相应组件可以支持条件搜索。 自定义Filter …

面试题详解

前言&#xff1a;这一期我们专门来巩固所学知识&#xff0c;同时见识一些面试题。对知识做出一个总结。 1 不创建临时变量交换两个整数 . 第一种方法 #include<stdio.h> int main() {int a 0;int b 0;scanf("%d %d", &a, &b);printf("交换前…

神经网络算法 - 一文搞懂BERT(基于Transformer的双向编码器)

本文将从BERT的本质、BERT的原理、BERT的应用三个方面&#xff0c;带您一文搞懂Bidirectional Encoder Representations from Transformers | BERT。 Google BERT BERT架构&#xff1a; 一种基于多层Transformer编码器的预训练语言模型&#xff0c;通过结合Tokenization、多种E…

Java基于数据库、乐观锁、悲观锁、Redis、Zookeeper分布式锁的简单案例实现(保姆级教程)

1. 分布式锁的定义 分布式锁是一种在分布式系统中用来协调多个进程或线程对共享资源进行访问的机制。它确保在分布式环境下&#xff0c;多个节点&#xff08;如不同的服务器或进程&#xff09;不会同时访问同一个共享资源&#xff0c;从而避免数据不一致、资源竞争等问题。 2…

等保测评服务的业务连续性规划:确保信息安全服务的韧性

在当前的数字化转型浪潮中&#xff0c;信息安全已成为企业运营的关键一环。等保测评服务作为信息安全合规的重要组成部分&#xff0c;其业务连续性规划对于保障服务的稳定性和客户信息资产的安全至关重要。本文将探讨等保测评服务的业务连续性规划策略&#xff0c;旨在构建一个…

树状数组算法

文章目录 树状数组是什么树状数组与线段树的区别与联系树状数组讲解点修&#xff0c;区查&#xff0c;讲解及模板点查&#xff0c;区修讲解及模板 树状数组是什么 树状数组是一种数据结构&#xff0c;提供O(logn)时间内的单点修改和区间求和操作&#xff0c;比线段树有更优的常…

MD编辑器学习笔记

MD编辑器学习笔记 目录标题文本样式列表图片链接代码片数学公式表格部分总结 目录 目录是使用“[TOC](目录&#xff09;”&#xff0c;记住别忘了加上&#xff08;&#xff09;标题 使用#来确定标题&#xff0c;几个#就是几级标题。记住#后面要加上空格文本样式 tips: 在写正…