1 链表
http://blog.chinaunix.net/uid-26497520-id-3601869.html
1.1 概述
就像许多其他程序一样,操作系统内核经常需要维护数据结构的列表。有时,linux内核中同时存在多个链表的实现代码。为了减少重复代码的数量,内核开发者已经建立了 一套标准的双向循环链表。如果你需要操作链表,那么建议你使用这一内核设施。内核链表的好主要体现为两点,一是可扩展性,内核一直都是在发展中的,所以代码都不能写成死代码,要方便修改和追加;二是封装,将链表常见的操作都进行封装,使用者只关注接口,不需关注实现。当使用这些链表接口时,应该始终牢记这些链表函数不进行任何锁定。如果你的程序有可能试图对同一个链表执行并发操作的话,则有责任实现一个锁方案。1.2 头文件定义
源码版本:2.6.34.14版本内核源码链表定义: include/linux/list.h
1.3 数据结构
1.3.1 传统的list
linux中的链表巧妙的解决了这个问题,linux的链表不是将用户数据保存在链表节点中,而是将链表节点保存在用户数据中。
1.3.2 内核的list
1.3.3 list_head结构
内核链表结构定义如下:
struct list_head {
struct list_head *next, *prev;
};
为了在代码中使用内核链表设施,只需在构成链表的结构里面嵌入一个list_head。如:
struct todo_struct {
... other members ...
struct list_head list;
}
注意list_head的成员prev,next指向的是list_head而不是包含list_head的结构体,我们的链表是通过list_head链起来的。
/*
* ptr:是指向type中链表节点的指针。
* type:一般是个结构体,也就是包含用户数据和链表节点的结构体。
* member:则是type中定义链表节点是用的名字。
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
注:container_of定义于include/linux/kernel.h中
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
注:offsetof定义于include/linux/stddef.h中
1.3.4 list_entry宏
第一次接触内核链表的同学可能会产生这样的疑问:得到list_head的地址有什么用,我们需要操作的是包含list_head的结构体,如何才能得到包含list_head的结构体的指针?这是list_entry将要完成的工作,相关源码如下:
举个例子来说明,我们获得了一个指向list_head的指针p,该list_head包含于todo_struct结构中并且其对应的成员名为list,那么list_entry(p, struct todo_struct, list)将返回该todo_struct的地址。从上面源码可以看出,offsetof计算成员在结构中的偏移,todo_struct的地址的计算方式就是用p的值减去list在todo_struct中的偏移。
下面分析一下container_of宏:
// 步骤1:将数字0强制转型为type*,然后取得其中的member元素
((type *)0)->member // 相当于((struct student *)0)->list
// 步骤2:定义一个临时变量__mptr,并将其也指向ptr所指向的链表节点
const typeof(((type *)0)->member)*__mptr = (ptr);
// 步骤3:计算member字段距离type中第一个字段的距离,也就是type地址和member地址之间的差
// offset(type, member)也是一个宏,定义如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
// 步骤4:将__mptr的地址 - type地址和member地址之间的差
// 其实也就是获取type的地址
1.3.5 list_for_each遍历宏
#define list_for_each(pos, head) \
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
pos = pos->next)
1.3.6 list_head使用示例图
在使用链表时我们通常会定义一个链表头,链表头通常是一个独立的list_head结构,在使用链表前需初始化链表头。从上图可以看出,我们的链表几乎可以说是独立于包含它的结构的,任何保含list_head成员的结构都可以添加到链表中来(当然我们只会将相同的结构组合成一个链表),针对整个链表的插入,删除、和并等操作完全是在list_head上进行的,而链表的遍历也是在list_head上移动。由此,内核有一套通用的专门用于处理链表的函数和宏,我们不必针对每一个链表都写一套操作函数。
1.4 函数操作
下面将列出对链表进行操作的函数和宏(head指向为链表头):1.4.1 初始化
LIST_HEAD_INIT(name) 在编译阶段初始化链表头
#define LIST_HEAD_INIT(name) { &(name), &(name) }
LIST_HEAD(name) 在编译阶段定义并初始化链表头
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
INIT_LIST_HEAD(struct list_head *head) 在运行阶段初始化链表头
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
1.4.2 增加、删除、替换、移动元素
list_add(struct list_head *new, struct list_head *head)
list_add_tail(struct list_head *new, struct list_head *head)
将new插入到链表开始/末尾
list_del(struct list_head *entry)
list_del_init(struct list_head *entry)
从链表中删除entry
list_replace(struct list_head *old, struct list_head *new)
list_replace_init(struct list_head *old, struct list_head *new)
在链表中用new替换old
list_move(struct list_head *list, struct list_head *head)
list_move_tail(struct list_head *list, struct list_head *head)
将list从其所在链表删除,再插入到head链表的开始/末尾
list_rotate_left(struct list_head *head)
将链表第一元素移动到最后
1.4.3 链表判定
list_is_last(const struct list_head *list, const struct list_head *head)
判定list为是否链表最后一个元素
list_empty(const struct list_head *head)
list_empty_careful(const struct list_head *head)
判定链表是否为空
list_is_singular(const struct list_head *head)
判定链表是否只包含一个元素
1.4.4 合并、分割链表
list_cut_position(struct list_head *head1, struct list_head *head2, struct list_head *entry)
将head链表一分为二
list_splice(const struct list_head *head1, struct list_head *head2)
list_splice_init(struct list_head *head1, struct list_head *head2)
合并两个链表(这是为栈设计的)
list_splice_tail(struct list_head *head1, struct list_head *head2)
list_splice_tail_init(struct list_head *list, struct list_head *head)
合并两个链表(这是为队列设计的)
1.4.5 访问包含list_head的结构体
list_entry(entry, type, member)
将一个list_head结构指针映射回指向包含它的大结构的指针
list_first_entry(head, type, member)
返回链表第一项(包含list_entry的结构体)
1.4.6 遍历
list_for_each(pos, head)
list_for_each_safe(pos, n, head)
顺序遍历链表,遍历过程中,pos指向list_head结构体
list_for_each_prev(pos, head)
list_for_each_prev_safe(pos, n, head)
逆序遍历链表,遍历过程中,pos指向list_head结构体
list_for_each_entry(pos, head, member)
顺序遍历链表,遍历过程中,pos指向包含list_head的大结构体
list_for_each_entry_reverse(pos, head, member)
逆序遍历链表,遍历过程中,pos指向包含list_head的大结构体
list_prepare_entry(pos, head, member)
list_for_each_entry_continue(pos, head, member)
list_for_each_entry_continue_reverse(pos, head, member)
list_for_each_entry_from(pos, head, member)
list_for_each_entry_safe(pos, n, head, member)
list_for_each_entry_safe_continue(pos, n, head, member)
list_for_each_entry_safe_reverse(pos, n, head, member)
1.5 示例代码
/* 练习使用linux内核链表,功能包括:
* 定义链表结构,创建链表、插入节点、删除节点、移动节点、遍历节点
**/
#include <stdio.h>
#include <inttypes.h>
#include <stdlib.h>
#include <errno.h>
#include "list.h"
//定义STUDENT链表结构
typedef struct student
{
uint32_t id;
char sex;
struct list_head list;//链表节点
}STUDENT;
STUDENT* set_student_info(uint32_t id, char sex)
{
STUDENT* stu = (STUDENT*)malloc(sizeof(STUDENT));
if (stu == NULL)
{
fprintf(stderr, "Failed to malloc memory, errno:%u, reason:%s\n", errno, strerror(errno));
return NULL;
}
stu->id = id;
stu->sex = sex;
return stu;
}
static void for_each_student(const struct list_head* head)
{
struct list_head* pos;
STUDENT* stu;
//遍历链表
list_for_each(pos, head)
{
stu = list_entry(pos, STUDENT, list);
printf("id: %u\tsex: %c\t\n",stu->id, stu->sex);
}
}
void destroy_student_list(struct list_head* head)
{
struct list_head* pos = head->next;
struct list_head* tmp = NULL;
while (pos != head)
{
tmp = pos->next;
list_del(pos);
pos = tmp;
}
}
int main()
{
//声明并初始化一个链表头结构head
LIST_HEAD(head);
//插入三个STUDENT
STUDENT* stu;
stu = set_student_info(1001,'f');
list_add_tail(&stu->list, &head);
stu = set_student_info(1002,'m');
list_add_tail(&stu->list, &head);
stu = set_student_info(1003,'f');
list_add_tail(&stu->list, &head);
printf("After insert three STUDENT: \n");
for_each_student(&head);
//将第一个节点移到末尾
printf("Move first node to tail:\n");
list_move_tail(head.next, &head);
for_each_student(&head);
//删除最后一个节点
printf("Delete the last node:\n");
list_del(head.prev);
for_each_student(&head);
destroy_student_list(&head);
return 0;
}
<list.h>
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ( { \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) ); } )
static inline void prefetch(const void *x) {;}
static inline void prefetchw(const void *x) {;}
#define LIST_POISON1 ((void *) 0x00100100)
#define LIST_POISON2 ((void *) 0x00200200)
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline 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;
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
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);
}
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;
}
static inline int list_empty_careful(const struct list_head *head)
{
struct list_head *next = head->next;
return (next == head) && (next == head->prev);
}
static inline void __list_splice(struct list_head *list,
struct list_head *head)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
struct list_head *at = head->next;
first->prev = head;
head->next = first;
last->next = at;
at->prev = last;
}
/**
* list_splice - join two lists
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice(struct list_head *list, struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head);
}
/**
* list_splice_init - join two lists and reinitialise the emptied list.
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head);
INIT_LIST_HEAD(list);
}
}
#define list_entry(ptr, type, member) container_of(ptr, type, member)
#define list_for_each(pos, head) \
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
pos = pos->next)
#define __list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
pos = pos->prev)
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
#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))
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_entry((head)->prev, typeof(*pos), member); \
prefetch(pos->member.prev), &pos->member != (head); \
pos = list_entry(pos->member.prev, typeof(*pos), member))
#define list_prepare_entry(pos, head, member) \
((pos) ? : list_entry(head, typeof(*pos), member))
#define list_for_each_entry_continue(pos, head, member) \
for (pos = list_entry(pos->member.next, typeof(*pos), member); \
prefetch(pos->member.next), &pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
//HASH LIST
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
static inline int hlist_empty(const struct hlist_head *h)
{
return !h->first;
}
static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
*pprev = next;
if (next)
next->pprev = pprev;
}
static inline void hlist_del(struct hlist_node *n)
{
__hlist_del(n);
n->next = LIST_POISON1;
n->pprev = LIST_POISON2;
}
static inline void hlist_del_init(struct hlist_node *n)
{
if (n->pprev) {
__hlist_del(n);
INIT_HLIST_NODE(n);
}
}
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;
if (first)
first->pprev = &n->next;
h->first = n;
n->pprev = &h->first;
}
/* next must be != NULL */
static inline void hlist_add_before(struct hlist_node *n,
struct hlist_node *next)
{
n->pprev = next->pprev;
n->next = next;
next->pprev = &n->next;
*(n->pprev) = n;
}
static inline void hlist_add_after(struct hlist_node *n,
struct hlist_node *next)
{
next->next = n->next;
n->next = next;
next->pprev = &n->next;
if(next->next)
next->next->pprev = &next->next;
}
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
pos = n)
#define hlist_for_each_entry(tpos, pos, head, member) \
for (pos = (head)->first; \
pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)
#define hlist_for_each_entry_continue(tpos, pos, member) \
for (pos = (pos)->next; \
pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)
#define hlist_for_each_entry_from(tpos, pos, member) \
for (; pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)
#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
for (pos = (head)->first; \
pos && ({ n = pos->next; 1; }) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = n)
#endif
2 队列
2.1 概述
内核中的队列是 以字节形式保存数据的 环形缓冲区,所以获取数据的时候,需要知道数据的大小。内核队列编程需要注意的是:
1 队列的size在初始化时,始终设定为2的n次方
2 使用队列之前将队列结构体中的锁(spinlock)释放
2.2 头文件定义
源码版本:2.6.34.14版本内核源码链表定义: include/linux/kfifo.h
2.3 数据结构
2.3.1 kfifo结构
struct kfifo {
unsigned char *buffer; /* the buffer holding the data */
unsigned int size; /* the size of the allocated buffer */
unsigned int in; /* data is added at offset (in % size) */
unsigned int out; /* data is extracted from off. (out % size) */
spinlock_t *lock; /* protects concurrent modifications */
};
2.4 操作函数
//根据给定buffer创建一个kfifo
void kfifo_init(struct kfifo *fifo, void *buffer, unsigned int size)
//给定size分配buffer和kfifo
int kfifo_alloc(struct kfifo *fifo, unsigned int size, gfp_t gfp_mask)
//释放kfifo空间
void kfifo_free(struct kfifo *fifo)
//向kfifo中添加数据
unsigned int kfifo_in(struct kfifo *fifo, const void *from,
unsigned int len)
//从kfifo中取数据
unsigned int kfifo_out(struct kfifo *fifo, void *to, unsigned int len)
//获取kfifo中有数据的buffer大小
unsigned int kfifo_len(struct kfifo *fifo)
在kfifo_init和kfifo_calloc中,kfifo->size的值总是在调用者传进来的size参数的
基础上向2的幂扩展,这是内核一贯的做法。这样的好处不言而喻--对kfifo->size取模运算可以转化为与运算,如:
kfifo->in % kfifo->size 可以转化为 kfifo->in & (kfifo->size – 1)。
kfifo的巧妙之处在于in和out定义为无符号类型,在put和get时,in和out都是增加,当达到最大值时,产生溢出,使得从0开始,进行循环使用。
例如下图:蓝色表示空闲,红色表示占用。
(1)空的kfifo
(2)put一个buffer后
(3)get一个buffer后
(4)当此时put的buffer长度超出in到末尾长度时,则将剩下的移到头部去
3 映射
3.1 概述
映射的有点想其他语言(C#或者python)中的字典类型,每个唯一的id对应一个自定义的数据结构。映射的使用需要注意的是,给自定义的数据结构申请一个id的时候,不能直接申请id,先要分配id(函数idr_pre_get),分配成功后,在获取一个id(函数idr_get_new)。3.2 头文件定义
源码版本:2.6.34.14版本内核源码链表定义: include/linux/idr.h
3.3 未整理
idr的结构比较复杂,我也没有很好的理解,但是csdn上有篇介绍linux idr结构的博客写的挺好,图文并茂:http://blog.csdn.net/paomadi/article/details/8539794
4 红黑树
4.1 概述
红黑树由于节点颜色的特性,保证其是一种自平衡的二叉搜索树。红黑树的一系列规则虽然实现起来比较复杂,但是遵循起来却比较简单,而且红黑树的插入,删除性能也还不错。所以红黑树在内核中的应用非常广泛,掌握好红黑树,即有利于阅读内核源码,也可以在自己的代码中借鉴这种数据结构。
红黑树必须满足的规则:
1,所有节点都有颜色,要么红色,要么黑色
2,根节点是黑色,所有叶子节点也是黑色
3,叶子节点中不包含数据
4,非叶子节点都有2个子节点
5,如果一个节点是红色,那么它的父节点和子节点都是黑色的
6,从任何一个节点开始,到其下叶子节点的路径中都包含相同数目的黑节点
红黑树中最长的路径就是红黑交替的路径,最短的路径是全黑节点的路径,再加上根节点和叶子节点都是黑色,从而可以保证红黑树中最长路径的长度不会超过最短路径的2倍。
4.2 头文件定义
源码版本:2.6.34.14版本内核源码链表定义: include/linux/rbtree.h
4.3 未整理
http://www.cnblogs.com/wang_yb/archive/2013/04/16/3023892.html