Linux C++ 创建子进程(设置信号集、设置进程标题、创建守护进程)

1、测试代码

思路:创建一个master进程,多个work进程,master进程主要用于管理work进程,work进程做具体的业务处理

流程:
1、信号初始化,给进程注册新号处理函数,当master进程收到SIGCHLD信号时,获取子进程结束状态,防止子进程变成僵尸进程。
2、创建子进程,并给master进程和子进程设置标题,master进程主要通过信号驱动,没接收到信号的时候挂起,work进程做主要的业务处理,master进程主要通过sigsuspend函数操作

 // 阻塞在这里等待一个信号,此时进程是挂起的,不占用资源
 sigsuspend函数做了一下四个操作
 // 1、根据给定参数设置新的mask(将信号集设置为空)
 // 2、一旦收到信号,便恢复原先信号屏蔽(屏蔽之前上面设置的信号)
 // 3、调用该信号的信号处理程序
 // 4、信号处理程序返回后,sigsuspend返回
// ps -eo pid,ppid,sid,tty,pgrp,comm,stat,cmd |grep -E 'bash|PID|createWorkPro'
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;

//一个信号有关的结构 ngx_signal_t
typedef struct 
{
    int           signo;       //信号对应的数字编号 ,每个信号都有对应的#define
    const  char   *signame;    //信号对应的中文名字 ,比如SIGHUP 
    //信号处理函数,这个函数由我们自己来提供,但是它的参数和返回值是固定的
    void  (*handler)(int signo, siginfo_t *siginfo, void *ucontext); //函数指针,   siginfo_t:系统定义的结构
} ngx_signal_t;

//声明一个信号处理函数
static void ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext); //static表示该函数只在当前文件内可见
static void ngx_process_get_status(void);                                      //获取子进程的结束状态,防止单独kill子进程时子进程变成僵尸进程
int init_signals();     // 初始化信号处理函数
void ngx_master_process_cycle(); //描述:创建worker子进程
void ngx_setproctitle(const char *title);   // 设置进程标题
//threadnums:要创建的子进程数量
static void ngx_start_worker_processes(int threadnums);
//描述:产生一个子进程
//inum:进程编号【0开始】
//pprocname:子进程名字"worker process"
static int ngx_spawn_process(int inum,const char *pprocname);
//描述:worker子进程的功能函数,每个woker子进程,就在这里循环着了(无限循环【处理网络事件和定时器事件以对外提供web服务】)
//     子进程分叉才会走到这里
//inum:进程编号【0开始】
static void ngx_worker_process_cycle(int inum,const char *pprocname);

#define NGX_PROCESS_MASTER     0  //master进程,管理进程
#define NGX_PROCESS_WORKER     1  //worker进程,工作进程
int ngx_process = NGX_PROCESS_MASTER;
static u_char  master_process[] = "master process";
size_t  g_argvneedmem=0;        //保存下这些argv参数所需要的内存大小
size_t  g_envneedmem=0;         //环境变量所占内存大小
int     g_os_argc;              //参数个数 
char    **g_os_argv;            //原始命令行参数数组,在main中会被赋值
char    *gp_envmem=NULL;        //指向自己分配的env环境变量的内存,在ngx_init_setproctitle()函数中会被分配内存
#define WORKNUM 5

