1. 程序中栈的结构
1. 命令行参数: ./nginx -12 -v 568
argv[0] argv[1] argv[2] argv[3]
argc = 3
2. argv[0] 所指向的是 进程名称 ,所以修改进程名 其实 就是修改argv[0]所指向的字符串。
3. 如果argv[0]所指向的原字符串长度够长,那么修改时直接把argv[0]所指向的字符串修改即可;如果太短,则就需要进行环境变量搬家等操作
2. 设置的进程名过长潜在问题
覆盖环境变量所指向的部分或全部内存内容,从而造成程序运行隐患。
所以在无法提前预估进程名称长度的情况下,有如下解决思路:
(1)重新分配一块内存,足够容纳环境变量的那些字符串,把环境变量的那些字符串搬到这块内存中来;
(2)将argv[0]指向的内容替换成新的进程名称即可。
3. 设置进程标题代码
#include <stdio.h>
#include <stdlib.h>
extern char** environ; //environ变量
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()函数中会被分配内存
//分配内存,并且把环境变量拷贝到新内存中来(设置进程名称的第1步)
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;
}
//设置进程名称(设置进程名称第2步)
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;
}
int main(int argc, char *const *argv)
{
g_os_argc = argc; //保存参数个数
g_os_argv = (char **) argv; //保存参数指针
g_argvneedmem = 0;
g_envneedmem = 0;
//统计argv所占的内存
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
ngx_init_setproctitle(); //环境变量搬家
ngx_setproctitle("aaaaaaaaaaaa"); //设置进程标题
return 0;
}