Linux编程细节3-内核-数据结构
来源:互联网 发布:优化探究化学 编辑:程序博客网 时间:2024/06/07 15:52
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) 从链表中删除entrylist_replace(struct list_head *old, struct list_head *new)list_replace_init(struct list_head *old, struct list_head *new) 在链表中用new替换oldlist_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 LISTstruct 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创建一个kfifovoid kfifo_init(struct kfifo *fifo, void *buffer, unsigned int size)//给定size分配buffer和kfifoint 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
- Linux编程细节3-内核-数据结构
- linux编程细节1-内核-文件目录
- Linux编程细节4-内核-进程管理
- Linux编程细节5-内核-中断异常
- Linux编程细节6-内核-信号处理
- 内核模块化编程的细节
- 内核编程的数据结构
- linux内核细节-NULL
- Linux socket 编程细节
- linux socket编程细节
- Linux内核数据结构
- Linux内核数据结构
- linux内核数据结构
- linux内核中的数据结构
- Linux内核数据结构
- Linux内核关键数据结构
- linux内核数据结构
- Linux内核数据结构
- Cocos2D-X弹出对话框的实现与封装
- Mysql错误:Ignoring query to other database解决方法
- 如何解压RPM包
- HTTP头信息及响应码
- 常用的自定义UI组件- 一:TimeView
- Linux编程细节3-内核-数据结构
- linux硬链接与软链接
- Android 百度地图 java.lang.UnsatisfiedLinkError: Couldn't load vi_voslib: findLibrary returned null
- 广州SAP FICO课程培训在即
- Linux编程细节4-内核-进程管理
- 分享一个项目missian(hessian over mina)
- 函数属性__attribute__
- service 电话监听器并上传到服务器
- Java并发教程