//在实际商业代码中,你能想到的要处理的信号,都弄进来
ngx_signal_t  signals[] = {
    // signo      signame             handler
    { SIGHUP,    "SIGHUP",           ngx_signal_handler },        //终端断开信号,对于守护进程常用于reload重载配置文件通知--标识1
    { SIGINT,    "SIGINT",           ngx_signal_handler },        //标识2   
	{ SIGTERM,   "SIGTERM",          ngx_signal_handler },        //标识15
    { SIGCHLD,   "SIGCHLD",          ngx_signal_handler },        //子进程退出时,父进程会收到这个信号--标识17
    { SIGQUIT,   "SIGQUIT",          ngx_signal_handler },        //标识3
    { SIGIO,     "SIGIO",            ngx_signal_handler },        //指示一个异步I/O事件【通用异步I/O信号】
    { SIGSYS,    "SIGSYS, SIG_IGN",  NULL               },        //我们想忽略这个信号,SIGSYS表示收到了一个无效系统调用,如果我们不忽略,进程会被操作系统杀死,--标识31
                                                                  //所以我们把handler设置为NULL,代表 我要求忽略这个信号,请求操作系统不要执行缺省的该信号处理动作(杀掉我)
    //...日后根据需要再继续增加
    { 0,         NULL,               NULL               }         //信号对应的数字至少是1,所以可以用0作为一个特殊标记
};

int main(int argc,char* argv[])
{
    //统计argv所占的内存
    g_argvneedmem = 0;
    for(int i = 0; i < argc; i++)  //argv =  ./nginx -a -b -c asdfas
    {
        g_argvneedmem += strlen(argv[i]) + 1; //+1是给\0留空间。
    } 
    //统计环境变量所占的内存。注意判断方法是environ[i]是否为空作为环境变量结束标记
    for(int i = 0; environ[i]; i++) 
    {
        g_envneedmem += strlen(environ[i]) + 1; //+1是因为末尾有\0,是占实际内存位置的,要算进来
    } //end for
    g_os_argc = argc;           //保存参数个数
    g_os_argv = (char **) argv; //保存参数指针

    //设置可执行程序标题相关函数:分配内存,并且把环境变量拷贝到新内存中来
    //这里无需判断penvmen == NULL,有些编译器new会返回NULL,有些会报异常,但不管怎样,如果在重要的地方new失败了,你无法收场,让程序失控崩溃,助你发现问题为好; 
    gp_envmem = new char[g_envneedmem]; 
    memset(gp_envmem,0,g_envneedmem);  //内存要清空防止出现问题
    char *ptmp = gp_envmem;
    //把原来的内存内容搬到新地方来
    for (int i = 0; environ[i]; i++) 
    {
        size_t size = strlen(environ[i])+1 ; //不要拉下+1,否则内存全乱套了,因为strlen是不包括字符串末尾的\0的
        strcpy(ptmp,environ[i]);      //把原环境变量内容拷贝到新地方【新内存】
        environ[i] = ptmp;            //然后还要让新环境变量指向这段新内存
        ptmp += size;
    }

    // 初始化信号函数,注册信号处理程序
    int errCode = init_signals();
    if(errCode!=0)
    {
        printf("init_signals failed\n");
        return 1;
    }

    // 创建子进程
    ngx_master_process_cycle();
    return 0;
}

// 返回0成功,返回-1失败
int init_signals()
{
    ngx_signal_t* sig = NULL;
    struct sigaction sa;
    for(sig = signals; sig->signo != 0; sig++)
    {
        memset(&sa,0,sizeof(sa));
        // 如果信号处理函数为NULL 表示这个信号要忽略
        if(sig->handler)
        {
            sa.sa_sigaction = sig->handler;
            sa.sa_flags = SA_SIGINFO;
        }
        else
        {
            sa.sa_handler = SIG_IGN;
        }
        sigemptyset(&sa.sa_mask);
        
        // 设置信号处理函数
        if(sigaction(sig->signo,&sa,NULL)==-1)
        {
            printf("sigaction failed sig = %d\n",sig->signo);
            return -1;
        }
    }
    return 0;
}

