当前位置: 首页 > news >正文

系统编程day05-进程间通信-信号

1.进程间通信

1.1什么是进程间通信

用于在操作系统中不同进程之间交换信息或协作。由于进程通常运行在独立的内存空间中,无法直接访问彼此的数据,因此需要通过操作系统提供的通信机制来实现信息的传递和共享。

1.2进程间通信的方式

2.信号

2.1信号的概念

例如:教室铃响了,就知道下课了。如果手机响了,就知道来信息或者来电话。

以上这些就是信号。信号已经在生活中离不开了。特别是物联网行业。

2.2信号的特点

  • 信号是传递信息的,但是不传递具体的信息,知识一个标志
  • 信号是一种软件中断机制(写代码触发,与硬件中断有所区别)

2.3信号的编号

linux中,信号编号的查看,用 kill -l来具体查看。 1-31 号信号称之为常规信号(也叫普通信号或标准信号),34-64称为实时信号。

2.4信号的四要素

  1. 编号

  2. 信号名称(和信号编号都一样,后续调用函数传参数的时候可以传编号也可传名称。)

  3. 对应事件

  4. 默认处理动作(终止进程,忽略,暂停)

后续可以使用man 7 signal查看signal信号

3.信号的产生与处理

产生

  1. 当用户按某些终端键时,将产生信号。 终端上按“Ctrl+c”组合键通常产生中断信号 SIGINT 终端上按“Ctrl+\”键通常产生中断信号 SIGQUIT 终端上按“Ctrl+z”键通常产生中断信号 SIGSTOP 等。

  2. 硬件异常将产生信号。 除数为0,无效的内存访问等。这些情况通常由硬件检测到,并通知内核,然后内核产生适当的信号发送给相应的进程。

  3. 软件异常将产生信号。 当检测到某种软件条件已发生(如:定时器 alarm),并将其通知有关进程时,产生信号。

  4. 调用系统函数(如:kill、raise、abort)将发送信号。

  5. 运行 kill /killall 命令将发送信号。 此程序实际上是使用 kill 函数来发送信号。

信号的处理

信号的处理:忽略、终止进程、暂停进程,(自定义处理)

4.未决信号集与阻塞信号集

信号是一种异步通信方式,信号发出,不用管是否被接收或者处理。

Linux 内核的进程控制块 PCB 是一个结构体,task_struct, 除了包含进程 id,状态,工作目录,用户 id,组 id,文件描述符表,还包含了信号相关的信息,主要指阻塞信号集和未决信号集。

未决信号集:未决信号集 信号产生,未决信号集中描述该信号的位立刻翻转为 1,表示信号处于未决状态。当信号被处理对应位翻转回为 0。这一时刻往往非常短暂。

阻塞信号集:将某些信号加入集合,对他们设置屏蔽,当屏蔽 x 信号后,再收到该信号,该信号的处理将推后。(注意,不是不处理,而是推后处理)

如果信号在阻塞信号集内,那么信号得不到执行,未决信号集里这个信号的位图一直是1,直到该信号从阻塞集剔除,执行完毕才会变成0,从未决信号集中剔除。

5.信号相关API函数

5.1kill函数(向其他进程发送信号)

函数原型:

#include <sys/types.h>
#include <signal.h>
//向pid发送一个信号sig
int kill(pid_t pid, int sig);

参数:

pid : 取值有 4 种情况 :

  • pid > 0: 将信号传送给进程 ID 为 pid 的进程。

  • pid = 0 : 将信号传送给当前进程所在进程组中的所有进程

  • pid = -1 : 将信号传送给系统内所有的进程。(慎用)

  • pid < -1 : 将信号传给指定进程组的所有进程。这个进程组号等于 pid 的绝对值。(如果传入-9527,其实是发送给9527进程组的所有进程)

sig : 信号的编号,这里可以填数字编号,也可以填信号的宏定义,可以通过命令 kill - l("l" 为字母)进行相应查看。不推荐直接使用数字,应使用宏名,因为不同操作系统信号编号可能不同,但名称一致。

返回值:

  • 成功:0

  • 失败:-1

