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

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

概述

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

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

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

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

程序代码

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

InitEnv.c:

  1. /********************************************************************** 
  2. * 版权所有 (C)2016, Zhou Zhaoxiong 
  3. * 文件名称:InitEnv.c 
  4. * 文件标识:无 
  5. * 内容摘要:程序运行之前检查配置文件和日志文件是否存在 
  6. * 其它说明:无 
  7. * 当前版本:V1.0 
  8. * 作   者:ZhouZhaoxiong 
  9. * 完成日期:20161213 
  10. **********************************************************************/ 
  11. #include "GetConfig.h" 
  12.  
  13. // 宏定义 
  14. #define INI_FILE_NAME  "InitEnv.ini" 
  15. #define LOG_FILE_NAME  "InitEnv.log" 
  16.  
  17. // 日志模块全局参数结构 
  18. typedef struct 
  19.     UINT32    iLoglevel;                // 日志级别 
  20.     UINT32    iMaxLogSize;              // 日志文件最大长度 
  21.     UINT32    iMaxBakCount;             // 最大备份日志文件数量 
  22.     UINT32    iNewLogFileFlag;          // 启动时是否新建空日志文件, 否则追加到上次的日志文件中,1-Yes 0-No 
  23.     UINT32    iLogPositionFlag;         // 是否输出日志位置信息(文件名/行号),1-Yes 0-No 
  24.     UINT8     szLogFileFullName[256];   // 日志文件全路径名称 
  25. } T_LogInfo; 
  26. T_LogInfo t_loginfo = {0}; 
  27.  
  28.  
  29. // 函数声明 
  30. INT32 AccessCfgFile(UINT8 *pszCfgFileName, UINT8 *pszCfgFileFullName); 
  31. INT32 InitLogInfo(UINT8 *pszCfgFileFullName); 
  32.  
  33.  
  34. /**************************************************************** 
  35. * 功能描述: 主函数 
  36. * 输入参数: 无 
  37. * 输出参数: 无 
  38. * 返 回 值: 0-执行完成 
  39. * 其他说明: 无 
  40. * 修改日期      版本号        修改人         修改内容 
  41. *------------------------------------------------------------- 
  42. * 20161213     V1.0     Zhou Zhaoxiong     创建 
  43. ****************************************************************/ 
  44. INT32 main(void) 
  45.     INT32 iRetVal                = 0; 
  46.     UINT8 szCfgFileFullName[256] = {0}; 
  47.  
  48.     // 首先检查配置文件是否存在,并获取带全路径的配置文件名 
  49.     iRetVal =AccessCfgFile(INI_FILE_NAME, szCfgFileFullName); 
  50.     if (iRetVal !=0)  // 配置文件不存在, 直接返回 
  51.     { 
  52.        printf("exec AccessCfgFile failed!\n"); 
  53.         return -1; 
  54.     } 
  55.  
  56.     // 打印获取到的带全路径的配置文件名 
  57.     printf("CfgFileFullName is %s!\n", szCfgFileFullName); 
  58.  
  59.     // 然后读取配置文件, 初始化日志信息 
  60.     iRetVal =InitLogInfo(szCfgFileFullName); 
  61.     if (iRetVal !=0)  // 读取配置文件失败, 直接返回 
  62.     { 
  63.        printf("exec InitLogInfo failed!\n"); 
  64.         return -1; 
  65.     } 
  66.  
  67.     // 打印初始化的日志信息 
  68.     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, 
  69.            t_loginfo.iNewLogFileFlag, t_loginfo.iLogPositionFlag,t_loginfo.szLogFileFullName); 
  70.  
  71.     return 0; 
  72.  
  73.  
  74. /**************************************************************** 
  75. * 功能描述: 检查配置文件是否存在,并获取带全路径的配置文件名 
  76. * 输入参数: pszCfgFileName-不带路径的配置文件名 
  77. * 输出参数: pszCfgFileFullName-带全路径的配置文件名 
  78. * 返 回 值: 0-存在 -1-不存在 -2-程序处理异常 
  79. * 其他说明: 无 
  80. * 修改日期      版本号         修改人       修改内容 
  81. *------------------------------------------------------------- 
  82. * 20161213     V1.0     Zhou Zhaoxiong     创建 
  83. ****************************************************************/ 
  84. INT32 AccessCfgFile(UINT8 *pszCfgFileName, UINT8*pszCfgFileFullName) 
  85.     UINT8  szTmpCfgFileName[256] = {0}; 
  86.     UINT8 *pFindStr              = NULL
  87.  
  88.     if (NULL ==pszCfgFileName || NULL == pszCfgFileFullName) 
  89.     { 
  90.        printf("AccessCfgFile: pszCfgFileName or pszCfgFileFullName isNULL!\n"); 
  91.         return -2; 
  92.     } 
  93.  
  94.     // 判断配置文件的后缀是否为ini 
  95.     pFindStr =strstr(pszCfgFileName, ".ini"); 
  96.     if (pFindStr ==NULL)   // 配置文件后缀错误,直接返回 
  97.     { 
  98.        printf("AccessCfgFile: the suffix of %s is not ini, pleasecheck!\n", pszCfgFileName); 
  99.         return -2; 
  100.     } 
  101.  
  102.     // 获取带全路径的配置文件名 
  103.    snprintf(szTmpCfgFileName, sizeof(szTmpCfgFileName)-1,"%s/etc/%s", getenv("HOME"), pszCfgFileName); 
  104.  
  105.     // 判断配置文件是否存在 
  106.     if (0 == access(szTmpCfgFileName,F_OK))   // 配置文件存在 
  107.     { 
  108.        snprintf(pszCfgFileFullName, sizeof(szTmpCfgFileName)-1, "%s",szTmpCfgFileName); 
  109.     } 
  110.     else 
  111.     { 
  112.        printf("AccessCfgFile: %s has not existed!\n",szTmpCfgFileName); 
  113.         return -1; 
  114.     } 
  115.  
  116.     return 0; 
  117.  
  118.  
  119. /**************************************************************** 
  120. * 功能描述: 读取配置文件, 初始化日志信息 
  121. * 输入参数: pszCfgFileFullName-带全路径的配置文件名 
  122. * 输出参数: 无 
  123. * 返 回 值: 0-处理成功 -1-处理失败 
  124. * 其他说明: 无 
  125. * 修改日期       版本号       修改人        修改内容 
  126. ------------------------------------------------------------- 
  127. * 20161213     V1.0     Zhou Zhaoxiong     创建 
  128. ****************************************************************/ 
  129. INT32 InitLogInfo(UINT8 *pszCfgFileFullName) 
  130.     if (NULL ==pszCfgFileFullName) 
  131.     { 
  132.         printf("InitLogInfo:pszCfgFileFullName is NULL!\n"); 
  133.         return -1; 
  134.     } 
  135.  
  136.     // 日志级别0-Fatal 1-Error 2-Warn 3-Info 4-Trace 5-Debug 6-All 
  137.     t_loginfo.iLoglevel= GetConfigFileIntValue("LOG""LogLevel", 3,pszCfgFileFullName); 
  138.  
  139.     // 日志文件最大长度, 单位MB, 范围是[1,500] 
  140.    t_loginfo.iMaxLogSize = GetConfigFileIntValue("LOG","LogMaxSize", 5, pszCfgFileFullName); 
  141.     if(t_loginfo.iMaxLogSize < 1 || t_loginfo.iMaxLogSize > 500) 
  142.     { 
  143.        t_loginfo.iMaxLogSize = 10;          // 配置超出[1,500]范围默认10M 
  144.     } 
  145.  
  146.     // 最大备份日志文件数量, 范围是[1,999] 
  147.     t_loginfo.iMaxBakCount = GetConfigFileIntValue("LOG","BackupCount", 10, pszCfgFileFullName); 
  148.     if(t_loginfo.iMaxBakCount < 1 || t_loginfo.iMaxBakCount > 999) 
  149.     { 
  150.        t_loginfo.iMaxBakCount = 10;    //配置超出[1,999]范围默认10个 
  151.     } 
  152.  
  153.     // 每次启动是否新建空日志文件 
  154.     t_loginfo.iNewLogFileFlag = GetConfigFileIntValue("LOG","NewLogFileFlag", 1, pszCfgFileFullName); 
  155.  
  156.     // 是否输出日志位置信息(文件名/行号)标志 
  157.     t_loginfo.iLogPositionFlag = GetConfigFileIntValue("LOG","LogPositionFlag", 1, pszCfgFileFullName); 
  158.  
  159.     // 记录日志文件全路径名称 
  160.      snprintf(t_loginfo.szLogFileFullName,sizeof(t_loginfo.szLogFileFullName)-1, "%s/log/%s",getenv("HOME"), LOG_FILE_NAME); 
  161.  
  162.     return 0; 