// 信号处理函数
static void ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext)
{
    ngx_signal_t* sig;
    char* action;
    for(sig=signals; sig->signo!=0; sig++)
    {
        if(sig->signo==signo)
            break;
    }
    action = (char*)"";

    // 主进程信号
    if(ngx_process == NGX_PROCESS_MASTER)
    {
        cout<<"master来信号啦:"<<signo
            <<sig->signame<<endl;
    }
    else if(ngx_process == NGX_PROCESS_WORKER)
    {
        cout<<"worker来信号啦:"<<signo
            <<sig->signame<<endl;
    }
    else
    {

    }

    if(siginfo && siginfo->si_pid)
    {
        cout<<"signo:"<<signo
            <<"signame:"<<sig->signame
            <<"发生该信号的进程id:"<<siginfo->si_pid
            <<endl;
    }
    else
    {
        cout<<"signo:"<<signo
            <<"signame:"<<sig->signame
            <<endl;
    }

    if(signo == SIGCHLD)
    {   // 获取子进程结束状态
        ngx_process_get_status();       
    }
    return;
}

static void ngx_process_get_status(void)
{
    pid_t pid;
    int status;
    int err;
    int one = 0;
    for(;;)
    {
        pid = waitpid(-1,&status,WNOHANG);
        // 子进程没结束
        if(pid == 0)
        {
            return;
        }
        // waitpid调用有错误
        if(pid == -1)
        {
            err = errno;
            // 调用被某个信号中断
            if(err == EINTR)
            {
                continue;
            }
            if(err == ECHILD && one) // 没有子进程
            {
                return;
            }
            if(err == ECHILD)   // 没有子进程
            {
                cout<<"waitpid() failed !"<<endl;
                return;
            }
        }
        // 走到这里,表示成功,返回正常
        one = 1;
        if(WTERMSIG(status))
        {
            cout<<"pid= "<<pid
                <<"code:"<<WTERMSIG(status)
                <<endl;
        }
        else
        {
            cout<<"pid= "<<pid
                <<"code:"<<WEXITSTATUS(status)
                <<endl;
        }
    }
    return;
}

void ngx_master_process_cycle()
{
    sigset_t set;        //信号集
    sigemptyset(&set);   //清空信号集
    //建议fork()子进程时学习这种写法,防止信号的干扰;
    sigaddset(&set, SIGCHLD);     //子进程状态改变
    sigaddset(&set, SIGALRM);     //定时器超时
    sigaddset(&set, SIGIO);       //异步I/O
    sigaddset(&set, SIGINT);      //终端中断符
    sigaddset(&set, SIGHUP);      //连接断开
    sigaddset(&set, SIGUSR1);     //用户定义信号
    sigaddset(&set, SIGUSR2);     //用户定义信号
    sigaddset(&set, SIGWINCH);    //终端窗口大小改变
    sigaddset(&set, SIGTERM);     //终止
    sigaddset(&set, SIGQUIT);     //终端退出符

    if(sigprocmask(SIG_BLOCK,&set,NULL)==-1)
    {
        cout<<"ngx_master_process_cycle sigprocmask faile"<<endl;
        return;
    }

    // 为master进程设置名字
    size_t size;
    size = sizeof(master_process);
    size += g_argvneedmem;
    if(size<1000)
    {
        char title[1000] = {0};
        strcpy(title,(const char*)master_process);
        strcat(title," ");
        for(int i=0;i<g_os_argc;i++)
        {
            strcat(title,g_os_argv[i]);
        }
        ngx_setproctitle(title);
    }

    // 创建子进程
    ngx_start_worker_processes(WORKNUM);
    // 创建子进程后,父进程的执行流程会返回到这里,子进程不会进入了
    // 信号屏蔽字不需要屏蔽了
    sigemptyset(&set);
    for(;;)
    {
        // 阻塞在这里等待一个信号,此时进程是挂起的,不占用资源
        // 1、根据给定参数设置新的mask
        // 2、一旦收到信号,便恢复原先信号屏蔽
        // 3、调用该信号的信号处理程序
        // 4、信号处理程序返回后,sigsuspend返回
        sigsuspend(&set);
        cout<<"-----mastetr pid:"<<getpid()
        <<"ppid:"<<getppid()
        <<"master process"<<endl;
        sleep(1);
    }
    return;
}

