Linux内核数据结构

链表

Linux内核中的链表是一个比较通用的,所以刚开始我看到时候不是特别习惯,和教科书上的都不一样,教科书上的是正向思维,就是有链表,然后怎么用。内核中的是有需求了,我们怎么构造链表,构造的链表怎么通用。这就是工作和学习的不同,最近一直找一些比较贴近实现的书,例如0bug,虽然很多人对其评价不太好,但我知道是很多c语言的书,评价很好,但是现实用的时候又有多少能用上?后面我会专门写一下0bug书上的东西。

<linux/list.h>

struct list_head {

struct list_head *next, *prev;

};

用法是将list_head嵌套到结构体中去,例如

struct foo {

struct list_head list;

void data;

};

问题就来了,如何通过list求得整个结构体?

/**

 * 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) );})

这个宏定义写的很高超,用于求得整个结构体的地址。

有人就会问了,为何需要从嵌套求得嵌套结构体地址呢?原因是,作为通用的链表,那么所有的函数应该是和具体用处无关的,所以例如list_add函数的参数就是一个list_head,也就是保存的是list

#define list_entry(ptr, type, member) /

container_of(ptr, type, member)

struct foo *ifoo;

....

INIT_LIST_HEAD(&ifoo->list);

以前不太清楚规矩,不知道内核链表是怎么用的,所以看很多函数都比较蒙,特别是splice一类的函数,因为用法都有一个链表头。

定义一个链表头,用于指向链表,后面的所有都可以根据这个链表头来索引。

static LIST_HEAD(foo_list);

这里foo_list就可以当作是一个链表头,其实也完全是一个链表节点一样的结构体。

链表的一些操作

1、Add

参数new表示一个新节点,head则是链表头。

static inline void list_add(struct list_head *new, struct list_head *head)

{

__list_add(new, head, head->next);

}

向链表头的prev新增。

static inline void list_add_tail(struct list_head *new, struct list_head *head)

{

__list_add(new, head->prev, head);

}

2、Del

从链表除去

static inline void list_del(struct list_head *entry)

{

__list_del(entry->prev, entry->next);

entry->next = LIST_POISON1;

entry->prev = LIST_POISON2;

}

除去并初始化

static inline void list_del_init(struct list_head *entry)

{

__list_del(entry->prev, entry->next);

INIT_LIST_HEAD(entry);

}

3、Movesplicing 链表节点

从下面函数的实现可以看出,任何设计和实现都要从最基本的开始,上层应用基本函数来实现复杂功能。

static inline void list_move(struct list_head *list, struct list_head *head)

{

__list_del(list->prev, list->next);

list_add(list, head);

}

static inline void list_move_tail(struct list_head *list,

  struct list_head *head)

{

__list_del(list->prev, list->next);

list_add_tail(list, head);

}

判断是否为空函数

static inline int list_empty(const struct list_head *head)

{

return head->next == head;

}

传入的一定是链表头

将两个链表合并,这两个函数第一个参数是一个链表头,head是将要合并的链表头,最后head访问,list则变成空的链表头了。

static inline void list_splice(const struct list_head *list,

struct list_head *head)

{

if (!list_empty(list))

__list_splice(list, head, head->next);

}

static inline void list_splice_init(struct list_head *list,

    struct list_head *head)

{

if (!list_empty(list)) {

__list_splice(list, head, head->next);

INIT_LIST_HEAD(list);

}

}

4、遍历链表

基本便利函数

#define list_for_each(pos, head) /

for (pos = (head)->next; prefetch(pos->next), pos != (head); /

         pos = pos->next)

struct list_head *p;

struct fox *f;

list_for_each(p, &fox_list) {

/* f points to the structure in which the list is embedded */

f = list_entry(p, struct fox, list);

}

将上面合并一下就成了

#define list_for_each_entry(pos, head, member) /

for (pos = list_entry((head)->next, typeof(*pos), member); /

     prefetch(pos->member.next), &pos->member != (head);  /

     pos = list_entry(pos->member.next, typeof(*pos), member))

遍历过程中删除,则需要safe函数。

#define list_for_each_safe(pos, n, head) /

for (pos = (head)->next, n = pos->next; pos != (head); /

pos = n, n = pos->next)

n先保存了next的指针,这样,及时posfree了无所谓了

Linux内核中,还有一个叫做hlist,用于hash表表项,而节点和前面讲到的list一样,目的是减少hash表项占用内存。

RCU,用于读取远多于写操作。

http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/index.html 

下面介绍一下hlist

http://www.cnitblog.com/luofuchong/archive/2008/01/17/38986.html 

 

队列 kfifo

1、创建队列

int kfifo_alloc(struct kfifo *fifo, unsigned int size, gfp_t gfp_mask);

void kfifo_init(struct kfifo *fifo, void *buffer, unsigned int size);

2、入列

unsigned int kfifo_in(struct kfifo *fifo, const void *from, unsigned int len);

3、出列

unsigned int kfifo_out(struct kfifo *fifo, void *to, unsigned int len);

unsigned int kfifo_out_peek(struct kfifo *fifo, void *to, unsigned int len, unsigned offset);

4、重置

static inline void kfifo_reset(struct kfifo *fifo);

5、销毁

void kfifo_free(struct kfifo *fifo);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值