由于公司项目的需要,需要将采集器c++代码在Red hat不同版本操作系统移植,出现了好多问题,所以写此文来做一次全面的总结。
首先现介绍一些采集器的功能:
- 响应Server服务器的请求;
- 维护访问物理设备的信息;
- 使用snmp协议周期地访问物理设备,获取需要地数据;
- 存入数据库。
采集器运行的环境:
- 编程语言 :C++
- 操作系统 :RHAS2.1 ,RH9.0,RHAS4.4
- 编译器 :gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5) 和 gcc version 3.4.6 20060404 (Red Hat 3.4.6-3) ,gcc version 2.96 20000731 (Red Hat Linux 7.2 2.96-108.1)
采集器使用的第三方开发包:
- log4cpp-0.3.5rc3 : 写log日志开发包
- net-snmp-5.2.1 : snmp开发包
- unixODBC-2.2.11 : sqlserver2k访问sql接口
- freetds-0.64-stable : sqlserver2k访问sql接口
- ACE : 跨操作系统C++开发平台
- snmp++ : snmp开发包
- expat : expat 是解析xml配置文件的api
在不同操作系统移植时,出现问题最多的是第三方开发包的移植, 例如:
在平台RHAS2.1下,只能使用ACE5.4.1和log4cpp-0.3.5rc3zdsmodi.tar开发包,否则编译不成功;
在平台RH9.0下,只能使用ACE5.4.1和log4cpp-0.3.5rc3.tar.gz开发包,否则编译不成功;
在平台RHAS4.4下,可以使用ACE5.5和log4cpp-0.3.5rc3.tar.gz开发包;
自己开发的代码由于按照c++标准开发的,基本上是重新编译即可。
现总结如下:
一、使用的日志开发包是通过修改得到的,由于早期是使用RHAS2.1操作系统,高版本的log4cpp-0.3.5rc3不支持,低版本也不支持。自己是从网上找到dedian的log4cpp日志的.cpp, .h源文件,复制到 log4cpp-0.3.5rc3目录下,居然编译通过,而且还能使用api写log日志了。这真是幸运呀。以后运行平台都使用RHAS4.4 和RH9.0,可以使用高版本的log4cpp-0.3.5rc3。
二、使用net-snmp-5.2.1 主要是用来在linux机上启动snmpd守候进程,采集器来读取pc机上的一些配置信息。
在red hat 9平台下 net-snmp 在编译会出问题:/usr/bin/ld: cannot find -lelf,那么需要装两个rpm bzip2-devel-1.0.2-8.i386.rpm,elfutils-devel-0.76-3.i386.rpm,重新编译即可。
在Red hat as4,4平台下,编译会出错,报/usr/lib/libpopt.so: could not read symbols: File in wrong format
将各个Makefile文件中对应的popt去掉,重新编译就可以通过。
将各个Makefile文件中对应的popt去掉,重新编译就可以通过。
三、sqlserver2k访问sql接口包unixODBC-2.2.11和freetds-0.64-stable ,是用来在linux平台访问windows平台下的sqlserver2k数据的开发包,在移植操作系统的时候,出现使用开发包自带的tsql或isql命令访问数据库能够正常访问,带使用程序使用对应的api老是报登陆不正确的错误提示,原因是用户名,密码编码的问题,要使用以下语句才可以:
char *charset = NULL;
setlocale(LC_ALL, "");
char* locale = setlocale(LC_ALL, NULL);
DBSETLCHARSET(mLogin,"ISO-8859-1" );
DBSETLNATLANG(mLogin, "us_english");
setlocale(LC_ALL, "");
char* locale = setlocale(LC_ALL, NULL);
DBSETLCHARSET(mLogin,"ISO-8859-1" );
DBSETLNATLANG(mLogin, "us_english");
还有一个隐患,就是高版本的sqlserver数据库有可能不在支持tds协议访问数据库,以后的可能会有问题
四、SNMP++是我最熟悉的snmp协议的api,不过是第一次在linux使用,有些代码比如snmpget等操作还不能编译通过,还有makefile也写的不好,需要重新整理,主要是没有时间。
五、在移植时不同的编译器在编译也会出问题,在编译器gcc3.2.2,主要是使用var-args.h的函数如var_start,var_end,而RHAS4.4使用的是gcc3.4.6,其使用的是c++标准的函数库std-args.h,不在支持var-args.h的函数,所以需要移植,移植代码仅仅举如下例子说明就明白了。这主要是代码没有严格按照标准的c++来编写而造成的问题。
在RHAS2.1和RH9。0使用的gcc3.2.2,其代码是:
#include <varargs.h>
int getAllElemText(XmlFile,NodeName,va_alist)
char *XmlFile ;
char *NodeName ;
va_dcl
{
char *pNodeName ;
va_list ap;
int j;
va_start(ap);
for(j=0;;j++)
{
pNodeName = va_arg(ap,char *);
if(!pNodeName)
break;
#ifdef DEBUG
printf("NO:[%d]pNodeName=[%s] /n",j,pNodeName);
#endif
}
va_end(ap);
return 0;
}
char *XmlFile ;
char *NodeName ;
va_dcl
{
char *pNodeName ;
va_list ap;
int j;
va_start(ap);
for(j=0;;j++)
{
pNodeName = va_arg(ap,char *);
if(!pNodeName)
break;
#ifdef DEBUG
printf("NO:[%d]pNodeName=[%s] /n",j,pNodeName);
#endif
}
va_end(ap);
return 0;
}
在RHAS4.4 的gcc3.4.6只支持std-arg.h,所以其对应的代码如下:
#include <stdarg.h>
int getAllElemText(char* XmlFile,char* NodeName, ...)
{
char *pNodeName ;
va_list ap;
int j;
va_start(ap,NodeName);
for(j=0;;j++)
{
pNodeName = va_arg(ap,char *);
if(!pNodeName)
break;
int getAllElemText(char* XmlFile,char* NodeName, ...)
{
char *pNodeName ;
va_list ap;
int j;
va_start(ap,NodeName);
for(j=0;;j++)
{
pNodeName = va_arg(ap,char *);
if(!pNodeName)
break;
#ifdef DEBUG
printf("NO:[%d]pNodeName=[%s] /n",j,pNodeName);
#endif
}
va_end(ap);
return 0;
}
printf("NO:[%d]pNodeName=[%s] /n",j,pNodeName);
#endif
}
va_end(ap);
return 0;
}
在gcc3.2 中,下面代码是能编译通过,并且能正常运行
unsigned int pos = mPacketStr.find_first_of(":");
if(pos == string::npos)
{
CFileLog::Instance()->log(getpid(),"CAPPResponse","parse","response has not : char",CFileLog::INFO);
return 1;
}
if(pos == string::npos)
{
CFileLog::Instance()->log(getpid(),"CAPPResponse","parse","response has not : char",CFileLog::INFO);
return 1;
}
但是在gcc3.4.6中,虽然能够编译通过,但是出警告错误:
warning: comparison is always true due to limited range of data type,
要把上面的代码改成这样才可以:
string::size_type pos = mPacketStr.find_first_of(":");
if(pos == string::npos)
{
CFileLog::Instance()->log(getpid(),"CAPPResponse","parse","response has not : char",CFileLog::INFO);
return 1;
}
if(pos == string::npos)
{
CFileLog::Instance()->log(getpid(),"CAPPResponse","parse","response has not : char",CFileLog::INFO);
return 1;
}
五、ACE使用出的问题最多。
在RHAS2.1平台下,高版本的ace不能在其上编译,最高只支持ace5.4.1版本的,惨呀,但是花了好长时间试图编译成功,实在不行了,在网上满地的找原因,才在一个ace商用支持的网站发现RHAS2.1最高只支持ace5.4.1版本的,我那个郁闷呀。
在RH9.0平台下,高版本的ace5.5不能在其上编译,只能使用ace5.4.1版本的
而在RHAS4.4平台下,ace5.4.1居然又编译不过了,这次我学乖了,首先在网上看发现,RHAS4.4平台下最低只能支持ace5.4.7的,原来的又废了,一横心下了最高版本的ace5.5,在ace目录下
%mkdir build
%cd build
%make
%cp /home/creco/ats/ACE_wrappers/build/ace/.libs/* /home/creco/ats/ACE_wrappers/lib
%mkdir build
%cd build
%make
%cp /home/creco/ats/ACE_wrappers/build/ace/.libs/* /home/creco/ats/ACE_wrappers/lib
我没有使用make install进行安装只是将库文件拷贝到一个我程序能够找的目录,谁想我的代码老是报ace的错误,找了好长时间才发现问题,发现是在ace目录下没有找到对应的config.h文件,可以在编译的目录下build/ace拷贝到ACE_ROOT/ace目录下即可。