//threadnums:要创建的子进程数量
static void ngx_start_worker_processes(int threadnums)
{
    for(int i=0;i<threadnums;i++)
    {
        ngx_spawn_process(i,"worker process");
    }
    return;
}

static int ngx_spawn_process(int inum,const char *pprocname)
{
    pid_t pid;
    pid = fork();
    switch (pid)
    {
    case -1:
        cout<<"创建进程失败"<<endl;
        break;
    case 0:
        ngx_worker_process_cycle(inum,pprocname);
    default:
        break;
    }
    return pid;
}

//描述:worker子进程的功能函数,每个woker子进程,就在这里循环着了(无限循环【处理网络事件和定时器事件以对外提供web服务】)
//     子进程分叉才会走到这里
//inum:进程编号【0开始】
static void ngx_worker_process_cycle(int inum,const char *pprocname)
{
    ngx_process = NGX_PROCESS_WORKER;
    sigset_t  set;      //信号集

    sigemptyset(&set);  //清空信号集
    if (sigprocmask(SIG_SETMASK, &set, NULL) == -1)  //原来是屏蔽那10个信号【防止fork()期间收到信号导致混乱】,现在不再屏蔽任何信号【接收任何信号】
    {
        cout<<"ngx_worker_process_cycle failed"<<endl;
    }
    ngx_setproctitle(pprocname);

    for(;;)
    {
        cout<<"pid:"<<getpid()
            <<"ppid:"<<getppid()
            <<"inum:"<<inum
            <<"work process"<<endl;
        sleep(3);
    }
}

void ngx_setproctitle(const char *title)
{
    //我们假设,所有的命令 行参数我们都不需要用到了,可以被随意覆盖了;
    //注意:我们的标题长度,不会长到原始标题和原始环境变量都装不下,否则怕出问题,不处理
    
    //(1)计算新标题长度
    size_t ititlelen = strlen(title); 

    //(2)计算总的原始的argv那块内存的总长度【包括各种参数】    
    size_t esy = g_argvneedmem + g_envneedmem; //argv和environ内存总和
    if( esy <= ititlelen)
    {
        //你标题多长啊,我argv和environ总和都存不下?注意字符串末尾多了个 \0,所以这块判断是 <=【也就是=都算存不下】
        return;
    }

    //空间够保存标题的,够长,存得下,继续走下来    

    //(3)设置后续的命令行参数为空,表示只有argv[]中只有一个元素了,这是好习惯;防止后续argv被滥用,因为很多判断是用argv[] == NULL来做结束标记判断的;
    g_os_argv[1] = NULL;  

    //(4)把标题弄进来,注意原来的命令行参数都会被覆盖掉,不要再使用这些命令行参数,而且g_os_argv[1]已经被设置为NULL了
    char *ptmp = g_os_argv[0]; //让ptmp指向g_os_argv所指向的内存
    strcpy(ptmp,title);
    ptmp += ititlelen; //跳过标题

    //(5)把剩余的原argv以及environ所占的内存全部清0,否则会出现在ps的cmd列可能还会残余一些没有被覆盖的内容;
    size_t cha = esy - ititlelen;  //内存总和减去标题字符串长度(不含字符串末尾的\0),剩余的大小,就是要memset的;
    memset(ptmp,0,cha);
}

查看当前进程信息,根据进程id发生信号,验证程序是否正确返回

ps -eo pid,ppid,sid,tty,pgrp,comm,stat,cmd |grep -E 'bash|PID|createWorkPro'

在这里插入图片描述

分别给master和work进程发生1这个信号,查看程序的打印

在这里插入图片描述

2、完整代码

添加守护进程代码,使程序脱离终端在后台运行

// ps -eo pid,ppid,sid,tty,pgrp,comm,stat,cmd |grep -E 'bash|PID|createWorkPro'
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;

