Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1284117
  • 博文数量: 554
  • 博客积分: 10425
  • 博客等级: 上将
  • 技术积分: 7555
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-09 09:49
文章分类

全部博文(554)

文章存档

2012年(1)

2011年(1)

2009年(8)

2008年(544)

分类:

2008-04-08 17:43:09

第 3 章
迁移问题
处理器体系结构问题
小端字节序与大端字节序
源平台和目标平台中字节排序方面的问题可能是问题的根源。在计算机内存中存储信息的方法以及其中的
字节排序方式称为字节存储顺序 (endianness)。如果任何多字节数据字段的最高有效字节 (Most Significant
Byte, MSB) 存储在内存地址的最低位,则该系统称为大端字节序 (big-endian) 系统。如果任何多字节数据字
段的最低有效字节 (Least Significant Byte, LSB) 存储在内存地址的最低位,则该系统称为小端字节序 (littleendian)
系统。Intel 处理器和 AMD Opteron™ 处理器采用小端字节序,意味着最低有效字节存储在地址的最
低位。相反, SPARC 处理器采用大端字节序。以下面的示例为例:
在 Intel 或 AMD Opteron 处理器中以小端字节序格式存储 0X01020304,假设采用 4 字节存储,如下所示:
在 SPARC 处理器中以大端字节序格式存储同一个数字 0X01020304,假设采用 4 字节存储,如下所示:
Solaris OS 在大端字节序模式(SPARC 处理器)和小端字节序模式(Intel 处理器或 AMD Opteron 处理器上
的 x86/x64 版本)下均可以使用。Solaris OS 的一项优势在于它可以透明地处理字节存储顺序问题,使 ISV
或开发者不必过于关注其应用程序何时可以在针对 UltraSPARC® 处理器的 Solaris OS 平台或针对 x86/x64
(对于 Intel 或 AMD Opteron 处理器)的 Solaris OS 平台上使用。Sun 的软件体系结构允许重新编译针对
SPARC 的 Solaris OS 平台上的大端字节序应用程序,以便在针对 x86/x64 的小端字节序 Solaris OS 平台上运
行。通常,不需要进行重新工程 (re-engineering)。但是,字节存储顺序的不同可能会导致出现问题,特别是
当应用程序使用低级代码来处理单词内的字节时,就更容易造成问题。
通常数值数据内的字节排序对于应用程序也是透明的,但下列情况除外:
• 通过网络发送数据: 标准 C 库提供下列例程:ntohl()、ntohs()、htonl() 和 htons(),以便在
网络字节顺序和主机字节顺序之间转换数值数据。这些例程在主机平台上实现,用于处理主机字节顺序
与网络字节顺序之间的转换,应始终使用这些例程将应用程序与底层网络字节排序隔离。
• 使用指针访问数值数据的各个字节: 如果使用数值数据类型的各个字节存储和检索值,由于底层平台的
字节存储顺序属性的不同,可能会得到不同的结果。在这种情况下,代码不可移植。例如:
 
 
Intel 处理器或 AMD Opteron 处理器中的值 0X01020304 (小端字节序格式)
在此示例中,小端字节序计算机将输出 4,大端字节序计算机将输出 1。若要使此代码可移植,应使用各个
字符型变量存储这些值,而不是使用数值数据类型存储这些值。此外,可以对每个值使用包含各个字段的
结构,并使用结构字段名获取和设置这些值。
应用程序数据存储
通常,所开发的文件系统不采用任何字节存储顺序,因此在 SPARC 版本与 x86/x64 版本的 Solaris OS 之间
交换文件并不存在什么问题。但是,存储可在平台之间共享的原始数据的应用程序可能会出现问题。例如,
如果针对 SPARC 的 Solaris OS 平台上的应用程序以原始格式将数据结构写入文件,这些文件中存储的数据
将采用特定字节存储顺序。从基于 Intel 处理器或 AMD 处理器的 Solaris 系统中读取或写入这些数据文件
时,可能会出现与数据字节存储顺序有关的问题。简而言之,文件中存储的二进制数据(原始数据)通常
无法在 SPARC 平台与 x86/x64 平台之间传输。
对于存储可在平台之间共享的数据的应用程序,可通过下列两种方式之一处理字节存储顺序问题:
• 使用文本文件和字符串,以应用程序定义的、不采用任何字节存储顺序的格式存储数据。
• 选择大端字节序或小端字节序约定,并在必要时使用外部数据表示 (External Data Representation, XDR) 库
之类的技术执行字节交换。
需要跨平台兼容性很容易理解,所以,多年以来,主流的应用程序一直可以在大端字节序 Solaris 环境和小
端字节序 Solaris 环境中使用,不会出现任何问题。范围涵盖了从个人办公软件应用程序到包括 Oracle、
Informix 和 Sybase 在内的供应商提供的主要数据库管理系统。
 
