【tag-004】Linux C++ 网络编程架构之进程名称更改

本节开始进入到进程的话题

在正式进行多进程代码编写前,还需要修改一下本工程运行之后在终端显示的进程名称,根据之前几节的内容,在main函数中添加一个死循环,保证程序运行后不退出,以便观察进程名称。

for(;;)
{
    sleep(1);
}

重新编译运行后,在终端查看进程名称

ps aux | grep -E 'bash|PID|nginx'

在这里插入图片描述
可以看到,在未作任何修改前,进程名称显示为 ./nginx_sim。

接下来,根据nginx官方源码,我们给自己的程序修改显示的进程名
ngx_global.h 文件中新增几个全局变量保存参数和环境变量用以备份

//用于修改进程名称
extern char  **g_os_argv;
extern char  *gp_envmem; 
extern int   g_environlen;
extern int   g_argvlen;
extern int   g_os_argc; //参数个数

main.cxx 中定义这几个变量

char  **g_os_argv;
char  *gp_envmem = nullptr; 
int   g_environlen = 0;
int   g_argvlen = 0;
int   g_os_argc; //参数个数

然后再main 函数中用这几个全局变量保存环境变量和main函数的输入参数

for(int i = 0; i<argc; ++i)
{
    g_argvlen += strlen(argv[i]) + 1;
}

//统计环境变量所占的内存。注意判断方法是environ[i]是否为空作为环境变量结束标记
for(int i = 0; environ[i]; ++i)
{
   	g_environlen += strlen(environ[i]) + 1;
}
g_os_argc = argc;
g_os_argv = (char **) argv;  

到这里就可以开始动手了
Note: 本工程我们不会用到命令行输入的参数,所以直接在内存中将其覆盖掉了。

ngx_func.h 文件声明修改进程名相关的函数

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

在 src 目录新建 ngx_setproctitle.cxx 文件用于对这两个函数的实现

  • 初始化函数用于参数和环境变量的备份
//设置可执行程序标题相关函数:分配内存,并且把环境变量拷贝到新内存中来
void ngx_init_setproctitle()
{    
    //new 失败时就让程序崩溃吧【这里分配了内存,一定要记得释放,本程序放在了 main.cxx 中的 freereSource() 函数中】
    gp_envmem = new char[g_environlen]; 
    memset(gp_envmem,0,g_environlen);  //内存要清空防止出现问题
    char *ptmp = gp_envmem;
    //把原来的内存内容搬到新地方来
    for (int i = 0; environ[i]; i++) 
    {
    	//这个加1还是很重要的,这个位置存放 '\0'
        size_t size = strlen(environ[i])+1 ; 
        strcpy(ptmp,environ[i]);      //把原环境变量内容拷贝到新地方【新内存】
        environ[i] = ptmp;            //然后还要让新环境变量指向这段新内存
        ptmp += size;
    }
    return;
}
  • 修改进程名字
