Linux中sigaction函数和SIGCHLD信号的使用

news2025/1/17 22:00:33

sigaction函数:

函数说明:注册一个信号处理函数

函数原型:int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);

函数参数:

  • signum:捕捉的信号
  • act:传入参数,新的处理方式
  • oldact:传出参数,旧的处理方式
 The sigaction structure is defined as something like:

           struct sigaction {
               void     (*sa_handler)(int);//信号处理函数
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;//信号处理函数执行期间需要阻塞的信号,信号处理函数结束后,就不阻塞了
               int        sa_flags;//通常为0,表示默认标识
               void     (*sa_restorer)(void);
           };

测试:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include <signal.h>
#include <sys/time.h>
void handler(int signo)
{
	printf("signo==[%d]\n",signo);
	sleep(5);
}
int main()
{
	//int sigaction(int signum, const struct sigaction *act,
	//     struct sigaction *oldact);
	struct sigaction act;
	act.sa_handler=handler;
	sigemptyset(&act.sa_mask);把set信号集全部置0,不阻塞任何信号
	act.sa_flags=0;
	sigaction(SIGINT,&act,NULL);
	while(1)
	{
		sleep(1);
	}
	return 0;
}

结果:


 

由结果我们可以知道在XXX信号处理函数执行期间,当XXX信号产生多次,该信号进入未决信号集中(被阻塞),在信号处理函数执行结束后,只会执行一次(信号不支持排队)。

若在sa_mask中阻塞了YYY信号,那么在XXX信号处理函数执行时,产生的YYY信号也会进入未决信号集中(被阻塞),等到XXX信号处理函数结束后执行一次。

SIGCHLD信号:

产生SIGCHLD信号的条件:

  • 子进程结束的时候
  • 子进程收到SIGSTOP信号
  • 当子进程停止时,收到SIGCONT信号

SIGCHLLD信号的作用:

子进程退出后,内核会给它的父进程发送SIGCHLD信号,父进程收到这个信号后可以使用wait/waitpid函数对子进程进行回收。 

父进程创建两个子进程,然后让父进程捕获SIGCHLD信号完成对子进程的回收

注意点:

有可能还未完成注册信号处理函数,两个子进程已经都退出了

解决方法:可以在fork之前先将SIGCHLD阻塞,当完成信号处理函数的注册后解除阻塞

当SIGCHLD信号函数处理期间,SIGCHLD信号若再次产生是被阻塞的,而且产生多次,最后只会被处理一次,这样就会产生僵尸进程

解决方法:可以在信号处理函数里面使用while(1)循环回收,这样就有可能出现捕获一次SIGCHLD信号但是回收了多个子进程的情况,从而避免产生僵尸进程

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<signal.h>
void handler(int signo)//信号处理函数
{
	//pid_t waitpid(pid_t pid, int *wstatus, int options);
	pid_t wpid;
	while(1)//一直死循环,防止信号处理函数执行的时候,有多个子进程终止(只会接收一次SIGCHLD信号),产生僵尸进程
	{
		wpid=waitpid(-1,NULL,WNOHANG);//设为非阻塞,因为子进程收到SIGSTOP,SIGCONT信号,也会发出SIGCHLD信号,这样一来并没有子进程终止,就会一直阻塞
		if(wpid>0)
		{
			printf("[%d]child is quit\n",wpid);
		}
		else if(wpid==0)//还有子进程运行,break,等下一个SIGCHLD信号
		{
			break;
		}
		else if(wpid==-1)//没有子进程,已经全部回收,break
		{
			printf("no child is living\n");
			break;
		}
	}
}
int main()
{
//先把SIGCHLD信号阻塞,防止还没有注册信号处理函数时,就已经有SIGCHLD信号产生,被忽略
	sigset_t set;
	sigemptyset(&set);
	sigaddset(&set,SIGCHLD);
// int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
	sigprocmask(SIG_BLOCK,&set,NULL);

	int i=0;
	for(;i<2;i++)//循环产生两个兄弟子进程
	{
		pid_t pid=fork();
		if(pid<0)
		{
			perror("fork error");
			return -1;
		}
		else if(pid>0)
		{
			printf("father:pid=[%d]\n",getpid());
		}
		else if(pid==0)
		{
			break;
		}
	}

	if(i==0)
	{
		printf("[%d]child:pid=[%d],fpid=[%d]\n",i,getpid(),getppid());
	}
	else if(i==1)
	{
		printf("[%d]child:pid=[%d],fpid=[%d]\n",i,getpid(),getppid());
	}
	else if(i==2)
	{
		struct sigaction act;
		act.sa_handler=handler;
		sigemptyset(&act.sa_mask);
		act.sa_flags=0;
		sigaction(SIGCHLD,&act,NULL);//注册信号处理函数

		sigprocmask(SIG_UNBLOCK,&set,NULL);//把SIGCHLD信号设置为非阻塞

		while(1)
		{
			sleep(1);
		}
	}
	return 0;
}