存储顺序和对齐
不同平台的数据存储顺序有所不同,因此,需要使用特定存储顺序的代码不可移植。以下面的示例为例:
此示例中的代码假定 year 将存储在 month 之前。此代码在 x86 平台上可以正常运行,但是在基于 SPARC
处理器的计算机上不能正常运行。为了避免出现此类潜在的错误情况,可移植代码应分别比较结构变量的
各个字段,而不应依靠存储顺序。例如:
在不同平台上,结构中的字段成员的对齐方式有所不同,因此,填充要求会不同,结构的大小也会不同。
为了使代码可移植,应始终使用 sizeof 运算符访问结构大小。
struct date
{
char yr;
char mon;
char day;
char x; // struct var size is 4
} planned_date, end_date;
if ( (*(long * )&end_date) > (*(long *)&planned_date))
{
printf("Sorry, You missed the deadline for migrating to Solaris \n");
}
if (compare_dates(end_date, planned_date) > 0)
{
printf("Sorry, You missed the deadline for migrating to Solaris \n");
}
int compare_dates(struct date date_to_compare, struct date
date_compare_against)
{
if (date_to_compare.year != date_compare_against.year)
return (date_to_compare.year - date_compare_against.year);
if (date_to_compare.month != date_compare_against.month)
return (date_to_compare.month - date_compare_against.month);
if (date_to_compare.day != date_compare_against.day)
return (date_to_compare.day - date_compare_against.day);
}
 