代码案例:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h>int main(int argc, char const *argv[])
{pid_t pid = fork();if (pid == -1){perror("fork");}else if (pid == 0) // 子进程{for (int i = 1; i <= 5; i++){printf("我还能再玩%ds\n", 5 - i);sleep(1);}while (1){/* code */}}else if (pid > 0) // 父进程{int status = 0;sleep(5);kill(pid, SIGINT); //向pid 发送一个sigint信号(2号信号)也就相//当于按下了ctrl+cwait(&status);  //等待资源回收printf("WIFEXITED(status):%d\n", WIFEXITED(status)); // 结果为0 因为非正常退出if (WIFEXITED(status))  //此处不会执行{printf("退出状态%d\n", WEXITSTATUS(status));}}return 0;
}

5.2raise(自己给自己发送信号)

函数原型:

#include <signal.h>
int raise(int sig);

参数:sig ,向自己发送一个sig信号 等价于 kill(getpid(),sig);

代码案例:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h>int main(int argc, char const *argv[])
{pid_t pid = fork();if (pid == -1){perror("fork");}else if (pid == 0) // 子进程{for (int i = 1; i <= 5; i++){printf("我还能再玩%ds\n", 5 - i);sleep(1);}raise(SIGINT); //向自己发送信号while (1){/* code */}}else if (pid > 0) // 父进程{int status = 0;sleep(5);// kill(pid, SIGINT); //向pid 发送一个sigint信号(2号信号)wait(&status);  //等待资源回收printf("WIFEXITED(status):%d\n", WIFEXITED(status)); // 结果为0 因为非正常退出if (WIFEXITED(status))  //此处不会执行{printf("退出状态%d\n", WEXITSTATUS(status));}}return 0;
}

5.3abort

函数原型:

#include <stdlib.h>
void abort(void);

功能:给自己发送异常终止信号 6) SIGABRT,并产生 core 文件,等价于 kill(getpid(), SIGABRT);

4.4alarm(定时发送信号)

函数原型:

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

功能:

  • 设置定时器(闹钟)。在指定 seconds 后,内核会给当前进程发送 14)SIGALRM 信号。进程收到该信号,默认动作终止。每个进程都有且只有唯一的一个定时器。

  • 取消定时器 alarm(0),返回旧闹钟余下秒数。

参数:

seconds:指定的时间,以秒为单位

返回值:
返回 0 或剩余的秒数

代码案例:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h>int main(int argc, char const *argv[])
{pid_t pid = fork();if (pid == -1){perror("fork");}else if (pid == 0) // 子进程{alarm(5);  //5s以后向该进程发送一个14号新号for (int i = 1; i <= 5; i++){printf("我还能再玩%ds\n", 5 - i);sleep(1);if (i==3){int ret = alarm(0);  //3s以后,定时器取消,返回值为剩余时间  2sprintf("ret:%d\n",ret);}}while (1){/* code */}}else if (pid > 0) // 父进程{int status = 0;sleep(5);// kill(pid, SIGINT); //向pid 发送一个sigint信号(2号信号)wait(&status);  //等待资源回收printf("WIFEXITED(status):%d\n", WIFEXITED(status)); // 结果为0 因为非正常退出if (WIFEXITED(status))  //此处不会执行{printf("退出状态%d\n", WEXITSTATUS(status));}}return 0;
}

5.5setitimer(可循环定时器)

函数原型:

#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);

可见这个函数参数非常的多

功能:设置定时器(闹钟)。 可代替 alarm 函数。精度微秒 us,可以实现周期定时。

  • which:指定定时方式

  1. 自然定时:ITIMER_REAL → 14)SIGALRM 计算自然时间(时钟时间)

  2. 虚拟空间计时(用户空间):ITIMER_VIRTUAL → 26)SIGVTALRM 只计算进程占用 cpu 的时间

  3.  运行时计时(用户 + 内核):ITIMER_PROF → 27)SIGPROF 计算占用 cpu 及执行系统调用的时间

  • struct itimerval *new_value 结构体指针,而且传入后不能修改。

结构体1

struct itimerval {struct timerval it_interval; // 闹钟触发周期  ,后续每一次触发的时间间隔 struct timerval it_value; //闹钟触发时间  从现在开始,到闹钟第一次触发的时间
};

结构体2

struct timeval {long tv_sec; // 秒long tv_usec; // 微秒
}

old_value: 存放旧的 timeout 值,一般指定为 NULL

具体理解呢就是:在12.50定了一个13.00的闹钟,这中间的10分钟就是第一次触发的时间(it_value)。而后面每24小时就触发一次13.00的闹钟,这个24小时就是循环触发闹钟的间隔时间(it_interval)。

