第六章: Kernel Data Structures
1. Linked Lists
(1) singly list
(2) doubly list
(3) Circular Linked Lists
(4) 链表定义在, <linux/list.h>:
struct list_head {
struct list_head *next
struct list_head *prev;
};
(5) 使用container_of()可以定义一个函数来获得实际的数据结构.
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
(6) LIST_HEAD可以设置链表的头节点,如:static LIST_HEAD(fox_list);
(7) 添加节点: list_add(struct list_head *new, struct list_head *head);
删除节点: list_del(struct list_head *entry);
删除节点并重新初始化(与list_del()相同,出了重新初始化list_head,这样可以继续使用该节点数据):
list_del_init():
list_del_init(struct list_head *entry);
移动节点到另一个list:
移到head之后: list_move(struct list_head *list, struct list_head *head);
移到list末尾,head之前: list_move_tail(struct list_head *list, struct list_head *head);
检查list是否为空: list_empty(struct list_head *head);
合并两个list: list_splice(struct list_head *list, struct list_head *head);
合并两个list并重新初始化原list: list_splice_init(struct list_head *list, struct list_head *head);
(8) 遍历list: list_for_each()宏,参数为2个list_head结构,第一个指向当前entry,是提供的临时变量,第二个指向head node.在遍历过程中,第一个参数指向下一个entry.
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);
}
(9) list_for_each_entry(pos, head, member)宏常用来遍历list,pos指向包含list_head的对象,head是要遍历的list的头节点list_head,member是list_head结构的变量名.
struct fox *f;
list_for_each_entry(f, &fox_list, list) {
/* on each iteration, ‘f’ points to the next fox structure ... */
}
list_for_each_entry_reverse(pos, head, member) :反向遍历list
(10) 遍历时删除节点
list_for_each_entry_safe(pos, next, head, member), next用来保存下一个list节点.
反向遍历时对应list_for_each_entry_safe_reverse(pos, n, head, member).
(11) list相关函数定义在<linux/list.h>
2. Queues
(1) Linux内核队列实现叫kfifo,实现在kernel/kfifo.c, 声明在<linux/kfifo.h>.Thi本节讨论的API是基于2.6,33内核.
(2) Linux’s kfifo提供2种操作,入队列(in),出队列(out),两个offsets值,in offset是队列中下一个入队列位置,out offset是下一个出队列位置.out offset总是小于或等于in offset
out offset等于in offset, 队列为空; in offset等于队列长度时,队列为满.
(3) 动态创建队列:
int kfifo_alloc(struct kfifo *fifo, unsigned int size, gfp_t gfp_mask);返回0成功,负值失败.
示例:
struct kfifo fifo;
int ret;
ret = kfifo_alloc(&kifo, PAGE_SIZE, GFP_KERNEL);
if (ret)
return ret;
手动分配buffer:
void kfifo_init(struct kfifo *fifo, void *buffer, unsigned int size);
size必须是2的指数.
静态创建队列:
DECLARE_KFIFO(name, size);
INIT_KFIFO(name);
(3) 入队列:
unsigned int kfifo_in(struct kfifo *fifo, const void *from, unsigned int len);
这个函数负值从from开始的len个字节数据到队列fifo,成功返回入队列的字节数.如果小于len,只复制可用的字节数.
出队列:
unsigned int kfifo_out(struct kfifo *fifo, void *to, unsigned int len);
这个函数复制队列fifo中最多len个字节数据到to地址.成功返回复制数据的字节数.如果小于len,则复制了小于要求的数目.
取完数据后,队列中将不在存在这些数据.如果要保留,可以使用kfifo_out_peek():
unsigned int kfifo_out_peek(struct kfifo *fifo, void *to, unsigned int len, unsigned offset);
与kfifo_out()操作相同,只是offet没有增加,参数offset指明队列的索引,设置为0从队列头开始读取.像kfifo_out()一样.
(4) 获取队列长度:
static inline unsigned int kfifo_size(struct kfifo *fifo);
static inline unsigned int kfifo_len(struct kfifo *fifo);
获取队列可用的空间:
static inline unsigned int kfifo_avail(struct kfifo *fifo);
判断队列是否为空或为满:
static inline int kfifo_is_empty(struct kfifo *fifo);
static inline int kfifo_is_full(struct kfifo *fifo);
(5) reset队列:
static inline void kfifo_reset(struct kfifo *fifo);
销毁队列:
void kfifo_free(struct kfifo *fifo);
3. Maps
(1) 映射支持至少3种操作:
Add (key, value)
Remove (key)
value = Lookup (key)
maps可以实现为hash表,也可以实现为二叉树.
Although a hash offers better average-case asymptotic complexity, a binary search tree has better worst-case behavior (logarithmic versus linear).
Linux提供简单而有效的map数据结构,仅仅用于一种情况: 映射 unique identification number(UID)到一个指针.
Linux在add操作之前还提供allocate操作,这不仅仅添加UID/value的映射,还生成UID.
idr结构用来映射用户空间的UID,如映射inotify watch descriptors or POSIX timer IDs到内核数据结构(inotify_watch or k_itimer),这个map叫做idr.
(2)初始化idr
首先静态或者动态创建idr结构,然后调用idr_init():
void idr_init(struct idr *idp);
如:
struct idr id_huh; /* statically define idr structure */
idr_init(&id_huh); /* initialize provided idr structure */
(3)分配新的UID
第一步,重新分配backing tree的大小,使用 idr_pre_get():
int idr_pre_get(struct idr *idp, gfp_t gfp_mask);(返回1成功,0失败)
第二步,获取新的UID,添加到idr,使用idr_get_new():
int idr_get_new(struct idr *idp, void *ptr, int *id);(返回0成功,非0失败)
idr_get_new_above() 能够申请获取最小的UID值:
int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id);
(4)查询UID
void *idr_find(struct idr *idp, int id);
(5)移除UID
void idr_remove(struct idr *idp, int id);
(6)销毁idr
void idr_destroy(struct idr *idp);(只释放违背使用的内存,不释放已经使用的)
强制移除所有UID
void idr_remove_all(struct idr *idp);
4. 二叉树(binary trees)
(1) it is an acyclic, connected, directed graph in which each vertex (called a node) has zero or
more outgoing edges and zero or one incoming edges.
(2) Binary Search Trees
二叉查找树是节点有特殊顺序的,一般遵循以下规则:
左孩子小于父节点;右孩子大于父节点;所有的子树都是二叉查找树.
(3) Self-Balancing Binary Search Trees
自平衡二叉查找树所有叶子的深度差最多为1.
(4) Red-Black Trees
Red-Black Trees是Self-Balancing Binary Search Tree的一种类型. 遵循以下规则:
所有的节点非红即黑;叶子节点为黑;叶子节点不包含数据;非叶子节点有2个孩子;如果节点是红,其孩子为黑;节点到其叶子的深度包含与到其他叶子相同数目的黑节点.
(5) rbtrees
1. Linked Lists
(1) singly list
/* an element in a linked list */
struct list_element {
void *data; /* the payload */
struct list_element *next; /* pointer to the next element */
};
![](http://hi.csdn.net/attachment/201201/13/0_1326420104r6QY.gif)
(2) doubly list
/* an element in a linked list */
struct list_element {
void *data; /* the payload */
struct list_element *next; /* pointer to the next element */
struct list_element *prev; /* pointer to the previous element */
};
![](http://hi.csdn.net/attachment/201201/13/0_1326420240557r.gif)
(3) Circular Linked Lists
![](http://hi.csdn.net/attachment/201201/23/0_1327318924k1Ep.gif)
(4) 链表定义在, <linux/list.h>:
struct list_head {
struct list_head *next
struct list_head *prev;
};
(5) 使用container_of()可以定义一个函数来获得实际的数据结构.
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
(6) LIST_HEAD可以设置链表的头节点,如:static LIST_HEAD(fox_list);
(7) 添加节点: list_add(struct list_head *new, struct list_head *head);
删除节点: list_del(struct list_head *entry);
删除节点并重新初始化(与list_del()相同,出了重新初始化list_head,这样可以继续使用该节点数据):
list_del_init():
list_del_init(struct list_head *entry);
移动节点到另一个list:
移到head之后: list_move(struct list_head *list, struct list_head *head);
移到list末尾,head之前: list_move_tail(struct list_head *list, struct list_head *head);
检查list是否为空: list_empty(struct list_head *head);
合并两个list: list_splice(struct list_head *list, struct list_head *head);
合并两个list并重新初始化原list: list_splice_init(struct list_head *list, struct list_head *head);
(8) 遍历list: list_for_each()宏,参数为2个list_head结构,第一个指向当前entry,是提供的临时变量,第二个指向head node.在遍历过程中,第一个参数指向下一个entry.
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);
}
(9) list_for_each_entry(pos, head, member)宏常用来遍历list,pos指向包含list_head的对象,head是要遍历的list的头节点list_head,member是list_head结构的变量名.
struct fox *f;
list_for_each_entry(f, &fox_list, list) {
/* on each iteration, ‘f’ points to the next fox structure ... */
}
list_for_each_entry_reverse(pos, head, member) :反向遍历list
(10) 遍历时删除节点
list_for_each_entry_safe(pos, next, head, member), next用来保存下一个list节点.
反向遍历时对应list_for_each_entry_safe_reverse(pos, n, head, member).
(11) list相关函数定义在<linux/list.h>
2. Queues
(1) Linux内核队列实现叫kfifo,实现在kernel/kfifo.c, 声明在<linux/kfifo.h>.Thi本节讨论的API是基于2.6,33内核.
(2) Linux’s kfifo提供2种操作,入队列(in),出队列(out),两个offsets值,in offset是队列中下一个入队列位置,out offset是下一个出队列位置.out offset总是小于或等于in offset
out offset等于in offset, 队列为空; in offset等于队列长度时,队列为满.
(3) 动态创建队列:
int kfifo_alloc(struct kfifo *fifo, unsigned int size, gfp_t gfp_mask);返回0成功,负值失败.
示例:
struct kfifo fifo;
int ret;
ret = kfifo_alloc(&kifo, PAGE_SIZE, GFP_KERNEL);
if (ret)
return ret;
手动分配buffer:
void kfifo_init(struct kfifo *fifo, void *buffer, unsigned int size);
size必须是2的指数.
静态创建队列:
DECLARE_KFIFO(name, size);
INIT_KFIFO(name);
(3) 入队列:
unsigned int kfifo_in(struct kfifo *fifo, const void *from, unsigned int len);
这个函数负值从from开始的len个字节数据到队列fifo,成功返回入队列的字节数.如果小于len,只复制可用的字节数.
出队列:
unsigned int kfifo_out(struct kfifo *fifo, void *to, unsigned int len);
这个函数复制队列fifo中最多len个字节数据到to地址.成功返回复制数据的字节数.如果小于len,则复制了小于要求的数目.
取完数据后,队列中将不在存在这些数据.如果要保留,可以使用kfifo_out_peek():
unsigned int kfifo_out_peek(struct kfifo *fifo, void *to, unsigned int len, unsigned offset);
与kfifo_out()操作相同,只是offet没有增加,参数offset指明队列的索引,设置为0从队列头开始读取.像kfifo_out()一样.
(4) 获取队列长度:
static inline unsigned int kfifo_size(struct kfifo *fifo);
static inline unsigned int kfifo_len(struct kfifo *fifo);
获取队列可用的空间:
static inline unsigned int kfifo_avail(struct kfifo *fifo);
判断队列是否为空或为满:
static inline int kfifo_is_empty(struct kfifo *fifo);
static inline int kfifo_is_full(struct kfifo *fifo);
(5) reset队列:
static inline void kfifo_reset(struct kfifo *fifo);
销毁队列:
void kfifo_free(struct kfifo *fifo);
3. Maps
(1) 映射支持至少3种操作:
Add (key, value)
Remove (key)
value = Lookup (key)
maps可以实现为hash表,也可以实现为二叉树.
Although a hash offers better average-case asymptotic complexity, a binary search tree has better worst-case behavior (logarithmic versus linear).
Linux提供简单而有效的map数据结构,仅仅用于一种情况: 映射 unique identification number(UID)到一个指针.
Linux在add操作之前还提供allocate操作,这不仅仅添加UID/value的映射,还生成UID.
idr结构用来映射用户空间的UID,如映射inotify watch descriptors or POSIX timer IDs到内核数据结构(inotify_watch or k_itimer),这个map叫做idr.
(2)初始化idr
首先静态或者动态创建idr结构,然后调用idr_init():
void idr_init(struct idr *idp);
如:
struct idr id_huh; /* statically define idr structure */
idr_init(&id_huh); /* initialize provided idr structure */
(3)分配新的UID
第一步,重新分配backing tree的大小,使用 idr_pre_get():
int idr_pre_get(struct idr *idp, gfp_t gfp_mask);(返回1成功,0失败)
第二步,获取新的UID,添加到idr,使用idr_get_new():
int idr_get_new(struct idr *idp, void *ptr, int *id);(返回0成功,非0失败)
idr_get_new_above() 能够申请获取最小的UID值:
int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id);
(4)查询UID
void *idr_find(struct idr *idp, int id);
(5)移除UID
void idr_remove(struct idr *idp, int id);
(6)销毁idr
void idr_destroy(struct idr *idp);(只释放违背使用的内存,不释放已经使用的)
强制移除所有UID
void idr_remove_all(struct idr *idp);
4. 二叉树(binary trees)
(1) it is an acyclic, connected, directed graph in which each vertex (called a node) has zero or
more outgoing edges and zero or one incoming edges.
![](http://hi.csdn.net/attachment/201202/24/0_1330076324wc9d.gif)
(2) Binary Search Trees
二叉查找树是节点有特殊顺序的,一般遵循以下规则:
左孩子小于父节点;右孩子大于父节点;所有的子树都是二叉查找树.
(3) Self-Balancing Binary Search Trees
自平衡二叉查找树所有叶子的深度差最多为1.
![](http://hi.csdn.net/attachment/201202/28/0_13304171925MPO.gif)
(4) Red-Black Trees
Red-Black Trees是Self-Balancing Binary Search Tree的一种类型. 遵循以下规则:
所有的节点非红即黑;叶子节点为黑;叶子节点不包含数据;非叶子节点有2个孩子;如果节点是红,其孩子为黑;节点到其叶子的深度包含与到其他叶子相同数目的黑节点.
(5) rbtrees
Linux中Red-Black Trees的实现为rbtrees, 定义在lib/rbtree.c, 声明在<linux/rbtree.h>.
rbtree的根节点为rb_root,要创建一个新的rbtree,首先新建一个rb_root,初值为RB_ROOT.
struct rb_root root = RB_ROOT;
rbtress的节点为rb_node,通过其可以移动左右孩子.
rbtree的实现不提供查找和插入的例程.这些功能由用户实现.