//一个信号有关的结构 ngx_signal_t
typedef struct 
{
    int           signo;       //信号对应的数字编号 ,每个信号都有对应的#define
    const  char   *signame;    //信号对应的中文名字 ,比如SIGHUP 
    //信号处理函数,这个函数由我们自己来提供,但是它的参数和返回值是固定的
    void  (*handler)(int signo, siginfo_t *siginfo, void *ucontext); //函数指针,   siginfo_t:系统定义的结构
} ngx_signal_t;

//声明一个信号处理函数
static void ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext); //static表示该函数只在当前文件内可见
static void ngx_process_get_status(void);                                      //获取子进程的结束状态,防止单独kill子进程时子进程变成僵尸进程
int init_signals();     // 初始化信号处理函数
void ngx_master_process_cycle(); //描述:创建worker子进程
void ngx_setproctitle(const char *title);   // 设置进程标题
//threadnums:要创建的子进程数量
static void ngx_start_worker_processes(int threadnums);
//描述:产生一个子进程
//inum:进程编号【0开始】
//pprocname:子进程名字"worker process"
static int ngx_spawn_process(int inum,const char *pprocname);
//描述:worker子进程的功能函数,每个woker子进程,就在这里循环着了(无限循环【处理网络事件和定时器事件以对外提供web服务】)
//     子进程分叉才会走到这里
//inum:进程编号【0开始】
static void ngx_worker_process_cycle(int inum,const char *pprocname);
// 创建守护进程
static int ngx_daemon();
void freeresoure();

#define NGX_PROCESS_MASTER     0  //master进程,管理进程
#define NGX_PROCESS_WORKER     1  //worker进程,工作进程
int ngx_process = NGX_PROCESS_MASTER;
static u_char  master_process[] = "master process";
size_t  g_argvneedmem=0;        //保存下这些argv参数所需要的内存大小
size_t  g_envneedmem=0;         //环境变量所占内存大小
int     g_os_argc;              //参数个数 
char    **g_os_argv;            //原始命令行参数数组,在main中会被赋值
char    *gp_envmem=NULL;        //指向自己分配的env环境变量的内存,在ngx_init_setproctitle()函数中会被分配内存
#define WORKNUM 5

//在实际商业代码中,你能想到的要处理的信号,都弄进来
ngx_signal_t  signals[] = {
    // signo      signame             handler
    { SIGHUP,    "SIGHUP",           ngx_signal_handler },        //终端断开信号,对于守护进程常用于reload重载配置文件通知--标识1
    { SIGINT,    "SIGINT",           ngx_signal_handler },        //标识2   
	{ SIGTERM,   "SIGTERM",          ngx_signal_handler },        //标识15
    { SIGCHLD,   "SIGCHLD",          ngx_signal_handler },        //子进程退出时,父进程会收到这个信号--标识17
    { SIGQUIT,   "SIGQUIT",          ngx_signal_handler },        //标识3
    { SIGIO,     "SIGIO",            ngx_signal_handler },        //指示一个异步I/O事件【通用异步I/O信号】
    { SIGSYS,    "SIGSYS, SIG_IGN",  NULL               },        //我们想忽略这个信号,SIGSYS表示收到了一个无效系统调用,如果我们不忽略,进程会被操作系统杀死,--标识31
                                                                  //所以我们把handler设置为NULL,代表 我要求忽略这个信号,请求操作系统不要执行缺省的该信号处理动作(杀掉我)
    //...日后根据需要再继续增加
    { 0,         NULL,               NULL               }         //信号对应的数字至少是1,所以可以用0作为一个特殊标记
};

