跨平台移植的一些思路
[email protected] 2010.1.11
1.解决方案
将不同平台的开发应用接口重新封装,提供统一开发规范,从而只用书写和管理一套程序代码,达到“一次代码编写,多个平台运行”的目的。
我们知道任何操作系统之上的应用程序的功能实现都依赖于操作系统API。基于这个认识,我们大概可以得到这样一个认识:任何的跨平台代码不过是对不同平台的操作系统的API的一层封装。举个例子,我们设想ttios_open大概可以这样实现:
int ttios_open(
const char *filename,
int oflag
)
{
#if defined _WINDOWS
return _open( filename, oflag);
#else
#if defined _LINUX // 这里假设_LINUX为LINUX平台的标识宏
return open( filename, oflag);
#endif
#endif
}
这里我们设想编写跨平台代码的关键在于在不同平台中找到实现相同功能的接口,然后在上面封装一层外壳供使用者调用。
解决方法即在不同系统平台之上重新封装了一层OS抽象层,并对上层应用程序提供了统一的 API 接口,通过调用 ttios_系列的功能函数,实现对应平台上的功能调用。在这样的开发环境下,我们的应用层开发人员甚至都不用去了解面向平台如linux,windows的开发规范,也不用去学习该平台的 SDK ,就能够完成开发任务。
2.因硬件体系差异而造成的注意点
比如字节顺序(大端法机器还是小端法机器),字节对齐,填充和移位操作等等.
基本类型的大小
C中基本类型的大小(占用的字节数)会随着CPU字长的变化而变化。不要直接用int,要用预先typedef好的定长类型。指针的大小也有上述的问题,也要小心。
如OSType.h中有定义
typedef unsigned char tti_bool; /* NOTE: modified for the embedded systems only */
typedef unsigned char tti_byte;
typedef unsigned char tti_uint8;
typedef char tti_int8;
typedef unsigned short tti_uint16;
typedef short tti_int16;
typedef unsigned long tti_uint32;
typedef signed long tti_int32;
typedef unsigned int tti_uint; /* use this when the size is not important. */
typedef tti_uint16 tti_unichar;
字节序
通俗地打个比方,在一个大尾序的机器上有一个4字节的整数0x01020304,通过网络或者文件传到一台小尾序的机器上就会变成0x04030201;
如果你编写的应用程序中涉及网络通讯,一定要在记得进行主机序和网络序的翻译;如果涉及跨机器传输二进制文件,也要记得进行类似的转换。
内存对齐
简单来说,出于CPU处理上的性能考虑,结构体中的数据不是紧挨着的,而是要空开一些间隔。这样的话,结构体中每个数据的地址正好都是某个字长的整数倍。因此,你的代码也不能依赖对齐的细节。凡是计算结构体大小的地方,都老老实实写上sizeof()。
移位操作
对于有符号整数的右移操作,有些系统默认使用算数右移(最高的符号位不变),有些默认使用逻辑右移(最高的符号位补0)。所以,不要对有符号整数进行右移操作。
3.OS系统调用细节方面区别
比如socket,在windows平台必须调用如下两个函数
int tti_netstart(void)
{
#ifdef _WINDOWS
WSADATA data;
if(WSAStartup(MAKEWORD(2,2),&data)<0)
{
return -1;
}
#endif
return 0;
}
int tti_netstop(void)
{
#ifdef _WINDOWS
if(WSACleanup()<0)
{
return -1;
}
#endif
return 0;
}
4.善用第三方可移植类库
如pthread_win32类库,在windows下模拟了pthread_create等函数,可直接使用。
一张架构图: