Linux setting process name (title) (7) -【Linux Communication Architecture Series】

Series Article Directory

C++ skill series
Linux communication architecture series
C++ high-performance optimization programming series
Deep understanding of software architecture design series
Advanced C++ concurrent thread programming

Looking forward to your attention! ! !
insert image description here

现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。
Now everything is for the future of dream weaving wings, let the dream fly in reality.

@TOC
nginx.c


//整个程序入口函数放这里
/*
王健伟老师 《Linux C++通讯架构实战》
商业级质量的代码,完整的项目,帮你提薪至少10K
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h> 
#include <errno.h>
#include <arpa/inet.h>

#include "ngx_macro.h"         //各种宏定义
#include "ngx_func.h"          //各种函数声明
#include "ngx_c_conf.h"        //和配置文件处理相关的类,名字带c_表示和类有关
#include "ngx_c_socket.h"      //和socket通讯相关
#include "ngx_c_memory.h"      //和内存分配释放等相关
#include "ngx_c_threadpool.h"  //和多线程有关

//本文件用的函数声明
static void freeresource();

//和设置标题有关的全局量
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()函数中会被分配内存
int     g_daemonized=0;         //守护进程标记,标记是否启用了守护进程模式,0:未启用,1:启用了

//socket/线程池相关
CSocekt      g_socket;          //socket全局对象
CThreadPool  g_threadpool;      //线程池全局对象

//和进程本身有关的全局量
pid_t   ngx_pid;                //当前进程的pid
pid_t   ngx_parent;             //父进程的pid
int     ngx_process;            //进程类型,比如master,worker进程等

sig_atomic_t  ngx_reap;         //标记子进程状态变化[一般是子进程发来SIGCHLD信号表示退出],sig_atomic_t:系统定义的类型:访问或改变这些变量需要在计算机的一条指令内完成
                                   //一般等价于int【通常情况下,int类型的变量通常是原子访问的,也可以认为 sig_atomic_t就是int类型的数据】                                   

//程序主入口函数----------------------------------
int main(int argc, char *const *argv)
{
    
       
    //time_t mytime = time(NULL);
    //printf("time = %u",mytime);
    //exit(0);
    //#ifdef _POSIX_THREADS 
    //    printf("henhao");
    //#endif    
    //exit(0);
    //printf("unsigned long sizeof=%d",sizeof(unsigned long));
    //printf("htonl(100)=%d",htonl(100));
    //printf("ntohl(htonl(100)=%d",ntohl(htonl(100)));
    //exit(0);
    //printf("EAGAIN=%d,EWOULDBLOCK=%d,EINTR=%d",EAGAIN,EWOULDBLOCK,EINTR);
    //ssize_t n = -1;
    //printf("ssize_t n = %d\n",n);
    //exit(0);
    int exitcode = 0;           //退出代码,先给0表示正常退出
    int i;                      //临时用
    //CMemory *p_memory;

    //(1)无伤大雅也不需要释放的放最上边    
    ngx_pid    = getpid();      //取得进程pid
    ngx_parent = getppid();     //取得父进程的id 
    //统计argv所占的内存
    g_argvneedmem = 0;
    for(i = 0; i < argc; i++)  //argv =  ./nginx -a -b -c asdfas
    {
    
    
        g_argvneedmem += strlen(argv[i]) + 1; //+1是给\0留空间。
    } 
    //统计环境变量所占的内存。注意判断方法是environ[i]是否为空作为环境变量结束标记
    for(i = 0; environ[i]; i++) 
    {
    
    
        g_envneedmem += strlen(environ[i]) + 1; //+1是因为末尾有\0,是占实际内存位置的,要算进来
    } //end for

    g_os_argc = argc;           //保存参数个数
    g_os_argv = (char **) argv; //保存参数指针

    //全局量有必要初始化的
    ngx_log.fd = -1;                  //-1:表示日志文件尚未打开;因为后边ngx_log_stderr要用所以这里先给-1
    ngx_process = NGX_PROCESS_MASTER; //先标记本进程是master进程
    ngx_reap = 0;                     //标记子进程没有发生变化
   
    //(2)初始化失败,就要直接退出的
    //配置文件必须最先要,后边初始化啥的都用,所以先把配置读出来,供后续使用 
    CConfig *p_config = CConfig::GetInstance(); //单例类
    if(p_config->Load("nginx.conf") == false) //把配置文件内容载入到内存            
    {
    
       
        ngx_log_init();    //初始化日志
        ngx_log_stderr(0,"配置文件[%s]载入失败,退出!","nginx.conf");
        //exit(1);终止进程,在main中出现和return效果一样 ,exit(0)表示程序正常, exit(1)/exit(-1)表示程序异常退出,exit(2)表示表示系统找不到指定的文件
        exitcode = 2; //标记找不到文件
        goto lblexit;
    }
    //(2.1)内存单例类可以在这里初始化,返回值不用保存
    CMemory::GetInstance();	
        
    //(3)一些必须事先准备好的资源,先初始化
    ngx_log_init();             //日志初始化(创建/打开日志文件),这个需要配置项,所以必须放配置文件载入的后边;     
        
    //(4)一些初始化函数,准备放这里        
    if(ngx_init_signals() != 0) //信号初始化
    {
    
    
        exitcode = 1;
        goto lblexit;
    }        
    if(g_socket.Initialize() == false)//初始化socket
    {
    
    
        exitcode = 1;
        goto lblexit;
    }

    //(5)一些不好归类的其他类别的代码,准备放这里
    ngx_init_setproctitle();    //把环境变量搬家

    //------------------------------------
    //(6)创建守护进程
    if(p_config->GetIntDefault("Daemon",0) == 1) //读配置文件,拿到配置文件中是否按守护进程方式启动的选项
    {
    
    
        //1:按守护进程方式运行
        int cdaemonresult = ngx_daemon();
        if(cdaemonresult == -1) //fork()失败
        {
    
    
            exitcode = 1;    //标记失败
            goto lblexit;
        }
        if(cdaemonresult == 1)
        {
    
    
            //这是原始的父进程
            freeresource();   //只有进程退出了才goto到 lblexit,用于提醒用户进程退出了
                              //而我现在这个情况属于正常fork()守护进程后的正常退出,不应该跑到lblexit()去执行,因为那里有一条打印语句标记整个进程的退出,这里不该限制该条打印语句;
            exitcode = 0;
            return exitcode;  //整个进程直接在这里退出
        }
        //走到这里,成功创建了守护进程并且这里已经是fork()出来的进程,现在这个进程做了master进程
        g_daemonized = 1;    //守护进程标记,标记是否启用了守护进程模式,0:未启用,1:启用了
    }

    //(7)开始正式的主工作流程,主流程一致在下边这个函数里循环,暂时不会走下来,资源释放啥的日后再慢慢完善和考虑    
    ngx_master_process_cycle(); //不管父进程还是子进程,正常工作期间都在这个函数里循环;
        
    //--------------------------------------------------------------    
    //for(;;)    
    //{
    
    
    //    sleep(1); //休息1秒        
    //    printf("休息1秒\n");        
    //}
      
    //--------------------------------------
lblexit:
    //(5)该释放的资源要释放掉
    ngx_log_stderr(0,"程序退出,再见了!");
    freeresource();  //一系列的main返回前的释放动作函数
    //printf("程序退出,再见!\n");    
    return exitcode;
}

//专门在程序执行末尾释放资源的函数【一系列的main返回前的释放动作函数】
void freeresource()
{
    
    
    //(1)对于因为设置可执行程序标题导致的环境变量分配的内存,我们应该释放
    if(gp_envmem)
    {
    
    
        delete []gp_envmem;
        gp_envmem = NULL;
    }

    //(2)关闭日志文件
    if(ngx_log.fd != STDERR_FILENO && ngx_log.fd != -1)  
    {
    
            
        close(ngx_log.fd); //不用判断结果了
        ngx_log.fd = -1; //标记下,防止被再次close吧        
    }
}

ngx_global.h

#include <signal.h> 

#include "ngx_c_socket.h"
#include "ngx_c_threadpool.h"

//一些比较通用的定义放在这里,比如typedef定义
//一些全局变量的外部声明也放在这里

//类型定义----------------

//结构定义
typedef struct _CConfItem
{
    
    
	char ItemName[50];
	char ItemContent[500];
}CConfItem,*LPCConfItem;

//和运行日志相关 
typedef struct
{
    
    
	int    log_level;   //日志级别 或者日志类型,ngx_macro.h里分0-8共9个级别
	int    fd;          //日志文件描述符

}ngx_log_t;


//外部全局量声明
extern size_t        g_argvneedmem;
extern size_t        g_envneedmem; 
extern int           g_os_argc; 
extern char          **g_os_argv;
extern char          *gp_envmem; 
extern int           g_daemonized;
extern CSocekt       g_socket;  
extern CThreadPool   g_threadpool;

extern pid_t         ngx_pid;
extern pid_t         ngx_parent;
extern ngx_log_t     ngx_log;
extern int           ngx_process;   
extern sig_atomic_t  ngx_reap;   

ngx_func.h

//函数声明放在这个头文件里-------------------------------------------

#ifndef __NGX_FUNC_H__
#define __NGX_FUNC_H__

//字符串相关函数
void   Rtrim(char *string);
void   Ltrim(char *string);

//设置可执行程序标题相关函数
void   ngx_init_setproctitle();
void   ngx_setproctitle(const char *title);

//和日志,打印输出有关
void   ngx_log_init();
void   ngx_log_stderr(int err, const char *fmt, ...);
void   ngx_log_error_core(int level,  int err, const char *fmt, ...);
u_char *ngx_log_errno(u_char *buf, u_char *last, int err);
u_char *ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...);
u_char *ngx_slprintf(u_char *buf, u_char *last, const char *fmt, ...);
u_char *ngx_vslprintf(u_char *buf, u_char *last,const char *fmt,va_list args);

//和信号/主流程相关相关
int    ngx_init_signals();
void   ngx_master_process_cycle();
int    ngx_daemon();
void   ngx_process_events_and_timers();


#endif  

ngx_setproctitle.cxx

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>  //env
#include <string.h>

#include "ngx_global.h"

//设置可执行程序标题相关函数:分配内存,并且把环境变量拷贝到新内存中来
void ngx_init_setproctitle()
{
    
       
    //这里无需判断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;
    }
    return;
}

//设置可执行程序标题
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);
    return;
}

Guess you like

Origin blog.csdn.net/weixin_30197685/article/details/131355373