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

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);
             };

测试:

cpp 复制代码
#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信号但是回收了多个子进程的情况,从而避免产生僵尸进程

cpp 复制代码
#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;
}

结果:

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

cpp 复制代码
#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;
			}
		}
	}
}

结果:

相关推荐
纸包鱼最好吃2 分钟前
java基础-package关键字、MVC、import关键字
java·开发语言·mvc
njsgcs3 分钟前
opencascade.js stp vite webpack 调试笔记
开发语言·前端·javascript
PgSheep9 分钟前
Spring Cloud Gateway 聚合 Swagger 文档:一站式API管理解决方案
java·开发语言
林鸿群12 分钟前
go语言实现IP归属地查询
开发语言·golang·ip归属地
xmweisi0213 分钟前
Ansible内置模块之 group
linux·运维·ansible·rhce·rhca·红帽认证
学地理的小胖砸19 分钟前
【Python 异常处理】
开发语言·python
小猪写代码19 分钟前
Ubuntu 系统默认已安装 python,此处只需添加一个超链接即可
linux·python·ubuntu
孤寂大仙v1 小时前
【Linux笔记】——Linux线程理解与分页存储的奥秘
linux·运维·笔记
程序员拂雨1 小时前
Java知识框架
java·开发语言
Jasmine_llq1 小时前
《P4391 [BalticOI 2009] Radio Transmission 无线传输 题解》
算法·字符串·substr