Linux编程细节3-内核-数据结构

内核代码版本:https://www.kernel.org/pub/linux/kernel/v2.6/   2.6.11

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 数据结构


linux内核中的链表使用方法和一般数据结构中定义的链表是有所不同的。

1.3.1 传统的list


传统的链表有个最大的缺点就是不好共通化,因为每个node中的data1,data2等等都是不确定的(无论是个数还是类型)。
linux中的链表巧妙的解决了这个问题,linux的链表不是将用户数据保存在链表节点中,而是将链表节点保存在用户数据中。

1.3.2 内核的list



即将传统的” 链表持有节点“改成现在的” 节点持有链表“。 这也即C++中的继承的含义

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 示例代码

<list.c>
/* 练习使用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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值