GetConfig.c:

  1. /********************************************************************** 
  2. * 版权所有 (C)2016, Zhou Zhaoxiong。 
  3. * 文件名称:GetConfig.h 
  4. * 文件标识:无 
  5. * 内容摘要:Linux下配置文件的读取 
  6. * 其它说明:无 
  7. * 当前版本:V1.0 
  8. * 作   者:ZhouZhaoxiong 
  9. * 完成日期:20161213 
  10. **********************************************************************/ 
  11. #include <stdio.h> 
  12. #include <stdlib.h> 
  13. #include <string.h> 
  14. #include <dirent.h> 
  15. #include <unistd.h> 
  16.  
  17.  
  18. // 数据类型重定义 
  19. typedef unsigned char   UINT8; 
  20. typedef signed   int    INT32; 
  21. typedef unsigned int    UINT32; 
  22.  
  23.  
  24. // 函数声明 
  25. void GetStringContentValue(FILE *fp, UINT8 *pszSectionName,UINT8 *pszKeyName, UINT8 *pszOutput, UINT32 iOutputLen); 
  26. void GetConfigFileStringValue(UINT8 *pszSectionName, UINT8*pszKeyName, UINT8 *pDefaultVal, UINT8 *pszOutput, UINT32 iOutputLen, UINT8*pszConfigFileName); 
  27. INT32 GetConfigFileIntValue(UINT8 *pszSectionName, UINT8*pszKeyName, UINT32 iDefaultVal, UINT8 *pszConfigFileName); 

