[Linux][OS][信号的捕捉] 可重入函数 | volatile | SIGCHLD信号

ops/2024/9/25 4:36:29/

回顾:[Linux][OS][信号的保存和处理]

信号捕捉

1.sigaction

int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);

参数:

    • signo:指定信号的编号
    • act:输入型参数,根据act修改该信号的处理动作
    • oact:输出型参数,通过oact传出该信号原来的处理动作

返回值:成功返回0,出错则返回- 1

act和oact指向sigaction结构体:

测试:

对函数指针结构体的存入

#include<iostream>
#include<cstring>
#include<unistd.h>
#include<signal.h>
using namespace std;
void handler(int signo)
{cout<<"catch a signal,signal number:"<<signo<<endl;
}
int main()
{struct sigaction act,oact;memset(&act,0,sizeof(act));//初始化memset(&oact,0,sizeof(oact));act.sa_handler=handler;sigaction(2,&act,&oact);while (true){cout<<"i am a process:"<<getpid()<<endl;sleep(1);/* code */}return 0;
}

小 tip:memset 常用于初始化变量、清空缓冲区或者设置特定模式的字节序列。

  • 问题一: pending位图,什么时候从1->0.?

执行信号捕捉方法之前,先清0,在调用,来使信号再次产生时,当前信号完成了再执行下一个,禁止不断嵌套式的捕捉

当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,提交到 block 表中

//循环模拟实现
void handler(int signo)
{cout << "catch a signal, signal number : " << signo << endl;while (true){PrintPending();sleep(1);}
}
  • 问题2: 信号被处理的时候,对应的信号也会被添加到block表中,防止信号捕捉被嵌套调用

正在处理二号信号时,2 号信号会被屏蔽,那么如何也屏蔽其他信号呢?

    sigemptyset(&act.sa_mask);sigaddset(&act.sa_mask, 1);sigaddset(&act.sa_mask, 3);sigaddset(&act.sa_mask, 4);act.sa_handler = handler; // SIG_IGN SIG_DFLsigaction(2, &act, &oact);

如果 在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需 要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。

2.可重入函数

验证:链表的插入是不可重入的

  • insert 函数被
  • 节点丢失,内存泄露

如果一个函数,被重复进入的情况下,出错了或者可能出错,就是不可重入函数,否则就叫可重入函数

目前我们学到的大部分函数都是多执行流下的不可重入的:

  • 调用了malloc或free,因为malloc也是用全局链表来管理堆的。(例如 STL 不可以,涉及了很多指针的变换和扩容)
  • 调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。

3.volatile

测试

int flag = 0;
//volatile int flag=0;void handler(int signo)
{cout << "catch a signal: " << signo << endl;flag = 1;
}int main()
{signal(2, handler);// 在优化条件下, flag变量只在读取,可能被直接优化到CPU内的寄存器中while(!flag); // flag 0, !falg 真  逻辑运算cout << "process quit normal" << endl;return 0;
}

编译优化的设置:g++ -o $@$^ -O1 (0 1 2 3 对优化等级的选择)

  • 在优化条件下, flag变量只在读取可能被直接优化到CPU内的寄存器中
  • while(!flag); // flag 0, !falg 真 逻辑运算

改 1 优化后为什么就无法退出循环了?

因为优化,导致我们的内存不可见了!

这时添加 volatile 关键字:防止编译器过度优化,保持内存的可见性!

volatile int flag=0;

4.SIGCHLD 信号

子进程退出的时候,不是静悄悄的退出

子进程在退出的时候,会主动的向父进程发送 SIGCHLD(17)信号

怎么证明?基于信号捕捉,来对信号进行回收,将对 17 的回收加入到 handler 方法

void handler(int signo)
{pid_t rid;rid = waitpid(-1, nullptr, WNOHANG);//实现非阻塞调用cout << "I am proccess: " << getpid() << " catch a signo: " << signo << "child process quit: " << rid << endl;
}int main()
{signal(17,handler);for (int i = 0; i < 10; i++){pid_t id = fork();if (id == 0){while (true){cout << "I am child process: " << getpid() << ", ppid: " << getppid() << endl;sleep(5);break;}cout << "child quit!!!" << endl;exit(0);}sleep(1);}// fatherwhile (true){cout << "I am father process: " << getpid() << endl;sleep(1);}return 0;
}

来进行调用测试:

得出子进程在进行等待的时候,我们可以基于信号的方式进行等待

