Linux c 通过信号回收N个子进程
1、信号基础概念
信号共性:
简单、不能携带大量信息,满足条件才发送
信号特质:
信号是软件层面上的”中断“,一旦信号产生,无论程序执行到什么位置,必须立即停止运行,处理结束,在继续执行后续指令
# 所有信号的产生和发送都是内核来处理的
递达:成功由内核产生,并且成功发送到指定进程
未决:处于发送和递达之间的状态(主要由于阻塞屏蔽导致该转态)
信号屏蔽字:
将某些信号加入集合,对他们设置屏蔽,当屏蔽x信号后,在收到该信号,该信号的处理将推后(解除屏蔽后在处理)
未决信号集(用来记录信号处理状态):
1、信号产生,未决信号集中描述该信号的位立即翻转为1,表信号处于未决状态,当信号被处理对应位翻转回为0,这一时刻往往非常短暂
2、信号产生后由于某些原因(主要是阻塞)不能抵达,这类信号的集合称之为未决信号集,在屏蔽解除前,信号一直处于未决转态
# 信号处理方式:
1、执行默认动作
2、忽略(丢弃)
3、捕捉(调用户处理函数)
2、计时函数使用
2.1、alarm函数(定时发送SIGALRM给当前进程,默认动作为终止进程)
unsigned int alarm(unsigned int seconds);
参数:
seconds 定时秒数
返回值:
上次定时闹钟剩余时间
无错误现象
2.2、setitimer函数(定时发送SIGALRM给当前进程,默认动作为终止进程)
int setitimer(int which,const struct itimerval* new_value,struct itimerval* old_value);
参数:
which
ITIMER_REAL (14 SIGLARM) 自然时间
ITIMER_VIRTUAL(26 SIGVTALRM) 虚拟空间时间(只计算进程占用cpu的时间)
ITIMER_PROF (27 SIGPROF) 运行时计时(用户+内核,)
new_value 定时秒数
it_interval:用来设定两次定时任务之间间隔的时间
it_value:定时的时长
old_value 上次定时闹钟剩余秒数
返回值:
成功 0
失败 -1
两个都是计时函数,但是第二个更精细,可以设置每次调用的间隔时间,和微秒数
2.3、使用两个还是分别实现1秒中,能打印多少数到屏幕
alarm函数
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int main()
{
int i=1;
alarm(1);
while(i++)
{
printf("%d\n",i);
}
return 0;
}
setitimer函数
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <signal.h>
void sys_perror(const char* str)
{
perror(str);
exit(1);
}
int main()
{ // 参数初始化
struct itimerval it,oldit;
it.it_value.tv_sec = 1; // 设置秒说
it.it_value.tv_usec = 0;
it.it_interval.tv_sec = 0; // 设置间隔为0秒
it.it_interval.tv_usec = 0;
// 错误检查
if(setitimer(ITIMER_REAL,&it,&oldit) == -1)
sys_perror("setitimer error");
int i=1;
while (i++)
{
printf("%d\n",i);
}
return 0;
}
两个函数实现效果一样,程序运行1秒钟之后就会,发送SIGALRM给当前进程,默认动作为终止进程
上面的概念说到,信号的处理动作有三种:执行默认动作、忽略、捕捉
现在我们将SIGALRM进行捕捉,然后执行我们自定义的函数,让他每次发送SIGALRM信号打印“hello world”
2.4、通过signal函数捕捉SIGALRM信号
signal函数
typedef void (*sighandler_t)(int);
sighandler_t signal(int signo,sighandler_t handler);
参数:
signo 信号编号
handler 回调函数(返回值void参数为int的函数类型)
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <signal.h>
void sys_perror(const char* str)
{
perror(str);
exit(1);
}
void fun2(int signo)
{
printf("hello world\n");
}
int main()
{
struct itimerval it,oldit;
signal(SIGALRM,fun2); // 注册SIGALRM信号的捕捉处理函数
it.it_value.tv_sec = 2; // 设置秒说
it.it_value.tv_usec = 0;
it.it_interval.tv_sec = 5; // 设置间隔为5秒
it.it_interval.tv_usec = 0;
if(setitimer(ITIMER_REAL,&it,&oldit) == -1)
sys_perror("setitimer error");
while (1); // 模拟下面行为
return 0;
}
2.5、通过sigaction捕捉ctrl+c(用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止进程。)
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
参数:
signum 信号编号
act 默认需要初始化结构体
oldact 旧的结构体(一步不使用可以传NULL)
返回值:
成功 0
失败 -1
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <signal.h>
void sys_perror(const char* str)
{
perror(str);
exit(1);
}
void cat_sig(int signo)
{
printf("hello world %d\n",signo);
}
int main()
{
struct sigaction act,oldact;
act.sa_handler = cat_sig; // 设置信号捕捉函数
sigemptyset(&(act.sa_mask)); // 设置信号捕捉期间mask
act.sa_flags = 0; // 设置默认属性
int ret = sigaction(SIGINT,&act,&oldact);
if(ret == -1)
sys_perror("sigaction error");
// signal(SIGINT,cat_sig);
while(1);
return 0;
}
信号捕捉特性:
sa_mask 只工作信号捕捉函数执行期间,
1、捕捉函数执行期间,信号屏蔽字,由mask->sa_mask,捕捉函数执行结束,恢复mask
2、捕捉函数执行期间,本信号自动被屏蔽(sa_flgs=0)
3、捕捉函数执行期间,被屏蔽信号多次发送,解除屏蔽后只处理一次