Linux下程序启动之后的初始化---检查配置文件及读取日志配置项的值

概述

最近,我对本开发组的几位新员工所编写的程序进行了代码走查,发现他们的代码都有一个共同的问题:缺少必要的初始化。在本文中,我将详细介绍程序在启动时所必须要进行的初始化操作,并用实际的C代码予以说明。

对于一般的程序来说,在启动时所必须要进行的初始化操作有两个:检查配置文件及读取日志配置项。对于检查配置文件,主要检查配置文件是否是规定的文件类型(后缀是否正确)及是否存放在了规定的目录下(一般存放在当前用户的etc目录下);对于读取日志配置项,主要将写日志文件相关的参数(如日志级别、日志文件最大长度、最大备份日志文件数量等)从配置文件的日志段中读取出来,放到全局变量中,以便后续流程使用。

可以用如下的流程图来形象地表示程序的整个流程:

从上图可以看出,程序启动之后,如果检查配置文件和读取日志配置项两者之一不通过,那么程序就不会执行后续流程。由此也可以看到,初始化操作在程序中的重要地位。下面我们用实际的C代码来说明上图中所示的两个初始化操作。

程序代码

为了便于说明,假设我们的主程序文件名为InitEnv.c,配置文件名为InitEnv.ini。同时,因为要从配置文件中读取日志配置项的值,我们编写GetConfig.c和GetConfig.h文件来提供从文件中读取配置项值的操作(也就是API)。三个文件的代码内容如下:

InitEnv.c:

/**********************************************************************

* 版权所有 (C)2016, Zhou Zhaoxiong

*

* 文件名称:InitEnv.c

* 文件标识:无

* 内容摘要:程序运行之前检查配置文件和日志文件是否存在

* 其它说明:无

* 当前版本:V1.0

* 作    者:ZhouZhaoxiong

* 完成日期:20161213

*

**********************************************************************/

#include "GetConfig.h"

// 宏定义

#define INI_FILE_NAME  "InitEnv.ini"

#define LOG_FILE_NAME  "InitEnv.log"

// 日志模块全局参数结构

typedef struct

{

UINT32    iLoglevel;                // 日志级别

UINT32    iMaxLogSize;              // 日志文件最大长度

UINT32    iMaxBakCount;             // 最大备份日志文件数量

UINT32    iNewLogFileFlag;          // 启动时是否新建空日志文件, 否则追加到上次的日志文件中,1-Yes 0-No

UINT32    iLogPositionFlag;         // 是否输出日志位置信息(文件名/行号),1-Yes 0-No

UINT8     szLogFileFullName[256];   // 日志文件全路径名称

} T_LogInfo;

T_LogInfo t_loginfo = {0};

// 函数声明

INT32 AccessCfgFile(UINT8 *pszCfgFileName, UINT8*pszCfgFileFullName);

INT32 InitLogInfo(UINT8 *pszCfgFileFullName);

/****************************************************************

* 功能描述: 主函数

* 输入参数: 无

* 输出参数: 无

* 返回值: 0-执行完成

* 其他说明: 无

* 修改日期      版本号       修改人       修改内容

*-------------------------------------------------------------

* 20161213       V1.0     Zhou Zhaoxiong     创建

****************************************************************/

INT32 main(void)

{

INT32 iRetVal                = 0;

UINT8 szCfgFileFullName[256]= {0};

// 首先检查配置文件是否存在,并获取带全路径的配置文件名

iRetVal =AccessCfgFile(INI_FILE_NAME, szCfgFileFullName);

if (iRetVal !=0)  // 配置文件不存在, 直接返回

{

printf("exec AccessCfgFile failed!\n");

return -1;

}

// 打印获取到的带全路径的配置文件名

printf("CfgFileFullName is %s!\n", szCfgFileFullName);

// 然后读取配置文件, 初始化日志信息

iRetVal =InitLogInfo(szCfgFileFullName);

if (iRetVal !=0)  // 读取配置文件失败, 直接返回

{

printf("exec InitLogInfo failed!\n");

return -1;

}

// 打印初始化的日志信息

printf("Loglevel is %d, MaxLogSize is %d(MB), MaxBakCount is %d,NewLogFileFlag is %d, LogPositionFlag is %d, LogFileFullName is %s!\n",t_loginfo.iLoglevel, t_loginfo.iMaxLogSize, t_loginfo.iMaxBakCount,

t_loginfo.iNewLogFileFlag, t_loginfo.iLogPositionFlag,t_loginfo.szLogFileFullName);

return 0;

}