//设置可执行程序标题
void ngx_setproctitle(const char *title)
{
    //我们假设,所有的命令 行参数我们都不需要用到了,可以被随意覆盖了;
    //注意:我们的标题长度,绝对不能长到原始标题和原始环境变量都装不下
    
    //计算新标题长度
    size_t ititlelen = strlen(title); 
    
    size_t esy = g_argvlen + g_environlen; //argv和environ内存总和
    if( esy <= ititlelen)
    {
        //标题太长的拒绝
        return;
    }   
    //设置后续的命令行参数为空,表示只有argv[]中只有一个元素
    g_os_argv[1] = NULL;  

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

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

可以在 mian 函数中添加测试代码了

int main(int argc, char* const* argv)
{
    ngx_pid = getpid();   

    for(int i = 0; i<argc; ++i)
    {
        g_argvlen += strlen(argv[i]) + 1;
    }

    //统计环境变量所占的内存。注意判断方法是environ[i]是否为空作为环境变量结束标记
    for(int i = 0; environ[i]; ++i)
    {
        g_environlen += strlen(environ[i]) + 1;
    }
    g_os_argc = argc;
    g_os_argv = (char **) argv;    

    //加载配置文件内容
    if(false == CConfig::GetInstance()->LoadConfig("nginx_sim.conf"))
    {
        //日志初始化(创建/打开日志文件)
        ngx_log_init(); 
        ngx_log_stderr(0,"配置文件[%s]载入失败,退出!","nginx_sim.conf");
        //找不到文件
        goto lblexit;
    }
    
    //日志初始化(创建/打开日志文件)
    ngx_log_init(); 

    //把环境变量搬家
    ngx_init_setproctitle(); 

    //这里设置进程名测试
    ngx_setproctitle("nginx-sim: master process");

	//这个死循环主要方便观察进程名称
    for(;;)
    {
        sleep(1);
    }

    ngx_log_stderr(0, "从配置文件中读到的 log 目录:%s",(CConfig::GetInstance()->GetString("Log")).c_str());
    ngx_log_stderr(0, "从配置文件中读到的 log 等级:%d",CConfig::GetInstance()->GetIntDefault("LogLevel",NGX_LOG_NOTICE));

lblexit:
    ngx_log_stderr(0,"程序退出!");
    //该释放的资源要释放掉
    freereSource();  //一系列的main返回前的释放动作函数
    
    return 0;
}

在这里插入图片描述
可以看到,我们启动程序以后终端显示的进程名字已经编程我们自己设置的了,目前只有一个master进程,后续我们将在代码中添加多个 worker 进程。

接下来将我们的程序做成一个守护进程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
主存和Cache之间的地址映射是一个很重要的问题,需要考虑到Cache的大小、块大小、替换策略等因素。下面是一个简单的C++程序,用来模拟主存和Cache之间的地址映射。 假设主存大小为256KB,Cache大小为64KB,块大小为16B,采用直接映射方式实现地址映射。因此,主存中的一个地址可以被映射到Cache中的唯一一个位置,而Cache中的一个地址可以被映射到主存中的16个位置之一。 ```cpp #include <iostream> #include <cstring> using namespace std; #define MEM_SIZE 256 * 1024 // 主存大小为256KB #define CACHE_SIZE 64 * 1024 // Cache大小为64KB #define BLOCK_SIZE 16 // 块大小为16B int mem[MEM_SIZE]; // 主存 int cache[CACHE_SIZE / BLOCK_SIZE][BLOCK_SIZE]; // Cache // 主存地址到Cache地址的映射函数 int mem2cache(int mem_addr) { int index = (mem_addr / BLOCK_SIZE) % (CACHE_SIZE / BLOCK_SIZE); return index; } // Cache地址到主存地址的映射函数 int cache2mem(int cache_addr) { int block_addr = cache_addr * BLOCK_SIZE; int mem_addr = block_addr + (cache_addr / (CACHE_SIZE / BLOCK_SIZE)) * (CACHE_SIZE); return mem_addr; } // 读取主存中的数据 int read_mem(int mem_addr) { int cache_addr = mem2cache(mem_addr); int tag = mem_addr / (CACHE_SIZE / BLOCK_SIZE); if (cache[cache_addr][0] == tag) // 命中 { cout << "Cache hit: " << cache_addr << endl; int offset = mem_addr % BLOCK_SIZE; return cache[cache_addr][offset]; } else // 不命中 { cout << "Cache miss: " << cache_addr << endl; int block_addr = cache_addr * BLOCK_SIZE; memcpy(cache[cache_addr], &mem[block_addr], BLOCK_SIZE * sizeof(int)); // 从主存中读取整个块 cache[cache_addr][0] = tag; // 更新tag int offset = mem_addr % BLOCK_SIZE; return cache[cache_addr][offset]; } } // 写入主存中的数据 void write_mem(int mem_addr, int data) { int cache_addr = mem2cache(mem_addr); int tag = mem_addr / (CACHE_SIZE / BLOCK_SIZE); if (cache[cache_addr][0] == tag) // 命中 { cout << "Cache hit: " << cache_addr << endl; int offset = mem_addr % BLOCK_SIZE; cache[cache_addr][offset] = data; } else // 不命中 { cout << "Cache miss: " << cache_addr << endl; int block_addr = cache_addr * BLOCK_SIZE; memcpy(&mem[block_addr], cache[cache_addr], BLOCK_SIZE * sizeof(int)); // 写入整个块到主存 cache[cache_addr][0] = tag; // 更新tag int offset = mem_addr % BLOCK_SIZE; cache[cache_addr][offset] = data; } } int main() { // 初始化主存和Cache for (int i = 0; i < MEM_SIZE; i++) { mem[i] = i; } memset(cache, 0, sizeof(cache)); // 读取和写入数据 cout << read_mem(0) << endl; cout << read_mem(16) << endl; write_mem(0, 100); cout << read_mem(0) << endl; cout << read_mem(16) << endl; return 0; } ``` 上述程序模拟了主存和Cache之间的地址映射,包括了Cache的读取和写入操作。在读取数据时,先根据主存地址计算出对应的Cache地址和tag值,然后判断是否命中Cache。如果命中,则直接从Cache中读取数据;如果不命中,则从主存中读取整个块,并将其存储到Cache中,并更新tag值。在写入数据时,同样需要先计算出对应的Cache地址和tag值,然后判断是否命中Cache。如果命中,则直接在Cache中写入数据;如果不命中,则先将整个块从Cache中写入到主存中,然后再将数据写入到Cache中,并更新tag值。 需要注意的是,上述程序中只实现了最简单的直接映射方式,实际上Cache的地址映射还有很多其他的方式,例如全相联映射、组相联映射等,每种方式都有其优缺点,需要根据具体应用场景选择合适的方式。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值