Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1482284
  • 博文数量: 181
  • 博客积分: 3308
  • 博客等级: 中校
  • 技术积分: 2227
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-03 12:03
个人简介

我是zoro

文章分类

全部博文(181)

文章存档

2015年(1)

2013年(35)

2012年(39)

2011年(50)

2010年(56)

分类: LINUX

2010-12-28 22:14:31

   linux内核开发者建立了一套标准的循环、双向链表的实现。如果需要操作链表,推荐使用这一内核设施。为了使用这个链表机制,驱动程序必须包含头文件。对于用户态的普通程序,若想用这个机制,可把内核中的list.h文件中的所需内容拷贝到自己的头文件中。
   在头文件中,定义了该链表机制所需的list_head类型的结构体。
     struct list_head {
     struct list_head *next, *prev;
     };
   用于实际代码的的链表几乎总是由某种结构类型构成,为了在代码中使用linux链表机制,只需在构成链表的机构里嵌入一个list_head。
   链表头通常是一个独立的list_head结构。下图显示了简单的struct list_head是如何用来维护一个数据结构链表的。
   在使用之前,必须用INIT_LIST_HEAD宏来初始化链表头。可如下声明并初始化一个实际的链表头:
struct list_head todo_list;
INIT_LIST_HEAD(&todo_list);
   另外,可以在编译时像下面这样初始化链表:
LIST_HEAD(todo_list);
   为了加深理解及在用户空间使用,下面为上述内容的相关源代码:
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
void INIT_LIST_HEAD(struct list_head *list)
{
 list->next = list;
 list->prev = list;
}
 
头文件中声明的一些常用操作链表函数:
 
1. void list_add(struct list_head *new,struct list_head *prev,struct list_head *next);
    在链表头后面添加新项(通常实在链表的头部)。这样,它可以被用来建立栈。但需要注意的是,head并不一定非得是链表名义上的头;如果传递了一个恰巧位于链表中间某处的list_head结构体,新项会紧跟在它的后面。因为linux链表是循环式的,链表头通常与其他的项没有本质上的区别。所以可以利用这个特点可是使添加新项的位置变得灵活了许多。
源码如下:
void __list_add(struct list_head *new,struct list_head *prev,struct list_head *next)
{
 next->prev = new;
 new->next = next;
 new->prev = prev;
 prev->next = new;
}
void list_add(struct list_head *new, struct list_head *head)
{
 __list_add(new, head, head->next);
}

 
2.void list_del(struct list_head *entry);
删除链表中的指定项。
源码如下:
void __list_del(struct list_head * prev, struct list_head * next)
{
 next->prev = prev;
 prev->next = next;
}
void list_del(struct list_head *entry)
{
 __list_del(entry->prev, entry->next);
 entry->next = LIST_POISON1;
 entry->prev = LIST_POISON2;
}
其中LIST_POISON1,LIST_POISON2在头文件中定义:
#define POISON_POINTER_DELTA 0
/*
 * These are non-NULL pointers that will result in page faults
 * under normal circumstances, used to verify that nobody uses
 * non-initialized list entries.
 */
#define LIST_POISON1  ((void *) 0x00100100 + POISON_POINTER_DELTA)
#define LIST_POISON2  ((void *) 0x00200200 + POISON_POINTER_DELTA)
   由于从链表删除的单元在物理上还存在,为了防止出错,使next及prev指向特定的地址。在实际应用中,对于没用的动态分配产生的链表单元,不要忘了free掉。
 

3.list_entry(struct list_head *ptr, type_of_struct, field_name);
   首先说明一下,list_entry是一个宏,定义在list.h中。因为调用程序通常对被嵌入list_head结构而组成链表的大结构更有兴趣。因此,可以利用list_entry宏将返回指向包含它的大结构的指针。其源码如下所示:
/**
 * list_entry - get the struct for this entry
 * @ptr: the &struct list_head pointer.
 * @type: the type of the struct this is embedded in.
 * @member: the name of the list_struct within the struct.
 */
#define list_entry(ptr, type, member) \
 container_of(ptr, type, member)
很尴尬,在list.h中没有定义container_of这个宏,通过查找其定义在文件中。
/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr: the pointer to the member.
 * @type: the type of the container struct this is embedded in.
 * @member: the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({   \
 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
 (type *)( (char *)__mptr - offsetof(type,member) );})
   在上述头文件的定义中,ptr是指向结构体一成员,type是包含ptr指向成员的结构体,member是str指向成员的名字。
   对于宏offsetof,它定义在中,
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
其作用是计算TYPE类型的结构体中成员MEMBER的偏移。
 
阅读(1877) | 评论(1) | 转发(0) |
0

上一篇:hello world模块

下一篇:linux C 正则表达式

给主人留下些什么吧!~~

chinaunix网友2011-01-03 15:09:20

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com