int main(int argc,char* argv[])
{
    //统计argv所占的内存
    g_argvneedmem = 0;
    for(int i = 0; i < argc; i++)  //argv =  ./nginx -a -b -c asdfas
    {
        g_argvneedmem += strlen(argv[i]) + 1; //+1是给\0留空间。
    } 
    //统计环境变量所占的内存。注意判断方法是environ[i]是否为空作为环境变量结束标记
    for(int i = 0; environ[i]; i++) 
    {
        g_envneedmem += strlen(environ[i]) + 1; //+1是因为末尾有\0,是占实际内存位置的,要算进来
    } //end for
    g_os_argc = argc;           //保存参数个数
    g_os_argv = (char **) argv; //保存参数指针

    //设置可执行程序标题相关函数:分配内存,并且把环境变量拷贝到新内存中来
    //这里无需判断penvmen == NULL,有些编译器new会返回NULL,有些会报异常,但不管怎样,如果在重要的地方new失败了,你无法收场,让程序失控崩溃,助你发现问题为好; 
    gp_envmem = new char[g_envneedmem]; 
    memset(gp_envmem,0,g_envneedmem);  //内存要清空防止出现问题
    char *ptmp = gp_envmem;
    //把原来的内存内容搬到新地方来
    for (int i = 0; environ[i]; i++) 
    {
        size_t size = strlen(environ[i])+1 ; //不要拉下+1,否则内存全乱套了,因为strlen是不包括字符串末尾的\0的
        strcpy(ptmp,environ[i]);      //把原环境变量内容拷贝到新地方【新内存】
        environ[i] = ptmp;            //然后还要让新环境变量指向这段新内存
        ptmp += size;
    }

    // 初始化信号函数,注册信号处理程序
    int errCode = init_signals();
    if(errCode!=0)
    {
        printf("init_signals failed\n");
        return 1;
    }

    // 创建守护进程
    int daemon_result = ngx_daemon();
    if(daemon_result == -1)
    {
        goto lblexit;
        return 0;
    }
    else if(daemon_result == 1)
    {// 原始父进程返回
        freeresoure();
        return 0;
    }
    // 创建子进程
    ngx_master_process_cycle();

lblexit:
    cout<<"程序退出!"<<endl;
    freeresoure();
    return 0;
}

void freeresoure()
{
    if(gp_envmem)
    {
        delete [] gp_envmem;
        gp_envmem = NULL;
    }
}

// 返回0成功,返回-1失败
int init_signals()
{
    ngx_signal_t* sig = NULL;
    struct sigaction sa;
    for(sig = signals; sig->signo != 0; sig++)
    {
        memset(&sa,0,sizeof(sa));
        // 如果信号处理函数为NULL 表示这个信号要忽略
        if(sig->handler)
        {
            sa.sa_sigaction = sig->handler;
            sa.sa_flags = SA_SIGINFO;
        }
        else
        {
            sa.sa_handler = SIG_IGN;
        }
        sigemptyset(&sa.sa_mask);
        
        // 设置信号处理函数
        if(sigaction(sig->signo,&sa,NULL)==-1)
        {
            printf("sigaction failed sig = %d\n",sig->signo);
            return -1;
        }
    }
    return 0;
}

// 信号处理函数
static void ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext)
{
    ngx_signal_t* sig;
    char* action;
    for(sig=signals; sig->signo!=0; sig++)
    {
        if(sig->signo==signo)
            break;
    }
    action = (char*)"";

    // 主进程信号
    if(ngx_process == NGX_PROCESS_MASTER)
    {
        cout<<"master来信号啦:"<<signo
            <<sig->signame<<endl;
    }
    else if(ngx_process == NGX_PROCESS_WORKER)
    {
        cout<<"worker来信号啦:"<<signo
            <<sig->signame<<endl;
    }
    else
    {

    }

    if(siginfo && siginfo->si_pid)
    {
        cout<<"signo:"<<signo
            <<"signame:"<<sig->signame
            <<"发生该信号的进程id:"<<siginfo->si_pid
            <<endl;
    }
    else
    {
        cout<<"signo:"<<signo
            <<"signame:"<<sig->signame
            <<endl;
    }

    if(signo == SIGCHLD)
    {   // 获取子进程结束状态
        ngx_process_get_status();       
    }
    return;
}