/****************************************************************

* 功能描述: 检查配置文件是否存在,并获取带全路径的配置文件名

* 输入参数: pszCfgFileName-不带路径的配置文件名

* 输出参数: pszCfgFileFullName-带全路径的配置文件名

* 返回值: 0-存在 -1-不存在 -2-程序处理异常

* 其他说明: 无

* 修改日期      版本号       修改人       修改内容

*-------------------------------------------------------------

* 20161213       V1.0     Zhou Zhaoxiong     创建

****************************************************************/

INT32 AccessCfgFile(UINT8 *pszCfgFileName, UINT8*pszCfgFileFullName)

{

UINT8  szTmpCfgFileName[256] = {0};

UINT8 *pFindStr              = NULL;

if (NULL ==pszCfgFileName || NULL == pszCfgFileFullName)

{

printf("AccessCfgFile: pszCfgFileName or pszCfgFileFullName isNULL!\n");

return -2;

}

// 判断配置文件的后缀是否为ini

pFindStr =strstr(pszCfgFileName, ".ini");

if (pFindStr ==NULL)   // 配置文件后缀错误,直接返回

{

printf("AccessCfgFile: the suffix of %s is not ini, pleasecheck!\n", pszCfgFileName);

return -2;

}

// 获取带全路径的配置文件名

snprintf(szTmpCfgFileName, sizeof(szTmpCfgFileName)-1,"%s/etc/%s", getenv("HOME"), pszCfgFileName);

// 判断配置文件是否存在

if (0 == access(szTmpCfgFileName,F_OK))   // 配置文件存在

{

snprintf(pszCfgFileFullName, sizeof(szTmpCfgFileName)-1, "%s",szTmpCfgFileName);

}

else

{

printf("AccessCfgFile: %s has not existed!\n",szTmpCfgFileName);

return -1;

}

return 0;

}

/****************************************************************

* 功能描述: 读取配置文件, 初始化日志信息

* 输入参数: pszCfgFileFullName-带全路径的配置文件名

* 输出参数: 无

* 返回值: 0-处理成功 -1-处理失败

* 其他说明: 无

* 修改日期      版本号       修改人       修改内容

* -------------------------------------------------------------

* 20161213       V1.0     Zhou Zhaoxiong     创建

****************************************************************/

INT32 InitLogInfo(UINT8 *pszCfgFileFullName)

{

if (NULL ==pszCfgFileFullName)

{

printf("InitLogInfo:pszCfgFileFullName is NULL!\n");

return -1;

}

// 日志级别0-Fatal 1-Error 2-Warn 3-Info 4-Trace 5-Debug 6-All

t_loginfo.iLoglevel= GetConfigFileIntValue("LOG", "LogLevel", 3,pszCfgFileFullName);

// 日志文件最大长度, 单位MB, 范围是[1,500]

t_loginfo.iMaxLogSize = GetConfigFileIntValue("LOG","LogMaxSize", 5, pszCfgFileFullName);

if(t_loginfo.iMaxLogSize < 1 || t_loginfo.iMaxLogSize > 500)

{

t_loginfo.iMaxLogSize = 10;          // 配置超出[1,500]范围默认10M

}

// 最大备份日志文件数量, 范围是[1,999]

t_loginfo.iMaxBakCount = GetConfigFileIntValue("LOG","BackupCount", 10, pszCfgFileFullName);

if(t_loginfo.iMaxBakCount < 1 || t_loginfo.iMaxBakCount > 999)

{

t_loginfo.iMaxBakCount = 10;    //配置超出[1,999]范围默认10个

}

// 每次启动是否新建空日志文件

t_loginfo.iNewLogFileFlag = GetConfigFileIntValue("LOG","NewLogFileFlag", 1, pszCfgFileFullName);

// 是否输出日志位置信息(文件名/行号)标志

t_loginfo.iLogPositionFlag = GetConfigFileIntValue("LOG","LogPositionFlag", 1, pszCfgFileFullName);

// 记录日志文件全路径名称

snprintf(t_loginfo.szLogFileFullName,sizeof(t_loginfo.szLogFileFullName)-1, "%s/log/%s",getenv("HOME"), LOG_FILE_NAME);

return 0;

}

