首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

将 Linux on x86 应用程序移植到 Linux on Power 的指南(2)

将 Linux on x86 应用程序移植到 Linux on Power 的指南(2)

处理字节顺序本节将介绍如何识别代码中依赖于字节顺序的区域,并将它们转换为正确的字节顺序格式的方法。
依赖于字节顺序的代码
数据引用上的非一致性是 C 语言的优势,这使得它在编程系统级软件(包括操作系统和设备驱动程序)中变得很流行。此优势包括类型转换、指针操作、union、位字段、结构和灵活的类型检查。但是,同样是这些特性,它们也可能是字节顺序可移植性问题的来源。作为示例,请考虑以下两个代码清单:
清单 1. 使用指针的非一致数据引用
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
int main(void) {
  int val;
  unsigned char *ptr;

  ptr = (char*) &val;
  val = 0x89ABCDEF; /* four bytes constant */
  printf("%X.%X.%X.%X\n", ptr[0], ptr[1], ptr[2], ptr[3]);
  exit(0);
}




清单 2. 使用 union 的非一致数据引用
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
union {
  int val;
  unsigned char c[sizeof(int)];
} u;

int main(void) {
  u.val = 0x89ABCDEF; /* four bytes constant */
  printf("%X.%X.%X.%X\n", u.c[0], u.c[1], u.c[2], u.c[3]);
  exit(0);
}




在 x86 系统上,结果为:
1
EF.CD.AB.89




在基于 POWER 处理器的系统上,结果为:
1
89.AB.CD.EF




字节顺序问题表现为 val,它从最重要的字节开始逐个字节地读取数据。
如何确定系统的字节顺序
在 Linux 中,GNU_C 预处理器通常会自动提供一组常用的预定义宏,它们的名称以双下划线开始和结束。一组对确定使用的系统的字节顺序最有用的宏是 __BYTE_ORDER__、__ORDER_LITTLE_ENDIAN__ 和 __ORDER_BIG_ENDIAN__。
1
2
3
/* Test for a big-endian machine */
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
/* Do big endian processing */




编写字节顺序中立的代码
如果程序模块在跨具有不同字节顺序的平台移植时能够保留其功能,那么它就是字节顺序中立的。换句话说,它的功能与运行它的平台的字节顺序无关。以下是一些编写字节顺序中立的代码的建议:
  • 使用宏和指令
    要让代码变得可移植,可以使用宏和条件编译指令,如清单 3 和清单 4 所示。
清单 3. 使用指令让字节顺序效果中立化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

#define BIG_ENDIAN 1
#define LITTLE_ENDIAN 0
#define BYTE_ORDER (( htonl(1)==1) )   //  returns 1 or 0 depending on platform


union {
  int val;
  unsigned char c[sizeof(int)];
}u;

int main(void) {
  u.val = 0x89ABCDEF;
  #if (BYTE_ORDER == BIG_ENDIAN)
  printf("%X.%X.%X.%X\n", u.c[0], u.c[1], u.c[2], u.c[3]);
  #else /*!BYTE_ORDER == BIG_ENDIAN*/
  printf("%X.%X.%X.%X\n", u.c[3], u.c[2], u.c[1], u.c[0]);
  #endif /*BYTE_ORDER == BIG_ENDIAN*/
  exit(0);
}




清单 4. 使用宏交换字节(对在运行时确定字节顺序很有用)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//  Useful Endian Neutral Macros

#include <endian.h>        

#if __BYTE_ORDER == __BIG_ENDIAN
// No translation needed for big endian system
#define sw2Bytes(val) val
#define sw4Bytes(val) val
#define sw8Bytes(val) val
#else
//  Little Endian:Translate
// Swap 2 byte, 16 bit values:

#define sw2Bytes(val) \
( (((val) >> 8) & 0x00FF) | (((val) << 8) & 0xFF00) )

// Swap 4 byte, 32 bit values:

#define sw4Bytes(val) \
( (((val) >> 24) & 0x000000FF) | (((val) >>  8) & 0x0000FF00) | \
   (((val) <<  8) & 0x00FF0000) | (((val) << 24) & 0xFF000000) )

// Swap 8 byte, 64 bit values:

#define sw8Bytes(val) \
( (((val) >> 56) & 0x00000000000000FF) | (((val) >> 40) & 0x000000000000FF00) | \
   (((val) >> 24) & 0x0000000000FF0000) | (((val) >>  8) & 0x00000000FF000000) | \
   (((val) <<  8) & 0x000000FF00000000) | (((val) << 24) & 0x0000FF0000000000) | \
   (((val) << 40) & 0x00FF000000000000) | (((val) << 56) & 0xFF00000000000000) )
#endif




1
2
3
4
5
int main(void) {
  int a=0x11121314;
  int b;
  b = sw4Bytes(a);      // b is 0x12 in LE and BE
}




  • 使用编译时选项
    实现此目的的另一种方式是在编译器命令行上将 BYTE_ORDER 的值定义为 -DBYTE_ORDER=BIG_ENDIAN。在一个具有不同字节顺序的新平台上编译时,这会消除编辑设备驱动程序或应用程序中的每个文件的需求。只需编译用于构建该驱动程序或应用程序的 makefile。
字节顺序:特定于 Linux
事实证明,Linux 内核提供了一组特定的系统宏,它们可以从低位优先到高位优先和从高位优先到低位优先执行 16、32 和 64 位交换。这些宏对从 Linux x86 移植到 Linux on Power 以及让您大代码对字节顺序中立都很方便,无需担忧会在代码中编写大量 #ifdef __LITTLE_ENDIAN 条件指令。
例如:
cpu_to_le16(u16);           //  converts CPU endianness 4 bytes to little-endian
le16_to_cpu(u16);           //  converts little-endian 4 bytes to CPU endianness
这两个宏将一个值从 CPU 使用的字节顺序转换为一种无符号、低位优先、32 位数字,或者执行反向转换。例如,在 Linux on Power 系统上,le16_to_cpu(u16) 将低位优先转换为高位优先,而 cpu_to_le16(u16) 将高位优先转换为低位优先。根据使用的宏和运行的是哪个系统,如果没有工作要做,那么它们将会返回原始值。
这些宏和其他许多宏位于:/usr/include/linux/byteorder/big_endian.h> 和 /usr/include/linux/byteorder/little_endian.h 中。
您可以查看这些头文件来找到您需要的宏。您还可以根据名称中的模式推断出它们的用途。
您可以使用预定义的 GNU_C 预处理器指令,轻松地包含正确的 include 文件,如下所示:
1
2
3
4
5
6
7
8
9
#include <endian.h>

#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
   #include <linux/byteorder/big_endian.h>
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
   #include <linux/byteorder/little_endian.h>
#else
   //  …user defined endian header
#endif

返回列表