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

测试:

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

 结果:

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
sigaction函数是用于设置信号处理函数函数,其主要函数原型为: ```c int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); ``` 其,参数signum表示要设置的信号的编号,act表示新的信号处理函数及相关属性,oldact表示原先的信号处理函数及相关属性。具体参数详解如下: - signum:表示要设置的信号的编号,可以是任何合法的信号编号。 - act:一个指向sigaction结构体的指针,用于设置新的信号处理函数及相关属性。该结构体包含如下成员: - sa_handler:表示信号处理函数的地址,可以是一个函数指针,也可以是SIG_IGN、SIG_DFL等特殊值。 - sa_mask:表示在执行信号处理函数时需要屏蔽的一组信号。这些信号信号处理函数执行期间被屏蔽,以防止被其它信号断。 - sa_flags:表示一些额外的标志位,如SA_RESTART表示系统调用被信号断时自动重启等。 - oldact:一个指向sigaction结构体的指针,用于保存原先的信号处理函数及相关属性。如果不需要保存,可以将这个参数设置为NULL。 下面是一个简单的例子,用于设置SIGINT信号处理函数为一个自定义函数: ```c #include <stdio.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> void sigint_handler(int signum) { printf("Received SIGINT signal: %d\n", signum); exit(0); } int main() { struct sigaction sa; sa.sa_handler = sigint_handler; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); if (sigaction(SIGINT, &sa, NULL) == -1) { perror("sigaction"); exit(1); } while (1) { printf("Waiting for SIGINT signal...\n"); sleep(1); } return 0; } ``` 在上述代码,我们首先定义了一个名为sigint_handler的函数,该函数会在接收到SIGINT信号时被调用。然后,我们定义了一个名为sa的sigaction结构体,用于设置SIGINT信号处理函数和相关属性。最后,我们通过调用sigaction函数来将SIGINT信号处理函数设置为sigint_handler函数。 当我们运行上述程序后,程序会进入一个无限循环,每秒钟输出一次“Waiting for SIGINT signal…”。当我们在终端按下Ctrl+C键时,程序会接收到SIGINT信号,并且会调用sigint_handler函数来处理该信号,输出一条信息并退出程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落落落sss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值