GetConfig.h:

/**********************************************************************

* 版权所有 (C)2016, Zhou Zhaoxiong。

*

* 文件名称:GetConfig.h

* 文件标识:无

* 内容摘要:Linux下配置文件的读取

* 其它说明:无

* 当前版本:V1.0

* 作    者:ZhouZhaoxiong

* 完成日期:20161213

*

**********************************************************************/

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <dirent.h>

#include <unistd.h>

// 数据类型重定义

typedef unsigned char  UINT8;

typedef signed  int    INT32;

typedef unsigned int   UINT32;

// 函数声明

void GetStringContentValue(FILE *fp, UINT8 *pszSectionName,UINT8 *pszKeyName, UINT8 *pszOutput, UINT32 iOutputLen);

void GetConfigFileStringValue(UINT8 *pszSectionName, UINT8*pszKeyName, UINT8 *pDefaultVal, UINT8 *pszOutput, UINT32 iOutputLen, UINT8*pszConfigFileName);

INT32 GetConfigFileIntValue(UINT8 *pszSectionName, UINT8*pszKeyName, UINT32 iDefaultVal, UINT8 *pszConfigFileName);

GetConfig.c:

/**********************************************************************

* 版权所有 (C)2016, Zhou Zhaoxiong。

*

* 文件名称:GetConfig.c

* 文件标识:无

* 内容摘要:Linux下配置文件的读取

* 其它说明:无

* 当前版本:V1.0

* 作    者:ZhouZhaoxiong

* 完成日期:20161213

*

**********************************************************************/

#include "GetConfig.h"

/**********************************************************************

* 功能描述:获取具体的字符串值

* 输入参数: fp-配置文件指针

pszSectionName-段名, 如: GENERAL

pszKeyName-配置项名, 如:EmployeeName

iOutputLen-输出缓存长度

* 输出参数: pszOutput-输出缓存

* 返回值:无

* 其它说明:无

* 修改日期        版本号       修改人      修改内容

*------------------------------------------------------------------

* 20161213      V1.0     Zhou Zhaoxiong     创建

********************************************************************/

void GetStringContentValue(FILE *fp, UINT8 *pszSectionName,UINT8 *pszKeyName, UINT8 *pszOutput, UINT32 iOutputLen)

