一、内核对象数据类型
1.1 C语言类型(int)
char、short、int、long long在不同的平台上大小不变。
long、ptr(指针)平台不同其大小不同,但二者的大小始终相同。
char的符号问题:
大多数平台上char默认是signed,但有些平台上默认是 unsigned。
char i = -1; 大部分平台上i是-1,有些平台上是255。
应该使用:signed char i = -1; unsigned char i = 255;
1.2 确定大小的类型(u32)
u8、u16、u32、u64、 s8、s16、s32、s64是linux内核确定大小的类型。
__u8等式linux用户态确定大小的类型。(头文件linux/types.h)
uint8_t、uint32_t是新编译器支持的C99标准确定大小的类型,可以跨平台。
1.3 特定内核对象的类型(pid_t)
进程标识符使用pid_t类型,而不使用int,屏蔽了实际的数据类型中任何可能的差异。
特定内核对象的类型,打印时,不太好选择printk或printf的输出格式:
1. 一些平台上排除的警告,在另一平台上可能会出现(size_t在一些平台上是unsigned long,在一些平台上是unsigned int)。
2. 将其强制转换成可能的最大类型,然后用响应的格式打印输出。
二、字节序
2.1 大端、小端
数值0x01020304,内存从低到高依次存储:04 03 02 01 为小端。 存储顺序反过来为大端)
数值0x00000001,内存从低到高依次存储:01 00 00 00 为小端。
2.2 转换函数
u32 __cpu_to_be32(u32); /* 把cpu字节序转为大端字节序 */
u32 __be32_to_cpu(u32); /* 把大端字节序转为cpu字节序 */
u32 __cpu_to_le32(u32); /* 把cpu字节序转为小端字节序 */
u32 __le32_to_cpu(u32); /* 把小端字节序转为cpu字节序 */
在头文件
2.3 检测本机大端小端的代码
#include
#include
/*int is_little_endian()
{
const static union
{
unsigned int i;
unsigned char c[4];
}u = { 0x00000001 };
return u.c[0];
}*/
int is_little_endian()
{
int i = 1;
return *(char*)&i;
}
int main(int argc, char*argv[])
{
if (is_little_endian())
printf("little endian.\n");
else
printf("big endian.\n");
return 0;
}
三、数据对齐
3.1 数据对齐
对齐:如果一个变量的内存地址正好是它长度的整数倍,它就被称作是自然对齐的。
3.2 数据未对齐印发的问题
一些平台不能访问未对其的数据,一些平台可以访问未对齐的数据,但是效率很低。
用指针进行类型转换引发的未对齐:
char dog[10]; //是对齐的
char *p = &dog[1];
long l = *(long*)p; //l是未对齐的
3.3 尽量保证数据对齐
为了实现跨平台,应尽量让数据自然对齐。
编译器会悄悄的插入数据保证struct的自然对齐:
1. 下面的代码,没有__attribute__((packed)),长度为16;
2. 下面的代码,有__attribute__((packed)),长度为10;
3. __attribute__((packed))不让编译器对struct进行填充,以保证其自然对齐(有时候我们需要这样的数据)。
#include
#include
#include
int main(int argc, char*argv[])
{
struct
{
__u16 i;
__u64 j;
}__attribute__((packed)) u;
printf("struct u len:%d\n", sizeof(u));
return 0;
}
3.4 访问未对齐的数据
应该使用下面的宏:
#include
get_unaligned(ptr)
put_unaligned(var, ptr)
四、其他有关可移植性的问题
避免使用显示的常量值
4.1 时间间隔
使用HZ代表一秒。
不能假定每秒就1000个jiffies。
与msec毫秒对应的jiffies数目总是msec*HZ/1000。
4.2 页大小
页大小为PAGE_SIZE个字节,而不是4KB。
分配16KB的空间临时存储数据,如下:
#include
int order = get_order(16*1024);
buf = get_free_pages(GFP_KERNEL, order);
4.3 指针和错误值
许多内核函数通过把错误值编码到一个指针值中来返回错误信息,这种函数必须小心使用,它的返回值不能简单地和NULL比较。
见
#include
static struct task_struct *kapmd_task;
kapmd_task = kthread_create(apm, NULL, "kapmd");
if (IS_ERR(kapmd_task)) {
printk(KERN_ERR "apm: disabled - Unable to start kernel "
"thread.\n");
err = PTR_ERR(kapmd_task);
kapmd_task = NULL;
remove_proc_entry("apm", NULL);
return err;
}
4.4 其他
处理器排序(rmb()、wmb())、SMP、内核抢占、高端内存