static void ngx_process_get_status(void)
{
    pid_t pid;
    int status;
    int err;
    int one = 0;
    for(;;)
    {
        pid = waitpid(-1,&status,WNOHANG);
        // 子进程没结束
        if(pid == 0)
        {
            return;
        }
        // waitpid调用有错误
        if(pid == -1)
        {
            err = errno;
            // 调用被某个信号中断
            if(err == EINTR)
            {
                continue;
            }
            if(err == ECHILD && one) // 没有子进程
            {
                return;
            }
            if(err == ECHILD)   // 没有子进程
            {
                cout<<"waitpid() failed !"<<endl;
                return;
            }
        }
        // 走到这里,表示成功,返回正常
        one = 1;
        if(WTERMSIG(status))
        {
            cout<<"pid= "<<pid
                <<"code:"<<WTERMSIG(status)
                <<endl;
        }
        else
        {
            cout<<"pid= "<<pid
                <<"code:"<<WEXITSTATUS(status)
                <<endl;
        }
    }
    return;
}

void ngx_master_process_cycle()
{
    sigset_t set;        //信号集
    sigemptyset(&set);   //清空信号集
    //建议fork()子进程时学习这种写法,防止信号的干扰;
    sigaddset(&set, SIGCHLD);     //子进程状态改变
    sigaddset(&set, SIGALRM);     //定时器超时
    sigaddset(&set, SIGIO);       //异步I/O
    sigaddset(&set, SIGINT);      //终端中断符
    sigaddset(&set, SIGHUP);      //连接断开
    sigaddset(&set, SIGUSR1);     //用户定义信号
    sigaddset(&set, SIGUSR2);     //用户定义信号
    sigaddset(&set, SIGWINCH);    //终端窗口大小改变
    sigaddset(&set, SIGTERM);     //终止
    sigaddset(&set, SIGQUIT);     //终端退出符

    if(sigprocmask(SIG_BLOCK,&set,NULL)==-1)
    {
        cout<<"ngx_master_process_cycle sigprocmask faile"<<endl;
        return;
    }

    // 为master进程设置名字
    size_t size;
    size = sizeof(master_process);
    size += g_argvneedmem;
    if(size<1000)
    {
        char title[1000] = {0};
        strcpy(title,(const char*)master_process);
        strcat(title," ");
        for(int i=0;i<g_os_argc;i++)
        {
            strcat(title,g_os_argv[i]);
        }
        ngx_setproctitle(title);
    }

    // 创建子进程
    ngx_start_worker_processes(WORKNUM);
    // 创建子进程后,父进程的执行流程会返回到这里,子进程不会进入了
    // 信号屏蔽字不需要屏蔽了
    sigemptyset(&set);
    for(;;)
    {
        // 阻塞在这里等待一个信号,此时进程是挂起的,不占用资源
        // 1、根据给定参数设置新的mask
        // 2、一旦收到信号,便恢复原先信号屏蔽
        // 3、调用该信号的信号处理程序
        // 4、信号处理程序返回后,sigsuspend返回
        sigsuspend(&set);
        cout<<"-----mastetr pid:"<<getpid()
        <<"ppid:"<<getppid()
        <<"master process"<<endl;
        sleep(1);
    }
    return;
}

//threadnums:要创建的子进程数量
static void ngx_start_worker_processes(int threadnums)
{
    for(int i=0;i<threadnums;i++)
    {
        ngx_spawn_process(i,"worker process");
    }
    return;
}

static int ngx_spawn_process(int inum,const char *pprocname)
{
    pid_t pid;
    pid = fork();
    switch (pid)
    {
    case -1:
        cout<<"创建进程失败"<<endl;
        break;
    case 0:
        ngx_worker_process_cycle(inum,pprocname);
    default:
        break;
    }
    return pid;
}