{

UINT8  szSectionName[100]    = {0};

UINT8  szKeyName[100]        = {0};

UINT8  szContentLine[256]    = {0};

UINT8  szContentLineBak[256] = {0};

UINT32iContentLineLen       = 0;

UINT32iPositionFlag         = 0;

// 先对输入参数进行异常判断

if (fp == NULL ||pszSectionName == NULL || pszKeyName == NULL || pszOutput == NULL)

{

printf("GetStringContentValue: input parameter(s) isNULL!\n");

return;

}

sprintf(szSectionName, "[%s]", pszSectionName);

strcpy(szKeyName,pszKeyName);

while (feof(fp) ==0)

{

memset(szContentLine, 0x00, sizeof(szContentLine));

fgets(szContentLine, sizeof(szContentLine), fp);      // 获取段名

// 判断是否是注释行(以;开头的行就是注释行)或以其他特殊字符开头的行

if(szContentLine[0] == ‘;‘ || szContentLine[0] == ‘\r‘ || szContentLine[0] ==‘\n‘ || szContentLine[0] == ‘\0‘)

{

continue;

}

// 匹配段名

if(strncasecmp(szSectionName, szContentLine, strlen(szSectionName)) == 0)

{

while(feof(fp) == 0)

{

memset(szContentLine,    0x00,sizeof(szContentLine));

memset(szContentLineBak, 0x00, sizeof(szContentLineBak));

fgets(szContentLine, sizeof(szContentLine), fp);     // 获取字段值

// 判断是否是注释行(以;开头的行就是注释行)

if(szContentLine[0] == ‘;‘)

{

continue;

}

memcpy(szContentLineBak, szContentLine, strlen(szContentLine));

// 匹配配置项名

if(strncasecmp(szKeyName, szContentLineBak, strlen(szKeyName)) == 0)

{

iContentLineLen = strlen(szContentLine);

for(iPositionFlag = strlen(szKeyName); iPositionFlag <= iContentLineLen;iPositionFlag ++)

{

if (szContentLine[iPositionFlag] == ‘ ‘)

{

continue;

}

if (szContentLine[iPositionFlag] == ‘=‘)

{

break;

}

iPositionFlag = iContentLineLen + 1;

break;

}

iPositionFlag = iPositionFlag + 1;   // 跳过=的位置

if(iPositionFlag > iContentLineLen)

{

continue;

}

memset(szContentLine, 0x00, sizeof(szContentLine));

strcpy(szContentLine, szContentLineBak + iPositionFlag);

// 去掉内容中的无关字符

for(iPositionFlag = 0; iPositionFlag < strlen(szContentLine); iPositionFlag ++)

{

if (szContentLine[iPositionFlag] == ‘\r‘ || szContentLine[iPositionFlag]== ‘\n‘ || szContentLine[iPositionFlag] == ‘\0‘)

{

szContentLine[iPositionFlag] = ‘\0‘;

break;

}

}

// 将配置项内容拷贝到输出缓存中

strncpy(pszOutput, szContentLine, iOutputLen-1);

break;

}

else if(szContentLine[0] == ‘[‘)

{

break;

}

}

break;

}

}

}

/**********************************************************************

* 功能描述:从配置文件中获取字符串

* 输入参数: pszSectionName-段名, 如:GENERAL

pszKeyName-配置项名, 如:EmployeeName

pDefaultVal-默认值

iOutputLen-输出缓存长度

pszConfigFileName-配置文件名

* 输出参数: pszOutput-输出缓存

* 返回值:无

* 其它说明:无

* 修改日期       版本号        修改人      修改内容

*------------------------------------------------------------------

* 20161213      V1.0     Zhou Zhaoxiong     创建

********************************************************************/

void GetConfigFileStringValue(UINT8 *pszSectionName, UINT8*pszKeyName, UINT8 *pDefaultVal, UINT8 *pszOutput, UINT32 iOutputLen, UINT8*pszConfigFileName)

{

FILE  *fp                    = NULL;

UINT8  szWholePath[256]      = {0};

// 先对输入参数进行异常判断

if (pszSectionName== NULL || pszKeyName == NULL || pszOutput == NULL || pszConfigFileName ==NULL)

{

printf("GetConfigFileStringValue: input parameter(s) isNULL!\n");

return;

}

// 获取默认值

if (pDefaultVal ==NULL)

{

strcpy(pszOutput, "");

}

else

{

strcpy(pszOutput, pDefaultVal);

}

// 打开配置文件

fp =fopen(pszConfigFileName, "r");

if (fp == NULL)

{

printf("GetConfigFileStringValue: open %s failed!\n",szWholePath);

return;

}

// 调用函数用于获取具体配置项的值

GetStringContentValue(fp, pszSectionName, pszKeyName, pszOutput,iOutputLen);

// 关闭文件

fclose(fp);

fp = NULL;

}

/**********************************************************************

* 功能描述:从配置文件中获取整型变量

* 输入参数: pszSectionName-段名, 如:GENERAL

pszKeyName-配置项名, 如:EmployeeName

iDefaultVal-默认值

pszConfigFileName-配置文件名

* 输出参数:无

* 返回值:iGetValue-获取到的整数值  -1-获取失败

* 其它说明:无

* 修改日期        版本号      修改人       修改内容

*------------------------------------------------------------------

* 20161213      V1.0     Zhou Zhaoxiong     创建

********************************************************************/

INT32 GetConfigFileIntValue(UINT8 *pszSectionName, UINT8*pszKeyName, UINT32 iDefaultVal, UINT8 *pszConfigFileName)

{

UINT8  szGetValue[512] = {0};

INT32  iGetValue       = 0;

// 先对输入参数进行异常判断

if (pszSectionName== NULL || pszKeyName == NULL || pszConfigFileName == NULL)

{

printf("GetConfigFileIntValue: input parameter(s) isNULL!\n");

return -1;

}

GetConfigFileStringValue(pszSectionName, pszKeyName, NULL, szGetValue,512-1, pszConfigFileName);    // 先将获取的值存放在字符型缓存中

if (szGetValue[0] ==‘\0‘ || szGetValue[0] == ‘;‘)    // 如果是结束符或分号, 则使用默认值

{

iGetValue =iDefaultVal;

}

else

{

iGetValue =atoi(szGetValue);

}

return iGetValue;

}

程序说明

我们主要对InitEnv.c文件进行说明:

第一,检查配置文件的操作是由AccessCfgFile函数完成的,该函数首先判断配置文件的后缀是否为ini,然后获取带全路径的配置文件名,最后判断配置文件是否存在。如果该函数执行失败(配置文件不存在或其他),那么直接停止程序的运行,不再执行后续流程。

第二,读取日志配置项的操作是由InitLogInfo函数完成的,该函数从配置文件InitEnv.ini的[LOG]段中将LogLevel、LogMaxSize、BackupCount、NewLogFileFlag和LogPositionFlag配置项的值读取出来。如果该函数执行失败,那么直接停止程序的运行,不再执行后续流程。

第三,如果AccessCfgFile和InitLogInfo函数都执行成功,那么就意味着程序初始化成功了,可以执行后续操作。本程序的后续操作便是打印读取到的配置项的值及日志文件全路径名称。

程序测试

我们将上述三个文件上传到Linux机器上,并使用“gcc -g -o InitEnvInitEnv.c  GetConfig.c”命令编译之后,生成InitEnv文件。执行“InitEnv”命令,即可对程序进行测试。

1)当配置文件InitEnv.ini不存在或未被放置到规定目录时,程序运行结果如下:

AccessCfgFile: /home/zhou/etc/InitEnv.ini has not existed!

exec AccessCfgFile failed!

2)当配置文件InitEnv.ini内容如下:

[LOG]

;LogLevel, 0-Fatal 1-Error 2-Warn 3-Info4-Trace 5-Debug 6-All

LogLevel=4

;Max log size (MB), [1,500]

LogMaxSize=10

;Max log backup count [1,999]

BackupCount=100

;If output log into new file when starting, 1-Yes 0-No

NewLogFileFlag=0

;If output position info(filename/linenum), 1-Yes 0-No

LogPositionFlag=1

程序运行结果为:

CfgFileFullName is /home/zhou/etc/InitEnv.ini!

Loglevel is 4, MaxLogSize is 10(MB), MaxBakCount is 100,NewLogFileFlag is 0, LogPositionFlag is 1, LogFileFullName is /home/zhou/log/InitEnv.log!

可见,在正常情况下,程序能够完成检查配置文件及读取日志配置项的操作,大家也可以对程序进行更多的测试。

总结

在程序进行具体的操作之前,一些初始化操作是必不可少的。本文中的示例只是涉及到检查配置文件和读取日志配置项两个操作,在实际的应用中可能还会有初始化数据库参数、建立与其他模块的通信链路等操作,这要视不同的程序而定。