GetConfig.c:

  1. /********************************************************************** 
  2. * 版权所有 (C)2016, Zhou Zhaoxiong。 
  3. * 文件名称:GetConfig.c 
  4. * 文件标识:无 
  5. * 内容摘要:Linux下配置文件的读取 
  6. * 其它说明:无 
  7. * 当前版本:V1.0 
  8. * 作   者:ZhouZhaoxiong 
  9. * 完成日期:20161213 
  10. **********************************************************************/ 
  11. #include "GetConfig.h" 
  12.  
  13.  
  14. /********************************************************************** 
  15. * 功能描述:获取具体的字符串值 
  16. * 输入参数: fp-配置文件指针 
  17.            pszSectionName-段名, 如: GENERAL 
  18.            pszKeyName-配置项名, 如:EmployeeName 
  19.            iOutputLen-输出缓存长度 
  20. * 输出参数: pszOutput-输出缓存 
  21. * 返 回 值:无 
  22. * 其它说明:无 
  23. * 修改日期        版本号         修改人        修改内容 
  24. *------------------------------------------------------------------ 
  25. * 20161213       V1.0     Zhou Zhaoxiong     创建 
  26. ********************************************************************/ 
  27. void GetStringContentValue(FILE *fp, UINT8 *pszSectionName,UINT8 *pszKeyName, UINT8 *pszOutput, UINT32 iOutputLen) 
  28.     UINT8  szSectionName[100]    = {0}; 
  29.     UINT8  szKeyName[100]        = {0}; 
  30.     UINT8  szContentLine[256]    = {0}; 
  31.     UINT8  szContentLineBak[256] = {0}; 
  32.     UINT32 iContentLineLen       = 0; 
  33.     UINT32 iPositionFlag         = 0; 
  34.  
  35.     // 先对输入参数进行异常判断 
  36.     if (fp == NULL ||pszSectionName == NULL || pszKeyName == NULL || pszOutput == NULL
  37.     { 
  38.        printf("GetStringContentValue: input parameter(s) isNULL!\n"); 
  39.         return
  40.     } 
  41.  
  42.     sprintf(szSectionName, "[%s]", pszSectionName); 
  43.     strcpy(szKeyName,pszKeyName); 
  44.  
  45.     while (feof(fp) ==0) 
  46.     { 
  47.        memset(szContentLine, 0x00, sizeof(szContentLine)); 
  48.        fgets(szContentLine, sizeof(szContentLine), fp);      // 获取段名 
  49.  
  50.         // 判断是否是注释行(以;开头的行就是注释行)或以其他特殊字符开头的行 
  51.         if(szContentLine[0] == ';' || szContentLine[0] == '\r' || szContentLine[0] =='\n' || szContentLine[0] == '\0'
  52.         { 
  53.             continue
  54.         } 
  55.  
  56.         // 匹配段名 
  57.         if(strncasecmp(szSectionName, szContentLine, strlen(szSectionName)) == 0)     
  58.         { 
  59.             while(feof(fp) == 0) 
  60.             { 
  61.                memset(szContentLine,    0x00,sizeof(szContentLine)); 
  62.                memset(szContentLineBak, 0x00, sizeof(szContentLineBak)); 
  63.                fgets(szContentLine, sizeof(szContentLine), fp);     // 获取字段值 
  64.  
  65.                 // 判断是否是注释行(以;开头的行就是注释行) 
  66.                 if(szContentLine[0] == ';'
  67.                 { 
  68.                    continue
  69.                 } 
  70.  
  71.                memcpy(szContentLineBak, szContentLine, strlen(szContentLine)); 
  72.  
  73.                 // 匹配配置项名 
  74.                 if(strncasecmp(szKeyName, szContentLineBak, strlen(szKeyName)) == 0)     
  75.                 { 
  76.                    iContentLineLen = strlen(szContentLine); 
  77.                     for(iPositionFlag = strlen(szKeyName); iPositionFlag <= iContentLineLen;iPositionFlag ++) 
  78.                     { 
  79.                        if (szContentLine[iPositionFlag] == ' '
  80.                        { 
  81.                            continue
  82.                        } 
  83.                        if (szContentLine[iPositionFlag] == '='
  84.                        { 
  85.                            break; 
  86.                         } 
  87.  
  88.                        iPositionFlag = iContentLineLen + 1; 
  89.                        break; 
  90.                     } 
  91.  
  92.                    iPositionFlag = iPositionFlag + 1;   // 跳过=的位置 
  93.  
  94.                     if(iPositionFlag > iContentLineLen) 
  95.                     { 
  96.                        continue
  97.                     } 
  98.  
  99.                    memset(szContentLine, 0x00, sizeof(szContentLine)); 
  100.                    strcpy(szContentLine, szContentLineBak + iPositionFlag); 
  101.  
  102.                     // 去掉内容中的无关字符 
  103.                     for(iPositionFlag = 0; iPositionFlag < strlen(szContentLine); iPositionFlag ++) 
  104.                     { 
  105.                        if (szContentLine[iPositionFlag] == '\r' || szContentLine[iPositionFlag]== '\n' || szContentLine[iPositionFlag] == '\0'
  106.                        { 
  107.                            szContentLine[iPositionFlag] = '\0'
  108.                            break; 
  109.                        } 
  110.                     } 
  111.  
  112.                     // 将配置项内容拷贝到输出缓存中 
  113.                    strncpy(pszOutput, szContentLine, iOutputLen-1); 
  114.                    break; 
  115.                 } 
  116.                 else if(szContentLine[0] == '['
  117.                 { 
  118.                    break; 
  119.                 } 
  120.             } 
  121.             break; 
  122.         } 
  123.     } 
  124.  
  125.  
  126. /********************************************************************** 
  127. * 功能描述:从配置文件中获取字符串 
  128. * 输入参数: pszSectionName-段名, 如:GENERAL 
  129.            pszKeyName-配置项名, 如:EmployeeName 
  130.            pDefaultVal-默认值 
  131.            iOutputLen-输出缓存长度 
  132.            pszConfigFileName-配置文件名 
  133. * 输出参数: pszOutput-输出缓存 
  134. * 返 回 值:无 
  135. * 其它说明:无 
  136. * 修改日期       版本号         修改人       修改内容 
  137. *------------------------------------------------------------------ 
  138. * 20161213      V1.0     Zhou Zhaoxiong     创建 
  139. ********************************************************************/  
  140. void GetConfigFileStringValue(UINT8 *pszSectionName, UINT8*pszKeyName, UINT8 *pDefaultVal, UINT8 *pszOutput, UINT32 iOutputLen, UINT8*pszConfigFileName) 
  141.     FILE  *fp                    = NULL
  142.     UINT8  szWholePath[256]      = {0}; 
  143.  
  144.     // 先对输入参数进行异常判断 
  145.     if (pszSectionName== NULL || pszKeyName == NULL || pszOutput == NULL || pszConfigFileName ==NULL
  146.     { 
  147.         printf("GetConfigFileStringValue: input parameter(s) isNULL!\n"); 
  148.         return
  149.     } 
  150.  
  151.     // 获取默认值 
  152.     if (pDefaultVal ==NULL
  153.     { 
  154.        strcpy(pszOutput, ""); 
  155.     } 
  156.     else 
  157.     { 
  158.        strcpy(pszOutput, pDefaultVal); 
  159.     } 
  160.  
  161.     // 打开配置文件 
  162.     fp =fopen(pszConfigFileName, "r"); 
  163.     if (fp == NULL
  164.     { 
  165.         printf("GetConfigFileStringValue: open %s failed!\n",szWholePath); 
  166.         return
  167.     } 
  168.  
  169.     // 调用函数用于获取具体配置项的值 
  170.    GetStringContentValue(fp, pszSectionName, pszKeyName, pszOutput,iOutputLen); 
  171.  
  172.     // 关闭文件 
  173.     fclose(fp); 
  174.     fp = NULL
  175.  
  176.  
  177. /********************************************************************** 
  178. * 功能描述:从配置文件中获取整型变量 
  179. * 输入参数:pszSectionName-段名, 如:GENERAL 
  180.           pszKeyName-配置项名, 如:EmployeeName 
  181.           iDefaultVal-默认值 
  182.           pszConfigFileName-配置文件名 
  183. * 输出参数:无 
  184. * 返 回 值:iGetValue-获取到的整数值  -1-获取失败 
  185. * 其它说明:无 
  186. * 修改日期        版本号        修改人       修改内容 
  187. *------------------------------------------------------------------ 
  188. * 20161213      V1.0     Zhou Zhaoxiong     创建 
  189. ********************************************************************/  
  190. INT32 GetConfigFileIntValue(UINT8 *pszSectionName, UINT8*pszKeyName, UINT32 iDefaultVal, UINT8 *pszConfigFileName) 
  191.     UINT8  szGetValue[512] = {0}; 
  192.     INT32  iGetValue       = 0; 
  193.  
  194.     // 先对输入参数进行异常判断 
  195.     if (pszSectionName== NULL || pszKeyName == NULL || pszConfigFileName == NULL
  196.     { 
  197.         printf("GetConfigFileIntValue: input parameter(s) isNULL!\n"); 
  198.         return -1; 
  199.     } 
  200.  
  201.    GetConfigFileStringValue(pszSectionName, pszKeyName, NULL, szGetValue,512-1, pszConfigFileName);    // 先将获取的值存放在字符型缓存中 
  202.  
  203.     if (szGetValue[0] =='\0' || szGetValue[0] == ';')    // 如果是结束符或分号, 则使用默认值 
  204.     { 
  205.         iGetValue =iDefaultVal; 
  206.     } 
  207.     else 
  208.     { 
  209.         iGetValue =atoi(szGetValue); 
  210.     } 
  211.  
  212.     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不存在或未被放置到规定目录时,程序运行结果如下:

  1. AccessCfgFile: /home/zhou/etc/InitEnv.ini has not existed! 
  2. exec AccessCfgFile failed!1212 

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

  1. [LOG] 
  2. ;LogLevel, 0-Fatal 1-Error 2-Warn 3-Info4-Trace 5-Debug 6-All 
  3. LogLevel=4  
  4.  
  5. ;Max log size (MB), [1,500] 
  6. LogMaxSize=10 
  7.  
  8. ;Max log backup count [1,999] 
  9. BackupCount=100 
  10.  
  11. ;If output log into new file when starting, 1-Yes 0-No 
  12. NewLogFileFlag=0 
  13.  
  14. ;If output position info(filename/linenum), 1-Yes 0-No 
  15. LogPositionFlag=1 

程序运行结果为:

  1. CfgFileFullName is /home/zhou/etc/InitEnv.ini! 
  2.  
  3. Loglevel is 4, MaxLogSize is 10(MB), MaxBakCount is 100,NewLogFileFlag is 0, LogPositionFlag is 1, LogFileFullName is /home/zhou/log/InitEnv.log!1212 

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

总结

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

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

【本文是51CTO专栏作者周兆熊的原创文章,作者微信公众号:周氏逻辑(logiczhou)】

戳这里,看该作者更多好文

责任编辑:武晓燕 来源: csdn博客
相关推荐

2010-02-05 09:31:23

Ubuntu 配置文件

2010-02-07 14:50:46

Ubuntu配置文件

2009-12-31 14:54:33

Ubuntu配置文件

2021-12-02 18:15:39

网络库Ratel设计

2013-07-30 11:30:42

Windows PhoWindows Pho

2010-01-12 13:47:18

Linux grub

2011-01-13 16:27:26

Linux配置文件

2023-10-30 15:11:57

2010-11-12 09:44:59

Cassandra配置

2023-08-07 16:14:32

propertiesSpring框架

2011-09-01 18:02:45

samba服务器

2010-02-02 18:19:52

Linux mplay

2009-08-13 09:58:55

C#读取配置文件

2009-08-13 09:16:57

C#读取配置文件

2016-11-11 00:33:25

双重检查锁定延迟初始化线程

2011-03-22 13:37:29

Nagios安装

2010-08-02 16:58:08

Flex配置文件

2011-03-25 17:13:37

Nagios配置文件

2011-02-25 16:39:34

proftpd配置文件

2010-01-08 15:13:44

交换机配置文件
点赞
收藏

51CTO技术栈公众号