结果:

 

使用信号传递让父子进程来回计数

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
//定义成全局变量,不然回调函数无法辨认
int num=0;//从0开始计数
int flag=0;//开关变量
void func1(int signo)//父进程的信号处理函数
{
	printf("f:[%d]\n",num);
	num+=2;
	flag=0;
	sleep(1);
}
void func2(int signo)//子进程的信号处理函数
{
	printf("c:[%d]\n",num);
	num+=2;
	flag=0;
	sleep(1);
}
int main()
{
	pid_t pid=fork();
	if(pid<0)
	{
		perror("fork error");
		return -1;
	}
	else if(pid>0)
	{
		signal(SIGUSR1,func1);//注册信号处理函数
		flag=1;//开关初始为1,让子进程先发出SIGUSR2信号给父进程
		while(1)
		{
			if(flag==0)
			{
				kill(pid,SIGUSR2);
				flag=1;
			}
		}
	}
	else if(pid==0)
	{
		num=1;//子进程从一开始计数
		signal(SIGUSR2,func2);
		while(1)
		{
			if(flag==0)
			{
				kill(getppid(),SIGUSR1);
				flag=1;
			}
		}
	}
}

 结果:

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

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

相关文章

【C++笔记】第一阶段:C++基础入门

C基础入门 1 C初识1.1 创建一个C程序1.1.1 创建项目1.1.2 创建文件1.1.3 编写代码1.1.4 运行程序 1.2 注释1.3 变量1.4 常量1.5 关键字1.6 标识符命名规则 2 数据类型2.1 整型2.2 sizeof关键字2.3 实型&#xff08;浮点型&#xff09;2.4 字符型2.5 转义字符2.6 字符串型2.7 布…

单源最短路建图方式例题整理

1129. 热浪&#xff08;活动 - AcWing&#xff09; 思路&#xff1a;题目只是加了一个背景&#xff0c;但实际上还是很裸的单源最短路问题。我们有四种写法&#xff0c;bellman_ford算法时间复杂度不如spfa&#xff0c;而且这里对边数没有限定&#xff0c;所以没必要使用bellma…

爱上JVM——常见问题:JVM组成(一)

1 JVM组成 1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f; 难易程度&#xff1a;☆☆☆ 出现频率&#xff1a;☆☆☆☆ JVM是什么 Java Virtual Machine Java程序的运行环境&#xff08;java二进制字节码的运行环境&#xff09; 好处&#xff1a; 一次编写&…

【AIGC】Stable Diffusion的模型入门

下载好相关模型文件后&#xff0c;直接放入Stable Diffusion相关目录即可使用&#xff0c;Stable Diffusion 模型就是我们日常所说的大模型&#xff0c;下载后放入**\webui\models\Stable-diffusion**目录&#xff0c;界面上就会展示相应的模型选项&#xff0c;如下图所示。作者…

【JAVA-Day81】 线程休眠: Java 中暂停线程执行的方法 ⏸️

线程休眠&#xff1a; Java 中暂停线程执行的方法 ⏸️&#x1f4a4; 线程休眠&#xff1a; Java 中暂停线程执行的方法 ⏸️&#x1f4a4;摘要 &#x1f4dd;引言 &#x1f680;正文 &#x1f4da;一、什么是线程休眠 ⏸️二、线程什么情况下会休眠 ❓三、模拟线程休眠 &#…

jenkins-maven环境的安装

jenkins-maven环境的安装

leetcode 160 相交链表

题目 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返回结…

Gemini 1.5 Pro揭秘:Google DeepMind新一代AI模型如何突破千万级别词汇限制?

Gemini 1.5 Pro 发布&#xff01; 这款模型凭借其超长的上下文处理能力脱颖而出&#xff0c;支持10M tokens。 它的多模态特性意味着&#xff0c;无论面对多么庞大复杂的内容&#xff0c;Gemini 1.5 Pro都能游刃有余地应对。 在AI的世界里&#xff0c;上下文的理解如同记忆的…

【类与对象(1)】类的引入、访问及封装、定义、作用域、实例化、类大小的计算、this指针

