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;
}
版权声明:本文为weixin_45715405原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。