读/ 写结构
大多数程序使用标准的 C 例程,在永久性存储介质上以完整的二进制结构读取和写入数据。这些例程需要
知道所读取或写入的数据大小。由于不同平台上的对齐方式和数据类型大小不同,结构大小也会有所不同,
因此,应使用 sizeof 运算符指定在这些例程中要读取或写入的字节数。
例如,如果程序需要读取一条 MyRecord 类型的记录(您知道数据共有 25 个字节),则不应编写以下代码:
而应使用以下约定:
32 位和 64 位问题
Solaris OS 和 Linux 在 32 位处理器和 64 位处理器上均可以运行。UltraSPARC 处理器是 64 位处理器。Intel
和 AMD 提供的 x86 兼容处理器是 32 位处理器,缺省地址空间为 4 GB。Intel EM64T 和 AMD Opteron 处理
器系列是 64 位处理器,支持 64 位的地址空间。但是,这些处理器也可以在 32 位兼容模式下运行,并可运
行任何传统的 32 位兼容应用程序。Solaris 10 OS 配备完整,可以处理其运行平台上固有的 32 位和 64 位问
题。只有编写应用程序不当,对不同数据类型的大小以及不同数据类型之间的转换进行了假定,才会出现
该问题。
C 语言规范不为其整型数据类型指定任何大小,只表明其关系,如下所示:
各种数据类型的大小由操作环境所使用的数据类型模型确定。32 位 OS 使用 ILP32 数据模型,该数据模型
指定 int、long 和 pointer 数据类型的大小是 32 位(因此称为 ILP32)。
64 位 OS 采用的是行业标准的 LP64 数据类型模型。LP64 数据类型模型指定 long 和 pointer 数据类型
的大小是 64 位,并未修改其他数据类型的大小。32 位 OS 和 64 位 OS 中不同数据类型的大小关系如表2
中所示。
 
 
多线程编程
大多数现代操作系统都使用多线程的方式来提高计算的粒度,以便更有效地利用可伸缩环境中的资源。
内核模式和用户模式的服务均依靠通过操作系统实现的底层线程模型。为保持不同数据结构的完整性并使
系统上运行的所有线程和进程实现可控的并发性,应使用互斥锁和信号量之类的实体。但是,具体的实现
细节可能会截然不同,即使在类似于 UNIX 的操作系统之间也是如此;这些细节是移植项目中存在的潜在
问题。
Solaris OS 内核本身支持线程。内核在内部调度线程,从而以最佳的方式共享可用的 CPU 资源。这些内核
线程绑定到轻量级进程 (Lightweight Process, LWP)。
多年来,Solaris OS 版本已采用多种线程实现方式。Solaris 线程一直处于非常重要的地位。但是,随 Solaris
OS 提供的 POSIX 线程库已成为实际的标准,因为通过该线程库可以很容易地在 UNIX 平台(包括 Linux)
之间移植应用程序。有关 Solaris 线程的更详细的说明,请参阅由 Jim Mauro 和 Richard McDougall 合著的
《Solaris Internals》,该书由 Sun Microsystems Press 出版。使用线程进行编程需要具备更高的技能。由 Steve
Kleiman、Devang Shah 和 Bart Smaalders 合著的《Programming With Threads》是有关该主题的一本很不错
的参考书。
在 Linux 端,有多种线程软件包实现方式,多年来,这些软件包已得到明显的改进。总的来说,这些实现
方式与 POSIX 兼容。但是,实现方式的不同会造成性能问题。基于内核版本 2.2 和 2.4 的 Linux 分发支持
Linux 线程。为了便于使用 Solaris 线程移植 Solaris OS 应用程序,可以使用另一个称为 Solaris 兼容线程库
(Solaris Compatible Thread Library, SCTL) 的开放源代码软件包。Linux 2.6 内核捆绑了一个新的线程模型,称
为本地 POSIX 线程库 (Native POSIX Thread Library, NPTL),效率可以提高很多。
尽管实现方式不同,但大多数调用线程库的语法非常类似,均符合 POSIX 线程标准。因此,从移植的角度
来说, Linux 端的线程库调用可移植到 Solaris 10 OS。当然,用户必须清楚线程库在 makefile 的链接程序命
令中的顺序和名称。通常, -lpthreads 是用于访问 Solaris 平台上的 POSIX 线程的链接程序选项。
如果由于任何特定原因,用户希望使用 Solaris OS 上的 Solaris 线程,则需要花费更多的精力来更改代码,
并将 POSIX 线程调用替换为 Solaris 线程调用。同时,用户还需要分析代码的语义准确性。表3 列出了
Solaris 线程和 POSIX 线程之间的对应关系。
表3. Linux 线程函数与 Solaris OS 线程函数之间的对应关系
编号 POSIX 线程函数Solaris 线程函数
1 pthread_create() thr_create()
2 pthread_exit() thr_exit()
3 pthread_getschedpa ram() thr_getprio()
4 pthread_getspecifi c() thr_getspecific()
5 pthread_join() thr_join()
6 pthread_key_create () thr_keycreate()
7 pthread_kill() thr_kill()
8 pthread_self() thr_self()
9 pthread_setschedpa ram() thr_setprio()
10 pthread_setspecifi c() thr_setspecific()
 
 
Linux 不支持可在不同进程之间共享的可共享同步对象(互斥锁、信号量和条件变量)。因此,调用下列函
数时将 PTHREAD_PROCESS_SHARED 用作第二个参数将失败:
• pthread_mutexattr_setpshared()
• pthread_condattr_setpshared()
• pthread_rwlockattr_setpshared()
信号量
Solaris OS 和 Linux 均为信号量提供了 POSIX 接口。Solaris 实现与 POSIX 实现兼容。在 Solaris 端,可以使
用以下两个函数集中的任何一个(表4)。
表4. POSIX 信号量函数与 Solaris OS 信号量函数之间的对应关系
编号POSIX 信号量函数Solaris 信号量函数
1 sem_destroy() sema_destroy()
2 sem_init() sema_init()
3 sem_post() sema_post()
4 sem_trywait() sema_trywait()
5 sem_wait() sema_wait()
 
 
以上文章转自于 : http://developers.sun.com.cn/
 
阅读(560) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~