LDD读书笔记第十一章-内核的数据类型

Linux数据类型

Linux内核C语言数据类型大小
  Arch     char     short     int     long     ptr     long-long     u8     u16    u32    u64 
i386 1 2 4 4 4 8 1 2 4 8
alpha 1 2 4 8 8 8 1 2 4 8
armv41 1 2 4 4 4 8 1 2 4 8
ia64 1 2 4 8 8 8 1 2 4 8
m86k 1 2 4 4 4 8 1 2 4 8
mips 1 2 4 4 4 8 1 2 4 8
ppc 1 2 4 4 4 8 1 2 4 8
sparc 1 2 4 4 4 8 1 2 4 8
sparc64 1 2 4 4 4 8 1 2 4 8
x86_64 1 2 4 8 8 8 1 2 4 8

需要注意的是sparc64架构运行的是32位用户空间。但是在内核空间是64位的。
在当前的所有平台上指针和long型的大小总相同
u8等固定大小的值有其有符号版本以s代替u如:s8
如果需要在用户空间使用固定大小的话使用双下划线版本如__u8
固定大小的类型是linux特有的 如果考虑向其他unix变种移植应使用C99标准类型变量如:uint8_t

其他有关移植性的问题

时间变量

使用时间变量的时候不要使用特定的值而是使用HZ宏如:
n毫秒为:n*HZ/1000

页大小

同样不要使用特定的值。而是使用如下函数

1
2
3
#include <asm/page.h>
int order = get_order(16*1024); /*传递的参数必须是2的幂*/
buf = get_free_pages(GFP_KERNEL,order);

字节序

不要对字节序做任何假设。
头文件asm/byteorder.h根据平台定义了__BIG_ENDIAN 或 __LITTLE_ENDIAN。
也可以使用如下函数转换成需要的类型

1
2
u32 cpu_to_le32 (u32);
u32 le32_to_cpu (u32);

这些宏相对le有be版本如be32_to_cpu,并且有相应的16为版本。
更多资料查看:
linux/byteorder/big_endian.h
linux/byteorder/little_endian.h

数据对齐

访问未对其的数据

1
2
3
#include <asm/unaligned.h>
get_unaligned(ptr);
put_unaligned(val, ptr);

对结构添加__attribute__可防止编译器自动对齐如

1
2
3
4
5
6
struct {
	u16 id;
	u64 lun;
	u16 reserved1;
	u32 reserved2;
} __attribute__ ((packed)) scsi;

指针和错误值

1
2
3
4
#include <linux/err.h>
void *ERR_PTR(long error);/*错误码到指针类型的错误指针*/
long IS_ERR(const void *ptr);/*判断是否为错误指针*/
long PTR_ERR(const void *ptr);/*错误指针到错误码*/

链表


如果重入的时候可能并发访问必须给链表添加锁机制。
定义在linux/list.h中:

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
31
	struct list_head {
	struct list_head *next, *prev;
};
使用链表:
<pre lang="C">
#include <linux/list.h>
struct todo_struct {
	struct list_head list;
	int priority; /* driver specific */
	/* 其他字段 */
};
/*运行时初始化链表头*/
struct list_head todo_list;
INIT_LIST_HEAD(&todo_list);
/*编译时初始化*/
LIST_HEAD(todo_list);
/*添加删除相关函数*/
list_add(struct list_head *new, struct list_head *head);
/*添加到头部*/
list_add_tail(struct list_head *new, struct list_head *head);
/*添加到尾部*/
list_del(struct list_head *entry);
list_del_init(struct list_head *entry);
/*删除链表项,如果可能插入到其他链表,使用init版本*/
list_move(struct list_head *entry, struct list_head *head);
list_move_tail(struct list_head *entry, struct list_head *head);
/*把给定的链表移动到头部或尾部*/
list_empty(struct list_head *head);
/*如果链表为空返回非零值*/
list_splice(struct list_head *list, struct list_head *head);
/*head之后插入list来合并链表*/

获取包含链表的结构的指针

1
2
3
4
list_entry(struct list_head *ptr, type_of_struct, 				field_name);
/*ptr为链表结构,type_of_struct为包含链表的结构的名称
*field_name为结构中链表字段的名称
*/

遍历链表

1
2
3
4
5
6
7
8
9
10
list_for_each(struct list_head *cursor, struct list_head 			*list)
/*遍历链表*/
list_for_each_prev(struct list_head *cursor, struct 					list_head *list)
/*向前遍历链表*/
list_for_each_safe(struct list_head *cursor, struct 					list_head *next, struct
				list_head *list)
/*如果在遍历中需要删除链表使用此方法*/
list_for_each_entry(type *cursor, struct list_head *list, 				member)
list_for_each_entry_safe(type *cursor, type *next, struct 			list_head *list,member)
/*遍历并且直接获取包含链表的结构体的指针*/

两个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
void todo_add_entry(struct todo_struct *new)
{
	struct list_head *ptr;
	struct todo_struct *entry;
	for (ptr = todo_list.next; ptr != &todo_list; ptr = ptr->next) {
		entry = list_entry(ptr, struct todo_struct, list);
		if (entry->priority < new->priority) {
			list_add_tail(&new->list, ptr);
			return;
		}
	}
	list_add_tail(&new->list, &todo_struct)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
void todo_add_entry(struct todo_struct *new)
{
	struct list_head *ptr;
	struct todo_struct *entry;
	list_for_each(ptr, &todo_list) {
		entry = list_entry(ptr, struct todo_struct, list);
		if (entry->priority < new->priority) {
			list_add_tail(&new->list, ptr);
			return;
		}
	}
	ist_add_tail(&new->list, &todo_struct)
}
----------------- 全文结束 -----------------
© 该日志作者:Senghoo, 于2010年07月29日发表于分类"Linux Kernel"下;
© 除非另有注明本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。
© 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
© 转载请注明: LDD读书笔记第十一章-内核的数据类型 | 转自: Senghoo's Blog
© Tag: , , ,

你可以继续围观

  • 最近在读的书-Linux内核相关
    在学习linux内核相关的东西。 购得两本linux内核相关的经典书籍。 Linux设备驱动程序(第三版) [caption id="attachment_10" align="alignnone" width="150" caption=" Linux Device Drivers,3r...
  • 《深入理解linux内核》边读边说-内存寻址(一)
    内存地址 内存地址有三种类型:逻辑地址,线性地址,物理地址。 逻辑地址:常见程序所表示的地址就是逻辑地址。比如MS-DOS程序设计时使用DS:EAX,CS:IP等地址都是逻辑地址。此类地址把地址分为段偏移的模式来把程序分为若干段。在逻辑地址层面上可以把内存看为若干个段来分开的空间。 线性地址:...

发表评论