思路:创建一个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>
usingnamespace std;
//一个信号有关的结构 ngx_signal_t
typedefstruct
{
int signo;//信号对应的数字编号 ,每个信号都有对应的#define
constchar*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 =newchar[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");
return1;
}
// 创建子进程
ngx_master_process_cycle();
return0;
}
// 返回0成功,返回-1失败
int init_signals()
{
ngx_signal_t* sig =NULL;
structsigaction 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;
}
}
return0;
}
// 信号处理函数
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;
}
elseif(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_tset;//信号集
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,(constchar*)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;
case0:
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_tset;//信号集
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这个信号,查看程序的打印
添加守护进程代码,使程序脱离终端在后台运行
// 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>
usingnamespace std;
//一个信号有关的结构 ngx_signal_t
typedefstruct
{
int signo;//信号对应的数字编号 ,每个信号都有对应的#define
constchar*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 =newchar[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");
return1;
}
// 创建守护进程
int daemon_result =ngx_daemon();
if(daemon_result ==-1)
{
goto lblexit;
return0;
}
elseif(daemon_result ==1)
{// 原始父进程返回
freeresoure();
return0;
}
// 创建子进程
ngx_master_process_cycle();
lblexit:
cout<<"程序退出!"<<endl;
freeresoure();
return0;
}
void freeresoure()
{
if(gp_envmem)
{
delete[] gp_envmem;
gp_envmem =NULL;
}
}
// 返回0成功,返回-1失败
int init_signals()
{
ngx_signal_t* sig =NULL;
structsigaction 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;
}
}
return0;
}
// 信号处理函数
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;
}
elseif(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_tset;//信号集
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,(constchar*)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;
case0:
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_tset;//信号集
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;
case0:
break;
default:
return1;
}
// 只有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;
}
}
return0;
}
原文作者:讳疾忌医-note;如有侵权,请联系管理员删除。