//描述:worker子进程的功能函数,每个woker子进程,就在这里循环着了(无限循环【处理网络事件和定时器事件以对外提供web服务】)
//     子进程分叉才会走到这里
//inum:进程编号【0开始】
static void ngx_worker_process_cycle(int inum,const char *pprocname)
{
    ngx_process = NGX_PROCESS_WORKER;
    sigset_t  set;      //信号集

    sigemptyset(&set);  //清空信号集
    if (sigprocmask(SIG_SETMASK, &set, NULL) == -1)  //原来是屏蔽那10个信号【防止fork()期间收到信号导致混乱】,现在不再屏蔽任何信号【接收任何信号】
    {
        cout<<"ngx_worker_process_cycle failed"<<endl;
    }
    ngx_setproctitle(pprocname);

    for(;;)
    {
        // cout<<"pid:"<<getpid()
        //     <<"ppid:"<<getppid()
        //     <<"inum:"<<inum
        //     <<"work process"<<endl;
        sleep(3);
    }
}

void ngx_setproctitle(const char *title)
{
    //我们假设,所有的命令 行参数我们都不需要用到了,可以被随意覆盖了;
    //注意:我们的标题长度,不会长到原始标题和原始环境变量都装不下,否则怕出问题,不处理
    
    //(1)计算新标题长度
    size_t ititlelen = strlen(title); 

    //(2)计算总的原始的argv那块内存的总长度【包括各种参数】    
    size_t esy = g_argvneedmem + g_envneedmem; //argv和environ内存总和
    if( esy <= ititlelen)
    {
        //你标题多长啊,我argv和environ总和都存不下?注意字符串末尾多了个 \0,所以这块判断是 <=【也就是=都算存不下】
        return;
    }

    //空间够保存标题的,够长,存得下,继续走下来    

    //(3)设置后续的命令行参数为空,表示只有argv[]中只有一个元素了,这是好习惯;防止后续argv被滥用,因为很多判断是用argv[] == NULL来做结束标记判断的;
    g_os_argv[1] = NULL;  

    //(4)把标题弄进来,注意原来的命令行参数都会被覆盖掉,不要再使用这些命令行参数,而且g_os_argv[1]已经被设置为NULL了
    char *ptmp = g_os_argv[0]; //让ptmp指向g_os_argv所指向的内存
    strcpy(ptmp,title);
    ptmp += ititlelen; //跳过标题

    //(5)把剩余的原argv以及environ所占的内存全部清0,否则会出现在ps的cmd列可能还会残余一些没有被覆盖的内容;
    size_t cha = esy - ititlelen;  //内存总和减去标题字符串长度(不含字符串末尾的\0),剩余的大小,就是要memset的;
    memset(ptmp,0,cha);
}

// 子进程返回0,父进程返回1,执行失败返回-1
static int ngx_daemon()
{
    switch (fork())
    {
    case -1:
        cout<<"ngx_daemon failed"<<endl;
        return -1;       
    case 0:
        break;
    default:
        return 1;
    }

    // 只有fork出来的子进程才能走到这个流程
    if(setsid() == -1)
    {
        cout<<"ngx_daemon setsid failed"<<endl;
        return -1;
    }
    umask(0);
    // 打开黑洞设备,以读写方式打开
    int fd = open("/dev/null",O_RDWR);
    if(fd == -1)   
    {
        cout<<"ngx_daemon open failed"<<endl;
        return -1;
    }
    if(dup2(fd,STDIN_FILENO) == -1)
    {
        cout<<"ngx_daemon dup2 failed"<<endl;
        return -1;
    }
    if(dup2(fd,STDOUT_FILENO)==-1)
    {
        cout<<"ngx_daemon dup2 failed"<<endl;
        return -1;
    }
    if(fd>STDERR_FILENO)
    {
        if(close(fd)==-1)
        {
            cout<<"ngx_daemon close failed"<<endl;
            return -1;
        }
    }
    return 0;
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

讳疾忌医丶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值