LDD3第十一章的学习-内核的数据类型

作者:Aningsk ,本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 未本地化版本许可协议进行许可。


这一章的内容看起来并不是太多,主要讲了在写驱动的时候用到数据类型,并保证在不同CPU架构上可移植的问题。那就快快写吧~我还想看看动画片,Fate、海贼王、火影忍者,嘿嘿。

 

Linux内核的数据类型的使用分为3类:标准C语言类型,有确定大小的类型(如u32),内核特定对象的类型(如pid_t)。

标准C语言类型。我觉得只要保证使用这些类型的变量是与硬件没有太大关系的,并且不同CPU架构对它们的影响也不大,那就可以放心使用标准C语言的类型。一般在32位CPU上,int类型、long类型、指针类型(ptr)都是4字节(32位),long long类型是8字节;而在64位的CPU上,int是4字节的,long类型、ptr类型,以及long long类型都是8字节(64位)的。在书中288页有关于这个的说明。值得记住的就是:在目前的所有架构中,指针类型的大小都是与long类型一致的。在内核中为防止直接对某一个内存地址取值,我们常常使用一个unsigned long类型来保存一个内核中普通的内存地址,从而避免一些代码缺陷。

有确定大小的类型。当我们包含头文件<linux/types.h>后,我们可以使用Linux特有的一些确定大小的类型:u8、u16、u32、u64等。如果我们在内核中要向用户空间传递这些类型的数据,可以在类型名前加上两个下划线,例如__u8。这些都是Linux所特有的,如果需要移植到其他UNIX类系统,就不能使用这些类型,在C99标准中包含了相似的类型,可供使用,例如:uint8_t、uint32_t等等。

内核特定对象的类型。一些内核常用的类型,可能会被另外使用typedef定义出来,例如pid_t表示进程的标识符。使用typedef定义内核最常用的数据的类型,可以保证在内核升级使这个数据类型变化了,其相关的模块并不需要太大的修改,甚至不用任何修改。但有时这些_t类型在不同的平台上也会有一些问题:如果我们用printk打印它们,应该选择什么样的类型。像size_t在一些平台是unsigned long,而一些平台是unsigned int。最有效的办法是,强制转化为最大的数据类型——long或者unsigned long。

 

其他有关移植性的问题:

不仅仅在内核,编写其他程序时也是如此:不要使用显式的常量,而是使用宏来代替他们。这样一下,程序的含义更加容易理解,在修改常亮也更加方便。

时间间隔

不要假定一秒就是100个jiffies,虽然我们经常遇到的常常如此。使用jiffies计算时间时,应该使用HZ来表示1秒。比如,0.5秒应该写为HZ/2,而不是50个jiffies。

页的大小

与上面的1秒类似,使用内存的时候不要假定一页是4KB,内存页的大小应该是PAGE_SIZE个字节。如果用户空间需要知道内存页的大小,可以使用getpagesize库函数。当驱动程序里需要一个指定大小的内存空间,不应该使用get_free_page获得4页内存,因为页大小不同平台时可能不一样的。内核提供了解决方法:get_order。使用方法在书中291页有例子。

字节序

我们也不能轻易地对平台的字节序做任何假设,虽然我们常见的都是小端序(低地址存低字节)。Linux提供了两个宏:u32 cpu_to_le32(u32);  u32 le32_to_cpu(u32);分别将CPU使用的数据转化为小端序数据,将小端序数据转化为CPU使用的数据。

数据对齐

很多平台在试图访问没有对齐的数据时,都当作一个异常来处理,这将带来很大的开销,降低了系统性能。如果要访问没有对齐的数据应该使用:get_unaligned(ptr);和put_unaligned(val, ptr);

对于结构体,为了保证在不同的平台的可移植性,除了字节序之外,还要注意应该强制数据项自然对齐,即在数据项大小的整数倍地址存储数据项。这样编译器可能会在结构体中插入填充数据,来保证这种自然对齐。但是,如果我们正在定义一个和具体设备要求大小结构相匹配的结构体,那么编译器多填充就会破坏我们的意图。我们可以主动告诉编译器,不要填充:__attribute__ ((packed))。具体使用在书中293页。

指针和错误值

有时我们常用返回NULL指针表示函数失败,但这样不能返回具体的错误原因。Linux在<linux/err.h>中定义了一些函数来实现返回具体的错误。在书中的294页。

 

链表

内核中经常需要维护数据结构的列表,Linux里面提供了一套标准的循环、双向链表的实现,可供使用。(定义在<linux/list.h>)值得注意的是,这些链表的函数都没有进行任何锁定,所以,如果驱动中可能存在对链表的并发操作,我们必须自己实现锁。定义一个需要加入链表中的结构体时,我们只需要在这个结构体中加入一个“list_head”。在使用前,我们必须初始化链表头(书中295页)。在书中296页列出了操作链表的一系列函数,我就不再这里重复啦,嘿嘿~

 

Aningsk

2015/07/19

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值