父进程管理两个子进程,实现功能如下
1. 父进程被kill之后,子进程也会被kill
2. 子进程被kill之后,子进程会被父进程重新拉起
3. 此进程为守护进程
编译后运行编译出来的文件,发现已经是守护进程了
执行ps -ef| grep maggie命令,会发现有三个同名进程,一个父进程,两个子进程
kill子进程,父进程会将其重新拉起
kill父进程,父进程会将结束,并同时杀死两个子进程
kill父进程时会发现,waitpid函数返回-1
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include <map>
#include <string>
using std::map;
using std::string;
volatile int sigFlag;
void SignalHandler(int sig)
{
printf("SignalHandler, sig:%d, pid: %d\n", sig, getpid());
sigFlag = 1;
}
void SetSignal()
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
//void (*sa_handler)(int), 信号处理函数
//sigset_t sa_mask, 用来设置信号屏蔽集,当信号函数返回后,还原
//int sa_flags, 指定对信号处理的各个选项
//void (*sa_signation)(int, siginfo_t *, void *), 是一个替代的信号处理程序,当sa_flag为SA_SIGINFO时,使用该处理程序。比sa_handler带有更多的参数
sa.sa_handler = SignalHandler;
//sigaction函数
//1. 第一个参数,捕捉的信号
//2. 第二个参数,相应信息
//3. 第三个参数, 如果为非空,会返回该信号的上一个动作
//设置主进程如果被中断了,如何处理函数
//
//SIGINT: 用户按中断键(一般采用Delete或者Ctril+C),终端驱动程序产生此信号,发送给前台进程组中的每个进程
sigaction(SIGINT, &sa, NULL);
//SIGHUP: 与终端链接断开
sigaction(SIGHUP, &sa, NULL);
//SIGTERM:由kill(1)命令发送的系统默认终止信号
sigaction(SIGTERM, &sa, NULL);
//SIGQUIT: 当用户在终端按退出键(一般采用Ctrl+\),此信号不止终止钱袋进程组,同时产生一个core文件
sigaction(SIGQUIT, &sa, NULL);
//signal(SIGPIPE, SIG_IGN)
//当服务器close一个连接时,若client端接着发数据。
//根据TCP 协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。
//根据信号的默认处理规则SIGPIPE信号的默认执行动作是terminate(终止、退出),所以client会退出。
//若不想客户端退出可以把SIGPIPE设为SIG_IGN
signal(SIGPIPE,SIG_IGN);
//取消以下信号的屏蔽
sigset_t sset;
sigemptyset(&sset);
sigaddset(&sset, SIGBUS);
sigaddset(&sset, SIGILL);
sigaddset(&sset, SIGFPE);
sigaddset(&sset, SIGSEGV);
sigaddset(&sset, SIGCHLD);//设置child中断时产生的信号不会被阻塞
sigaddset(&sset, SIGABRT);
//设置信号屏蔽
//1. 第一个参数, SIG_BLOCK是或操作,表示新增需要被屏蔽的信号
//SIG_UNBLOCK是取消以下信号的阻塞
//SIG_SETMASK是赋值操作,设置需要屏蔽的信号,并与原先无关
//2. 第二个参数,需要修改的信号
//3. 第三个参数,如果不是空,获得操作后的信号屏蔽信息
sigprocmask(SIG_UNBLOCK, &sset, &sset);
}
void init()
{
sigFlag = 0;
//设置信号
SetSignal();
//设置守护进程
//1.第一个参数:为0时,将根目录修改为工作目录
//2.第二个参数:为0时,做输入,输出以及错误输出重定向到/dev/null
daemon(1, 1);
}
pid_t ForkChild(int channel)
{
pid_t p = fork();
if(p < 0) {
return -1;
}
if(p != 0){ //parent
return p;
}
printf("creat child[%d]\n", channel);
//child
while(!sigFlag){
printf("child[%d]\n", channel);
sleep(3);
}
printf("-----------child[%d]_end------------\n", channel);
//必须是exit不能是return,否则会继续进行
exit(10);
}
int main()
{
//初始化
init();
map<pid_t, int> list;
map<pid_t, int>::iterator iter;
//生成两个子进程
for(int i = 0; i < 2; i++){
pid_t p = ForkChild(i);
list[p] = i;
}
while(!sigFlag){
int status = 0;
//watipid函数用来等待父进程接收到子进程的SIGCHLD信号
//1. 第一个参数,pid_t pid
//pid == -1: 等待任一子进程,此种情况下与wait函数相等
//pid > 0: 等待进程ID与pid相等的子进程
//pid == 0:等待组ID等于调用进程组ID的任一子进程
//pid < -1:等待组ID等于pid绝对值的任一子进程
//2. 第二个参数,int *statloc,存放终止进程的终止状态
//3. 第三个参数 int option, 进一步控制waitpid操作,可支持作业控制,以及设置wait的非阻塞版本,<<Uinix环境高级编程>>P193
//4. 成功返回进程id,失败返回0
printf("--------waitpid----------\n");
pid_t p = waitpid(-1, &status, 0);//此处waitpid相当于wait函数
if(p == 0) {
continue;
}
printf("--------pid:%d----------\n", p);
iter = list.find(p);
if(iter == list.end()){
printf("GET error pid!");
break;
}
//重新拉起子进程
int channel = iter->second;
list.erase(iter);
p = ForkChild(channel);
list[p] = channel;
}
printf("-----sigFlag:%d-------------\n",sigFlag);
//父进程被kill之后,杀死两个子进程
for(iter = list.begin(); iter != list.end(); iter++) {
if(iter->first != -1){
kill(iter->first, SIGTERM);
}
}
return 0;
}