等待的好处:

  1. 获取子进程的退出状态,释放子进程的僵尸
  2. 虽然不知道父子谁先运行,但是我们清楚,一定是 father 最后退出

还是要调用 wait 接口,father 必须保证自己是一致在运行的-->把子进程等待写入信号捕捉函数中

多个子进程该如何正确的回收呢?如果在退出一般的时候呢?while 循环+非阻塞方案

void handler(int signo)
{sleep(5);pid_t rid;while ((rid = waitpid(-1, nullptr, WNOHANG)) > 0)//非阻塞方案{cout << "I am proccess: " << getpid() << " catch a signo: " << signo << "child process quit: " << rid << endl;}
}

随机休眠时长进行测试:

#include <cstdlib> // 用于rand()和srand()
#include <ctime>   // 用于time()
srand(time(nullptr));      // 初始化随机数生成器
if (id == 0){int random_sleep_time = rand() % 10 + 1; // 生成1到10之间的随机数cout << "Child process " << getpid() << " will sleep for " << random_sleep_time << " seconds." << endl;sleep(random_sleep_time);cout << "Child process " << getpid() << " is quitting." << endl;exit(0);}

实现边创建,边等待回收:

子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略SIG_DFL -> action -> IGN)事实上,由于UNIX 的历史原因,要想不产生僵尸进程还有另外一种办法:父进程调用sigaction将SIGCHLD的处理动作 置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉 signal(17,SIG_IGN);


http://www.ppmy.cn/ops/95908.html

相关文章

在线客服系统 - 温州小拼科技有限公司

在当今数字化时代&#xff0c;企业与客户之间的沟通与联系日益依赖于高效、智能的客服系统。在众多客服解决方案中&#xff0c;“百度客服”、“商务通”、“美恰客服”、“53客服”以及“小拼客服”作为行业内的佼佼者&#xff0c;各自以其独特的功能优势和服务理念&#xff0…

09-JavaScript(代码)

01-二级导航 <style>* {margin: 0;padding: 0;list-style: none;}ul {width: 500px;height: 30px;margin: 30px auto;position: relative;}li {width: 100px;height: 30px;border: 1px solid #000;text-align: center;line-height: 30px;box-sizing: border-box;float: …

JavaScript初级——基础知识

一、JS的HelloWord 1、JS的代码需要编写到script标签中 2、JS的执行是根据语句从上到下一次执行的。 二、JS的编写位置 1、可以将js代码编写到标签的onclick属性中&#xff0c;当我们点击按钮时&#xff0c;js代码才会执行。 2、可以将js代码写在超链接的href属性中&#xff0…

前端创作纪念日

机缘 作者也是一名新人大学生&#xff0c;在学习过程中总是get不到专业的知识体系&#xff0c;机缘巧合下了解通过md文档记笔记然后分享在各大博客平台上面&#xff0c;可以吸引社区博客朋友们的关注的鼓励&#xff0c;使得直接创作努力学习的心更加澎湃。 实战项目中的经验分…

Redis 为什么读写性能高?

Redis 为什么读写性能高&#xff1f; Redis 作为一种开源的、基于内存的数据结构存储系统&#xff0c;以其卓越的读写性能而闻名。它被广泛应用于缓存、消息队列、实时数据处理等场景。那么&#xff0c;是什么使得 Redis 拥有如此高的读写性能呢&#xff1f;本文将从以下几个方…

qt-11基本对话框(消息框)

基本对话框--消息框 msgboxdlg.hmsgboxdlg.cppmain.cpp运行图QustionMsgInFormationMsgWarningMsgCriticalMsgAboutMsgAboutAtMsg自定义 msgboxdlg.h #ifndef MSGBOXDLG_H #define MSGBOXDLG_H#include <QDialog> #include <QLabel> #include <QPushButton>…

Unity教程(十)Tile Palette搭建平台关卡

Unity开发2D类银河恶魔城游戏学习笔记 Unity教程&#xff08;零&#xff09;Unity和VS的使用相关内容 Unity教程&#xff08;一&#xff09;开始学习状态机 Unity教程&#xff08;二&#xff09;角色移动的实现 Unity教程&#xff08;三&#xff09;角色跳跃的实现 Unity教程&…

站易WordPress

站易WordPress是一家专业提供网站建设和运营服务的公司&#xff0c;成立于2014年6月11日。他们提供的服务范围广泛&#xff0c;包括企业官方网站建设、网站运营维护、网站托管、网站优化、跨境独立站建站、外贸网站建设以及海外多语言网站建设等。 站易WordPress不仅专注于传统…