济源网站建设公司,北京邢台企业商会网站,佛山网站建设3lue,电脑系统优化工具读写配置文件代码实战。nginx.conf 一个项目要启动#xff0c;需要配置很多信息#xff0c;第一项就是学习如何配置一个项目 nginx.conf的内容
#是注释行#xff0c;
#每个有效配置项用 等号 处理#xff0c;等号前不超过40个字符#xff0c;等号后不超过400个字符#…读写配置文件代码实战。nginx.conf 一个项目要启动需要配置很多信息第一项就是学习如何配置一个项目 nginx.conf的内容
#是注释行
#每个有效配置项用 等号 处理等号前不超过40个字符等号后不超过400个字符#[开头的表示组信息也等价于注释行
[Socket]
ListenPort 5678 DBInfo 127.0.0.1;1234;myr;123456;mxdb_g
我们的目的是
1.将 ListenPort的值取出来
2.将DBInfo的值取出来 ngx_c_conf.h #ifndef __NGX_CONF_H__
#define __NGX_CONF_H__#include vector#include ngx_global.h //一些全局/通用定义//类名可以遵照一定的命名规则规范比如老师这里第一个字母是C后续的单词首字母大写
class CConfig
{
//---------------------------------------------------
//这段代码老师在《c从入门到精通》 多线程这章老师提过 单例设计模式就是如下这些代码大家即便没学过也可以现在学
private:CConfig();
public:~CConfig();
private:static CConfig *m_instance;public: static CConfig* GetInstance() { if(m_instance NULL){//锁if(m_instance NULL){ m_instance new CConfig();static CGarhuishou cl; }//放锁 }return m_instance;} class CGarhuishou //类中套类用于释放对象{public: ~CGarhuishou(){if (CConfig::m_instance){ delete CConfig::m_instance;CConfig::m_instance NULL; }}};
//---------------------------------------------------
public:bool Load(const char *pconfName); //装载配置文件const char *GetString(const char *p_itemname);int GetIntDefault(const char *p_itemname,const int def);public:std::vectorLPCConfItem m_ConfigItemList; //存储配置信息的列表};#endifngx_c_conf.cxx //系统头文件放上边
#include stdio.h
#include stdlib.h
#include string.h
#include vector//自定义头文件放下边,因为g中用了-I参数所以这里用也可以
#include ngx_func.h //函数声明
#include ngx_c_conf.h //和配置文件处理相关的类,名字带c_表示和类有关//静态成员赋值
CConfig *CConfig::m_instance NULL;//构造函数
CConfig::CConfig()
{
}//析构函数
CConfig::~CConfig()
{ std::vectorLPCConfItem::iterator pos; for(pos m_ConfigItemList.begin(); pos ! m_ConfigItemList.end(); pos){ delete (*pos);}//end form_ConfigItemList.clear();
}//装载配置文件
bool CConfig::Load(const char *pconfName)
{ FILE *fp;fp fopen(pconfName,r);if(fp NULL)return false;//每一行配置文件读出来都放这里char linebuf[501]; //每行配置都不要太长保持500字符内防止出现问题//走到这里文件打开成功 while(!feof(fp)) //检查文件是否结束 没有结束则条件成立{ //大家要注意老师的写法注意写法的严密性商业代码就是要首先确保代码的严密性if(fgets(linebuf,500,fp) NULL) //从文件中读数据每次读一行一行最多不要超过500个字符 continue;if(linebuf[0] 0)continue;//处理注释行 if(*linebuf; || *linebuf || *linebuf# || *linebuf\t|| *linebuf\n)continue;lblprocstring://屁股后边若有换行回车空格等都截取掉if(strlen(linebuf) 0){//10 换行13回车32 是空格if(linebuf[strlen(linebuf)-1] 10 || linebuf[strlen(linebuf)-1] 13 || linebuf[strlen(linebuf)-1] 32) {linebuf[strlen(linebuf)-1] 0;goto lblprocstring;} }if(linebuf[0] 0)continue;if(*linebuf[) //[开头的也不处理continue;//这种 “ListenPort 5678”走下来char *ptmp strchr(linebuf,);if(ptmp ! NULL){LPCConfItem p_confitem new CConfItem; //注意前边类型带LP后边new这里的类型不带memset(p_confitem,0,sizeof(CConfItem));strncpy(p_confitem-ItemName,linebuf,(int)(ptmp-linebuf)); //等号左侧的拷贝到p_confitem-ItemNamestrcpy(p_confitem-ItemContent,ptmp1); //等号右侧的拷贝到p_confitem-ItemContentRtrim(p_confitem-ItemName);Ltrim(p_confitem-ItemName);Rtrim(p_confitem-ItemContent);Ltrim(p_confitem-ItemContent);//printf(itemname%s | itemcontent%s\n,p_confitem-ItemName,p_confitem-ItemContent); m_ConfigItemList.push_back(p_confitem); //内存要释放因为这里是new出来的 } //end if} //end while(!feof(fp)) fclose(fp); //这步不可忘记return true;
}//根据ItemName获取配置信息字符串不修改不用互斥
const char *CConfig::GetString(const char *p_itemname)
{std::vectorLPCConfItem::iterator pos; for(pos m_ConfigItemList.begin(); pos ! m_ConfigItemList.end(); pos){ if(strcasecmp( (*pos)-ItemName,p_itemname) 0)return (*pos)-ItemContent;}//end forreturn NULL;
}
//根据ItemName获取数字类型配置信息不修改不用互斥
int CConfig::GetIntDefault(const char *p_itemname,const int def)
{std::vectorLPCConfItem::iterator pos; for(pos m_ConfigItemList.begin(); pos !m_ConfigItemList.end(); pos){ if(strcasecmp( (*pos)-ItemName,p_itemname) 0)return atoi((*pos)-ItemContent);}//end forreturn def;
} nginx.cxx #include stdio.h
#include stdlib.h
#include unistd.h
#include string.h#include ngx_c_conf.h //和配置文件处理相关的类,名字带c_表示和类有关
#include ngx_signal.h
#include ngx_func.h //各种函数声明//和设置标题有关的全局量
char **g_os_argv; //原始命令行参数数组,在main中会被赋值
char *gp_envmem NULL; //指向自己分配的env环境变量的内存
int g_environlen 0; //环境变量所占内存大小int main(int argc, char *const *argv)
{ g_os_argv (char **) argv;ngx_init_setproctitle(); //把环境变量搬家//我们在main中先把配置读出来供后续使用 CConfig *p_config CConfig::GetInstance(); //单例类if(p_config-Load(nginx.conf) false) //把配置文件内容载入到内存{printf(配置文件载入失败退出!\n);exit(1);}//获取配置文件信息的用法 int port p_config-GetIntDefault(ListenPort,0); //0是缺省值printf(port%d\n,port);const char *pDBInfo p_config-GetString(DBInfo);if(pDBInfo ! NULL){printf(DBInfo%s\n,pDBInfo);}for(;;){sleep(1); //休息1秒printf(休息1秒\n);}printf(程序退出再见!\n);return 0;
} 运行结果 hunandedehunandede-virtual-machine:/mnt/hgfs/linux/4-2/nginx$ ./nginx
port5678
DBInfo127.0.0.1;1234;myr;123456;mxdb_g
休息1秒
休息1秒内存泄漏查找工具valgrind 以及使用
使用环境 linux 下的工具 安装
sudo apt-get install valgrind 使用
valgrind --toolmemcheck --leak-checkfull --show-reachableyes ./nginx //Valgrind帮助程序员寻找程序里的bug和改进程序性能的工具集。擅长是发现内存的管理问题 //里边有若干工具其中最重要的是Memcheck(内存检查工具用于检查内存的泄漏 //2.1memcheck的基本功能能发现如下的问题 //a)使用未初始化的内存 //b)使用已经释放了的内存 //c)使用超过malloc()分配的内存 //d)对堆栈的非法访问 //e)申请的内存是否有释放***** //f)malloc/free,new/delete申请和释放内存的匹配 //g)memcpy()内存拷贝函数中源指针和目标指针重叠 //2.2内存泄漏检查示范 //所有应该释放的内存都要释放掉作为服务器程序开发者要绝对的严谨和认真 //格式 //valgrind --toolmemcheck 一些开关 可执行文件名 //--toolmemcheck 使用valgrind工具集中的memcheck工具 //--leak-checkfull 指的是完全full检查内存泄漏 //--show-reachableyes 是显示内存泄漏的地点 //--trace-children yes 是否跟入子进程 //--log-filelog.txt讲调试信息输出到log.txt不输出到屏幕 //最终用的命令 //valgrind --toolmemcheck --leak-checkfull --show-reachableyes ./nginx //查看内存泄漏的三个地方 //(1) 9 allocs, 8 frees 差值是1就没泄漏超过1就有泄漏 //(2)中间诸如 by 0x401363: CConfig::Load(char const*) (ngx_c_conf.cxx:77)和我们自己的源代码有关的提示就要注意 //(3)LEAK SUMMARY:definitely lost: 1,100 bytes in 2 blocks 测试结果分析 注意这里3236 total heap usage: 10 allocs, 9 frees, 79,021 bytes allocated
hunandedehunandede-virtual-machine:/mnt/hgfs/linux$ cd 4-2/nginx/
hunandedehunandede-virtual-machine:/mnt/hgfs/linux/4-2/nginx$ valgrind --toolmemcheck --leak-checkfull --show-reachableyes ./nginx
3236 Memcheck, a memory error detector
3236 Copyright (C) 2002-2015, and GNU GPLd, by Julian Seward et al.
3236 Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
3236 Command: ./nginx
3236
port5678
DBInfo127.0.0.1;1234;myr;123456;mxdb_g
程序退出再见!
3236
3236 HEAP SUMMARY:
3236 in use at exit: 72,704 bytes in 1 blocks
3236 total heap usage: 10 allocs, 9 frees, 79,021 bytes allocated
3236
3236 72,704 bytes in 1 blocks are still reachable in loss record 1 of 1
3236 at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
3236 by 0x4EC3EFF: ??? (in /usr/lib/x86_64-linux-gnu/libstdc.so.6.0.21)
3236 by 0x40106F9: call_init.part.0 (dl-init.c:72)
3236 by 0x401080A: call_init (dl-init.c:30)
3236 by 0x401080A: _dl_init (dl-init.c:120)
3236 by 0x4000C69: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so)
3236
3236 LEAK SUMMARY:
3236 definitely lost: 0 bytes in 0 blocks
3236 indirectly lost: 0 bytes in 0 blocks
3236 possibly lost: 0 bytes in 0 blocks
3236 still reachable: 72,704 bytes in 1 blocks
3236 suppressed: 0 bytes in 0 blocks
3236
3236 For counts of detected and suppressed errors, rerun with: -v
3236 ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
hunandedehunandede-virtual-machine:/mnt/hgfs/linux/4-2/nginx$ 设置标题代码实战 我们这样执行写好的代码就是说带了参数 aa bb cc
./nginx aa bb cc hunandedehunandede-virtual-machine:/mnt/hgfs/linux/4-2/nginx$ ./nginx aa bb cc
port5678
DBInfo127.0.0.1;1234;myr;123456;mxdb_g
休息1秒
休息1秒
休息1秒
休息1秒
然后使用 如下命令查看
ps -eo pid,ppid,sid,tty,pgrp,comm,stat,cmd | grep -E bash|PID|nginx hunandedehunandede-virtual-machine:~$ ps -eo pid,ppid,sid,tty,pgrp,comm,stat,cmd | grep -E bash|PID|nginxPID PPID SID TT PGRP COMMAND STAT CMD2652 2651 2652 pts/9 2652 bash Ss -bash3326 3325 3326 pts/10 3326 bash Ss -bash3348 2652 2652 pts/9 3348 nginx S ./nginx aa bb cc3371 3326 3326 pts/10 3370 grep R grep --colorauto -E bash|PID|nginx
hunandedehunandede-virtual-machine:~$ 注意下图红色的部分实际上我们执行的该可执行程序的 加上参数
在ps中最终会显示在这里。
改动目标将启动的可执行程序和其参数都替换成我们想要的字符串
我们这里的想法就是改动这块。 原理和实现思路分析 //argc:命令行参数的个数 //argv:是个数组每个数组元素都是指向一个字符串的char *里边存储的内容是所有命令行参数 例如我们在执行nginx的时候加上了3个参数
./nginx -v -s 5
那么对应的argc 和 argv数组的每个值如下 //argc 4 //argv[0] ./nginx ----指向的就是可执行程序名 ./nginx //argv[1] -v //argv[2] -s //argv[3] 5 //argv内存之后接着连续的就是环境变量参数信息内存【是咱们这个可执行程序执行时有关的所有环境变量参数信息】 //可以通过一个全局的environ[char **]就可以访问 //environ内存和argv内存紧紧的挨着 我们先来验证自己的猜测是否正确 。 int main(int argc, char *const *argv)
{cout 验证argc 和 argv 以及 环境变量的存储是否是我们理解的布局 start endl;cout 参数个数argc的值为 argc endl;for (int i 0; i argc; i){//这里注意老师的写法在c中打印argv 也就是 char *argv[]中的地址的写法argv是一个指针数组本质上是一个数组这个数组的每一项都是一个指针// cout myargv[ i ] 的地址为 *(argv[i]) endl;//myargv[0] 的地址为./nginx// cout myargv[ i ] 的1地址为 argv[i] endl;//myargv[0] 的1地址为./nginx// cout myargv[ i ] 的2地址为 *argv[i] endl;myargv[0] 的2地址为.printf(argv[%d]地址%x , i, (unsigned int)((unsigned long)argv[i]));//由于数组中的每一项都是指针我们要先拿到数组里面的 指针由于指针和long 在32位上占用 4个字节在64位上占用8个字节因此可以先将 指针转成 unsigned long由于打印的时候需要的%x因此在转成unsigned int//这里可以思考一个额外的问题这里将指针 从 unsigned long 强行转成 unsigned int 会不会有丢失呢printf(argv[%d]内容%s\n, i, argv[i]);}// 下边环境变量随便打两个,只要打印的地址和上面的地址能够连贯就说明我们关于 参数的地址和环境变量的地址挨着的判断是正确的for (int i 0; i 2; i){printf(evriron[%d]地址%x , i, (unsigned int)((unsigned long)environ[i]));printf(evriron[%d]内容%s\n, i, environ[i]);}cout 验证argc 和 argv 以及 环境变量的存储是否是我们理解的布局 end endl;//这里注意的是这段代码在vs2017上和 在linux上执行的结果不同在linux的内存图确实如我们所想但是在vs2017上是environ的内存在前面argv的内存在后边
}
//这里注意的是这段代码在vs2017上和 在linux上执行的结果不同在linux的内存图确实如我们所想但是在vs2017上是environ的内存在前面argv的内存在后边
注意这两行
argv[3]地址93a03693 argv[3]内容cc evriron[0]地址93a03696 evriron[0]内容LC_PAPERzh_CN.UTF-8
evrion[0]的地址刚好紧挨着 argv[3]这说明我们的猜想是对的 hunandedehunandede-virtual-machine:/mnt/hgfs/linux/4-2/nginx$ ./nginx aa bb cc
验证argc 和 argv 以及 环境变量的存储是否是我们理解的布局 start
参数个数argc的值为4
argv[0]地址93a03685 argv[0]内容./nginx
argv[1]地址93a0368d argv[1]内容aa
argv[2]地址93a03690 argv[2]内容bb
argv[3]地址93a03693 argv[3]内容cc
evriron[0]地址93a03696 evriron[0]内容LC_PAPERzh_CN.UTF-8
evriron[1]地址93a036ab evriron[1]内容LC_ADDRESSzh_CN.UTF-8
验证argc 和 argv 以及 环境变量的存储是否是我们理解的布局 end那么在linux上的原理图大致如下 实现思路 (1)重新分配一块内存用来保存environ中的内容 //第一步 环境变量原先的地址cout环境变量原先的地址endl;for (int i 0; environ[i]; i){printf(evriron[%d]地址%x ,i,(unsigned int)((unsigned long)environ[i]));printf(evriron[%d]内容%s\n ,i,environ[i]);}printf(--------------------------搬家中------------------------------\n);//让环境变量搬家g_os_argv (char **)argv;ngx_init_setproctitle(); // 把环境变量搬家cout环境变量搬家后的地址endl;for (int i 0; environ[i]; i){printf(evriron[%d]地址%x ,i,(unsigned int)((unsigned long)environ[i]));printf(evriron[%d]内容%s\n ,i,environ[i]);}
其核心是这个方法 ngx_init_setproctitle(); // 把环境变量搬家
//设置可执行程序标题相关函数分配内存并且把环境变量拷贝到新内存中来
void ngx_init_setproctitle()
{ int i;//统计环境变量所占的内存。注意判断方法是environ[i]是否为空作为环境变量结束标记for (i 0; environ[i]; i) {g_environlen strlen(environ[i]) 1; //1是因为末尾有\0,是占实际内存位置的要算进来} //end for//这里无需判断penvmen NULL,有些编译器new会返回NULL有些会报异常但不管怎样如果在重要的地方new失败了你无法收场让程序失控崩溃助你发现问题为好 gp_envmem new char[g_environlen]; memset(gp_envmem,0,g_environlen); //内存要清空防止出现问题char *ptmp gp_envmem;//把原来的内存内容搬到新地方来for (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)修改argv[0]所指向的内存 // 第二步设置标题代码title我要保证所有命令行参数我都不 用了如果还要用那么建议存储起来并告知其他模块的owner如果要使用启动程序时的参数请到存储的地方去拿。这个要其他用到的owner sync到位 ngx_setproctitle(nginx: my master process); //设置可执行程序标题
void ngx_setproctitle(const char *title)
{//我们假设所有的命令 行参数我们都不需要用到了可以被随意覆盖了//注意我们的标题长度不会长到原始标题和原始环境变量都装不下否则怕出问题不处理//(1)计算新标题长度size_t ititlelen strlen(title); //(2)计算总的原始的argv那块内存的总长度【包括各种参数】size_t e_environlen 0; //e表示局部变量for (int i 0; g_os_argv[i]; i) {e_environlen strlen(g_os_argv[i]) 1;}size_t esy e_environlen g_environlen; //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;
} 测试结果。
在一个终端让其跑起来
hunandedehunandede-virtual-machine:/mnt/hgfs/linux/4-2/nginx$ ./nginx aa bb cc 在另一个终端测试看是否改动成功我们在代码中是写成替换成my master process