Linux内核分析及编程之数据类型与列表


1.1    数据类型所占空间

在编译内核时使用-Wall-Wstict-prototypes选项,可以避免很多错误的发生

内核使用的基本数据类型

int    标准C语言整数类型

u32    32位整数类型

pid_t    特定内核对象pid的类型

其中基于sparc64平台的linux用户空间可以运行32代码,用户空间指针是32位宽的,但内核是64位的

内核中的地址是unsigned long类型,指针大小和long类型相同

使用有前缀的类型用于将变量显露给用户空间.如_ _u8类型.例如一个驱动程序通过ioctl函数与运行在用户空间的程序交换数据,应该用_ _u32来声明32位的数据类型

有时内核使用C语言的类型,如unsigned int,这通常用于大小独立于体系结构的数据项.

内核中许多数据类型由typedef声明,这样方便移植.如使用pid_t类型作为进程标志符(pid)的类型,而不是int类型,pid_t屏蔽了在不同平台上的实际数据类型的差异.

如果不容易选择适合的类型,就将起强制转换成最可能的类型(long或unsigned long).

1.2    时间间隔

对于1s的时间间隔,不能用100个jiffy.因为不同的平台可能设置不一,所以应该用Hz(每秒定时器中断的次数)来衡量

页面大小

内存页的大小个PAGE_SIZE字节,而不是4KB.在不同的平台上,页大小范围可以是4KB到64KB.PAGE_SHIFT的作用是通过对地址右移PAGE_SHIT得到一个地址所在页的页

号.对于用户空间,可以使用geipagesize函数来得到页的大小.

使用get_free_pages函数申请16KB空闲空间(2的14次方),先将16KB转换成2的x次方的空闲页数.在x85下定义PAGE_SHIFT为12

int order = (14 - PAGE_SHIFT > 0)?14 - PAGE_SHIFT:0;

buf = get_free_pages(GFP_KERNEL,order);

字节存储顺序

字节存储顺序有两种,低字节优先的方式是在存储多字节数值时,低字节在前面,高字节在后面.高字节优先正好相反.现代的处理器大部分工作在big-endian模式下.Linux内核定

义了一组宏,用于在处理器字节序数据和特殊字节数据之间进行转换.宏在Linux/byteorder/big_endian.h中

u32 _ _cpu_to _le32(u32);//将一个CPU的值的字节序转换策划嗯一个小端字节的无符号值

还有_be64_to_cpu

数据对齐

读取一个存储在非4字节倍数的地址中的4字节值,这就存在数据对其的问题,用下面的宏访问未对齐的数据

#Include<asm/unaligned.h>

get_unaligned(ptr);

put_unaligned(val,ptr);

这些宏是与类型无关,对各项数据项,都有效.

1.3    内核通用链表

在<Linux/list.h>文件中定义了一个list_head类型简单结构

struct list_head{

struct list_head *next,*prev;

};

通用链表的常用用途是将某一个数据结构本身串成链表,或将某些链表与一个数据结构联系起来,这两种情况实质上都是由结构list_head组成链表.

将某一个数据结构本身串成链表

(1)加入list_head结构成员

struct example_struct{

struct list_head list;

int priority;

.......//其他成员

}

用list成员将example_struct结构串成链表,就是list_head"背负"的负载是example_struct结构.

(2)创建list_head结构

使用前必须申请链表头并用INIT_LIST_HEAD宏来初始化链表头.可使用两种方法

方法1:

struct list_head example_list;

INIT_LIST_HEAD(&example_list);

方法2:

LIST_HEAD(example_list):

其中,这两个宏在include/Linux/list.h中定义如下:

#define LIST_HEAD(name) \

struct list_head name = LIST_HEAD_INIT(name)

#define INIT_LIST_HEAD(ptr)do{

(ptr)->next = (ptr); (prt)->prev = (ptr);\

}while(0)

(3)从example_list链表中得到节点对应的example_struct结构指针,其中ptr是example_list链表中的指针,如ptr = example_list->next

struct example_struct *node = list_entury(ptr,struct example_struct,list);

在上面代码行中的宏定义list_entry将一个list_head结构指针映射回一个只想结构example_struct的指针,即得到list_head的宿主结构,宏定义(在include/Linux/list.h)

#define list_entry(ptr,type,member)\

container_of(ptr,type,member)

得到链表中节点的结构:

ptr是链表中的一个struct list_head结构元素指针

type是用户定义的结构类型,其中,包含struct list_head结构成员

member用户定义结构的struct list_head结构成员名字

在include/Linux/kernel.h中有contain_of的定义,参数含义与list_entry中一直,container_of得到list的容器结构,即含有list成员的机构type.contain_of的定义如下:

#define container_of(ptr,type,member)({

//将链表中的元素ptr转换成结构type中成员menber的类型

const typeof(((type *)0)->member)*_mptr = (ptr);

//_mptr减去member成员偏移地址正好是type结构地址

(type *)((char *)_mptr - offsetof(type,member));

})

在include/Linux/stddef.h中有宏offsetof的定义,列出如下

#define offsetof(TYPE,MEMBER)((size_t)&((TYPE *)0)->MEMBER)

其中&((struct example_struct *)0 )->list表示当结构example_struct正好在地址0上时其成员list的地址,即成员位移

  • 1
  • 2
  • 下一页

相关内容