现在的位置: 首页 > 综合 > 正文

Linux内核数据类型及跨平台

2018年10月02日 ⁄ 综合 ⁄ 共 2620字 ⁄ 字号 评论关闭

一、内核对象数据类型

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字节序 */

        在头文件<linux/byteorder.h>中

2.3 检测本机大端小端的代码

#include <stdio.h>
#include <stdlib.h>

/*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 <stdio.h>
#include <stdlib.h>
#include <linux/types.h>

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 <asm/unaligned.h>

get_unaligned(ptr)
put_unaligned(var, ptr)

四、其他有关可移植性的问题

避免使用显示的常量值

4.1 时间间隔

        使用HZ代表一秒。

        不能假定每秒就1000个jiffies。

        与msec毫秒对应的jiffies数目总是msec*HZ/1000。

4.2 页大小

        页大小为PAGE_SIZE个字节,而不是4KB。

        分配16KB的空间临时存储数据,如下:

#include <asm/page.h>
int order = get_order(16*1024);
buf = get_free_pages(GFP_KERNEL, order);

4.3 指针和错误值

        许多内核函数通过把错误值编码到一个指针值中来返回错误信息,这种函数必须小心使用,它的返回值不能简单地和NULL比较。

        见<linux/err.h>

#include <linux/err.h>

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、内核抢占、高端内存

抱歉!评论已关闭.