1.类的引入 C兼容了C语言结构体的用法&#xff0c;但是同时又升级成了类。结构体中只能定义变量&#xff0c;类中不仅可以定义变量&#xff0c;还可以定义函数。 例如&#xff0c;数据结构中实现栈&#xff0c;结构体stack中只定义了变量&#xff0c;要实现的函数在结构体外定…

npm使用国内淘宝镜像(最新地址)

目录 前言 一、命令配置 二、使用cnpm安装 三、常见包地址 四、总结 往期回顾 前言 我们前端程序员在使用国外的镜像源速度很慢并且容易下载失败&#xff0c;有时候需要尝试多次才有可能下载成功&#xff0c;很麻烦&#xff0c;但是可以切换为国内镜像源&#xff0c;下…

Node.js开发-express框架

express框架 1) 介绍2) express 路由3) express 响应设置4) express 其他响应5) express中间件6) 静态资源中间件7) 获取请求体数据 body-parser8) Router 路由模块化9) EJS 模板引擎 1) 介绍 express 是一个基于 Node.js 平台的极简、灵活的 WEB 应用开发框架&#xff0c;官方…

MySQL学习记录——팔 函数

文章目录 1、日期函数2、字符串函数3、数学函数4、其它函数 1、日期函数 //获取日期 select current_date(); //获取时间 select current_time(); //获取时间戳, 格式为日期时间 select current_timestamp(); //获取当前时间, 格式为日期时间 select now(); //获取参数的日期部…

单html页面使用Vue3和Element-Plus

一、快速入门 使用CDN方式引入Vue3使用CDN方式引入Element-Plus的样式文件和组件库 案例 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, ini…

Linux学习(二)Makefile与GCC

程序运行步骤&#xff0c;预处理->预编译->编译->链接 1.预处理 gcc -E 文件名&#xff0c;生成的文件不会存储&#xff0c;可以重定向 > 到另一个文件中 2.编译 gcc -c *.c -o *.o 编译生成的文件为.o 可以将它编译为汇编 gcc -S 文件名 3.链接 gcc *.o *.…

投资银行在网络安全生态中的作用

文章目录 一、投资银行的含义(一)并购买方。(二)并购卖方。(三)IPO辅助。(四)投资银行业务的另一方面是帮助这些交易融资。二、从投资银行角度看网络安全产业(一)行业的短期前景三、复杂的网络安全并购(一)行业知识对投资银行业务很重要(二)在网络安全领域,技术…

《区块链公链数据分析简易速速上手小册》第8章:实战案例研究(2024 最新版)

文章目录 8.1 案例分析&#xff1a;投资决策支持8.1.1 基础知识8.1.2 重点案例&#xff1a;股票市场趋势预测准备工作实现步骤步骤1: 加载和准备数据步骤2: 特征工程步骤3: 训练模型步骤4: 评估模型 结论 8.1.3 拓展案例 1&#xff1a;基于情感分析的投资策略准备工作实现步骤步…

【Python】通过conda安装Python的IDE

背景 系统&#xff1a;win11 软件&#xff1a;anaconda Navigator 问题现象&#xff1a;①使用Navigator安装jupyter notebook以及Spyder IDE 一直转圈。②然后进入anaconda prompt执行conda install jupyter notebook一直卡在Solving environment/-\。 类似问题&#xff1a; …

专业140+总分420+东北大学841通信专业基础考研经验东大电子信息与通信工程,真题,大纲,参考书。

今年考研顺利上岸&#xff0c;被东北大学通信工程录取&#xff0c;其中专业课841通信专业基础140&#xff0c;数二140&#xff0c;总分420&#xff0c;整体每门课都还是比较均衡&#xff0c;刚开始考研前也和大家一样&#xff0c;焦虑&#xff0c;紧张&#xff0c;面对考研怕失…

软件测试【三】Python中的数据类型

一、Python中的数据类型: python中的list列表定义 在Python中&#xff0c;list是一种有序的数据类型&#xff0c;可以存储任意类型的对象&#xff0c;包括数字、字符串、布尔值、函数等。 定义一个list列表可以使用中括号[]来表示&#xff0c;其中每个元素之间用逗号隔开。以…

VitePress-16- 配置- head 的配置网页icon与插入一个script标签

作用说明 head 配置项&#xff0c;可以在页面 HTML 的 <head> 标签中呈现的其他元素。 用户添加的标签在结束 head 标签之前呈现&#xff0c;在 VitePress 标签之后。说白了&#xff0c;就是自定义一些 head 标签中的元素&#xff0c;例如 &#xff1a;页面的icon等。 由…