“磨刀不误砍柴工”,当程序完成了必要的初始化操作之后,便可以进行正式的操作了。

时间: 2024-10-24 07:58:57

Linux下程序启动之后的初始化---检查配置文件及读取日志配置项的值的相关文章

linux下nginx启动停止重启控制脚本

这是控制nginx服务的脚本文件,包括控制nginx的启动.重启.停止.平滑重启.对配置文件的额检查. [[email protected] ~]# cat nginx.sh #!/bin/env bash # description:nginx server   ###必须加描述 # nginx - this script is used to control nginx service # processname nginx # chkconfig: - 85 15 # edit by su

linux下mysql启动与停止

mysql.启动与停止   1.启动   MySQL安装完成后启动文件mysql在/etc/init.d目录下,   在需要启动时运行下面命令即可.   [[email protected] init.d]# /etc/init.d/mysql start   2.停止 /usr/bin/mysqladmin -u root -p shutdown   3.自动启动   1)察看mysql是否在自动启动列表中   [[email protected] local]# /sbin/chkconfi

linux&nbsp;下&nbsp;apache启动、停止、重启命令

原文:linux 下 apache启动.停止.重启命令 基本的操作方法: 本文假设你的apahce安装目录为/usr/local/apache2,这些方法适合任何情况 apahce启动命令: 推荐/usr/local/apache2/bin/apachectl start apaceh启动 apache停止命令 /usr/local/apache2/bin/apachectl stop   停止 apache重新启动命令: /usr/local/apache2/bin/apachectl res

如何解决linux下apache启动时httpd: apr_sockaddr_info_get() failed for 报错

今天在家里的RHLE5.5上安装apache的时候,先用user1用户./configure命令配置,然后才用root用户make && make install,结果apache起来的时候就报如下错误: httpd: apr_sockaddr_info_get() failed for bogon httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 fo

Centos | Linux 下安装启动 mysql 出现 8618 [ERROR] Aborting,查看日志:Plugin &#39;FEDERATED&#39; is disabled.

1.试试启动时指定配置文件 ./bin/mysqld_safe --defaults-file=mysql.cnf 或 ./bin/mysqld_safe --defaults-file=mysql.cnf $ 2.试试修改 mysql.cnf(也可能是my.cnf) 在 [mysqld]下指定tmpdir tmpdir = youTmpdir Centos | Linux 下安装启动 mysql 出现 8618 [ERROR] Aborting,查看日志:Plugin 'FEDERATED'

Linux下Tomcat启动后显示控制台

Linux下Tomcat启动后显示控制台: 采用 ./startup.sh 启动,则没有显示控制台. 要像windows一样显示控制台,则 : ./catalina.sh run

linux下程序运行时间

如何计算程序运行时间,呵呵,一大堆复杂的程序, 先来个简单的,qsort算法,数据吗?随机产生: 看见了吧,(当然,在数据量小的情况下,感觉不到差别,但是在数据量超大的情况下,就会有明显感觉了.) 60000000 vs 10000000 时间消耗有区别哦. [[email protected] ctest]# time ./33 hehe:60000000 ----------------------------------------before quick sort------------

Linux下Oracle启动、建立表空间、用户、授权、数据库导入导出

1.1进入到sqlplus启动实例 [[email protected] ~]$ su - oracle                                 --“切换到oracle用户”[[email protected] ~]$ lsnrctl start                               --“打开监听”[[email protected] ~]$ sqlplus /nolog                                --“进入到

linux下无法启动eclipse:/usr/local/jdk1.6.0_17/bin/../jre/lib/i386/client/libjvm.so: cannot restore segmen

解决办法: vi /etc/sysconfig/selinux 将: SELINUX=enforcing 改为: #SELINUX=enforcingSELINUX=disabled 重启linux linux下无法启动eclipse:/usr/local/jdk1.6.0_17/bin/../jre/lib/i386/client/libjvm.so: cannot restore segmen