代码案例:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/time.h>int main(int argc, char const *argv[])
{struct itimerval new, old;new.it_interval.tv_sec = 5; // 后续每次触发是5snew.it_interval.tv_usec = 0;new.it_value.tv_sec = 10; // 第一次触发是10s以后new.it_value.tv_usec = 0;setitimer(ITIMER_REAL, &new, &old);//按照new规定的时间进行定时器设置,包括循环时间和第一次执行的时间//old 用来保存上一次定时器状态,例如剩余时间(在此处是0,因为上次没有调用定时器)sleep(2);setitimer(ITIMER_REAL, &new, &old); ////按照new规定的时间进行定时器设置,包括循环时间和第一次执行的时间//old 此时,保存上一个定时器计数剩下的时间,大约是8s,可以用于后续恢复这个计时器状态printf("%ld\n", old.it_interval.tv_sec);printf("%ld\n", old.it_interval.tv_usec);printf("%ld\n", old.it_value.tv_sec);printf("%ld\n", old.it_value.tv_usec);setitimer(ITIMER_REAL, &old, NULL);int count = 1;while (1){sleep(1);printf("过了%ds\n", count);count++;}return 0;
}

6.给信号注册自定义函数

进程收到信号后的操作:

1.执行默认动作,例如结束进程,或者是暂停进程。

2.直接忽略

3.执行自定义信号处理函数(捕获) 用用户定义的信号处理函数处理该信号。

6.1信号自定义函数的作用

6.2signal函数

函数原型:

#include <signal.h>   //头文件
typedef void(*sighandler_t)(int);  //函数指针     int
sighandler_t signal(int signum, sighandler_t handler);

函数功能:注册信号处理函数(不可用于 SIGKILL、SIGSTOP 信号),即确定收到信号后处理函数的入口地址。此函数不会阻塞。不再按照系统默认方式处理。

参数:

  • signum:信号的编号,这里可以填数字编号,也可以填信号的宏定义,可以通过命令 kill - l("l" 为字母)进行相应查看。

  • handler : 取值有 3 种情况:

    • SIG_IGN:忽略该信号

    • SIG_DFL:执行系统默认动作

    • 信号处理函数名:自定义信号处理函数。

代码案例:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/time.h>
#include <stdlib.h>char *p = NULL;void my_deal(int signal) //后续这个signal 可以用于监控是哪个信号触发了这个函数
{if (p !=NULL ){free(p);p = NULL;}_exit(0);
}
int main(int argc, char const *argv[])
{p = (char *)malloc(128);signal(SIGINT,my_deal);  //我想在终端 按下 ctrl + c的时候,将这个p释放掉while (1){printf("%p\n",p);sleep(1);}free(p);return 0;
}

6.3sigaction

函数原型:

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction*oldact);

函数功能:检查或修改指定信号的设置(或同时执行这两种操作)。

参数:

  • signum:要操作的信号。一般传宏

  • act: 要设置的对信号的新处理方式(传入参数)。

  • oldact:原来对信号的处理方式(传出参数)。

如果 act 指针非空,则要改变指定信号的处理方式(设置),如果 oldact 指针非空,则系统将此前指定信号的处理方式存入 oldact。

struct sigaction 结构体:

struct sigaction {//两个信号处理函数指针  选择一个即可
void(*sa_handler)(int); //旧 信号处理函数指针  相当于 signal函数的第二参数。
void(*sa_sigaction)(int, siginfo_t *, void *); //新的信号处理函数指针
sigset_t sa_mask; //信号阻塞集
int sa_flags; //信号处理的方式
void(*sa_restorer)(void); //已弃用
};//sa_flags:用于指定信号处理的行为,通常设置为 0,表使用默认属性。它可以是以下值的“按位或”组合
/*SA_RESTART:使被信号打断的系统调用自动重新发起(已经废弃) SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。 SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为僵尸进程。 SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。 SA_RESETHAND:信号处理之后重新设置为默认的处理方式。 SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数。
*/

代码案例:

#define _POSIX_C_SOURCE 199309L
#define _XOPEN_SOURCE 700#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/time.h>//倒计时10s,10s之内不允许使用ctrl+c打断函数,过了10s之后就可以使用ctrl+c结束运行
void my_deal() {printf("使用了ctrl+c,但是没用\n");
}int main() {struct sigaction act;act.sa_handler = my_deal;//处理方式为自己定义的处理方式sigemptyset(&act.sa_mask);//将信号集置空act.sa_flags = 0;//使用默认属性sigaction(SIGINT, &act, NULL); int i = 1;while (i <= 10) {printf("还剩%ds\n", 10 - i);sleep(1);i++;}act.sa_handler = SIG_DFL;sigaction(SIGINT, &act, NULL);while (1) {sleep(1);printf("现在可以中断\n");}return 0;
}

7.信号集

函数:

#include <signal.h>
int sigemptyset(sigset_t *set); //将 set 集合置空
int sigfillset(sigset_t *set); //将所有信号加入 set 集合
int sigaddset(sigset_t *set, int signo); //将 signo 信号加入到 set 集合
int sigdelset(sigset_t *set, int signo); //从 set 集合中移除 signo 信号
int sigismember(const sigset_t *set, int signo); //判断信号是否存在

并没有结束,先休息两天,下次再更新。


文章转载自:

http://w3Kv7rEn.cnfjs.cn
http://LpMl0DZ7.cnfjs.cn
http://FRpDoopU.cnfjs.cn
http://KtF2TPQs.cnfjs.cn
http://qJTRKmth.cnfjs.cn
http://tBLIRup0.cnfjs.cn
http://9BtK2MTi.cnfjs.cn
http://iuBKgnP5.cnfjs.cn
http://twFeQcCg.cnfjs.cn
http://tE3O28BV.cnfjs.cn
http://AurHFsOF.cnfjs.cn
http://gae8YTOv.cnfjs.cn
http://HbsIgytA.cnfjs.cn
http://wc2fkhTq.cnfjs.cn
http://30wDx3ss.cnfjs.cn
http://3dpTuFBj.cnfjs.cn
http://dadoUxb2.cnfjs.cn
http://qscgGAVy.cnfjs.cn
http://dDHXYlTF.cnfjs.cn
http://I83t6d7c.cnfjs.cn
http://2gnWkfPF.cnfjs.cn
http://CfnRPiVA.cnfjs.cn
http://J9ruRxym.cnfjs.cn
http://H6EWBVVU.cnfjs.cn
http://IGJbCwlP.cnfjs.cn
http://n8lzH1Wy.cnfjs.cn
http://dHb2djRU.cnfjs.cn
http://mzRP4eFR.cnfjs.cn
http://mV7GUI7E.cnfjs.cn
http://QLCqlJS9.cnfjs.cn
http://www.dtcms.com/a/370129.html

相关文章:

  • OpenHarmony之有源NFC-connected_nfc_tag模块详解
  • 吴恩达机器学习合集
  • java基础学习(五):对象中的封装、继承和多态
  • 神马 M66S+ 282T矿机参数详解:SHA-256算法与Hydro冷却技术
  • AI 生成式艺术重塑动漫角色创作:从技术逻辑到多元可能性(一)
  • c++primer 个人学习总结-模板和泛型编程
  • solidity的高阶语法2
  • 9.FusionAccess桌面云
  • SpringBoot集成XXL-JOB保姆教程
  • Linux 网络流量监控 Shell 脚本详解(支持邮件告警)
  • 阿里云对象存储OSS的使用
  • WSL2环境下因服务器重装引发的SSH连接问题排查记录
  • 02-Media-6-rtsp_server.py 使用RTSP服务器流式传输H264和H265编码视频和音频的示例程序
  • I/O 多路复用 (I/O Multiplexing)
  • Nginx性能调优:参数详解与压测对比
  • java接口和抽象类有何区别
  • C/C++动态爱心
  • YOLOv8 在 Intel Mac 上的 Anaconda 一键安装教程
  • 关于 React 19 的四种组件通信方法
  • Joplin-解决 Node.js 中 “digital envelope routines::unsupported“ 错误
  • [论文阅读] 软件工程 - 需求工程 | 2012-2019年移动应用需求工程研究趋势:需求分析成焦点,数据源却藏着大问题?
  • sensitive-word 敏感词性能提升14倍优化全过程 v0.28.0
  • 留数法分解有理分式
  • 基于FPGA的汉明码编解码器系统(论文+源码)
  • C++经典的数据结构与算法之经典算法思想:排序算法
  • 大恒-NF相机如何控制风扇
  • 01.单例模式基类模块
  • 数位DP -
  • kotlin - 2个Fragment实现左右显示,左边列表,右边详情,平板横、竖屏切换
  • 基于SpringBoot+Thymeleaf开发的实验室助理工作管理系统