Linux内核链表

一、简介

  链表是一种常用的数据结构,它通过指针将一系列数据节点连接成一条数据链。相对于数组,链表具有更好的动态性,建立链表时无需预先知道数据总量,可以随机分配空间,可以高效地在链表中的任意位置实时插入或删除数据。链表的开销主要是访问的顺序性和组织链的空间损失。
  通常链表数据结构至少包含两个域:数据域和指针域,数据域用于存储数据,指针域用于建立与下一个节点的联系。按照指针域的组织以及各个节点之间的联系形式,链表又可以分为单链表、双链表、循环链表等多种类型。

二、内核链表

在Linux内核中使用了大量的链表结构来组织数据。这些链表大多采用了[include/linux/list.h]中实现的一套链表数据结构。

1. 定义

链表数据结构的定义:

struct list_head
{
    struct list_head *next, *prev;
};

list_head结构包含两个指向list_head结构的指针prev和next,由此可见,内核的链表具备双链表功能,实际上,通常它都组织成双向循环链表

传统的链表,指针的类型会随着节点struct的类型改变而改变,带数据域的链表降低了链表的通用性,不容易扩展;而Linux内核链表中,指针的类型固定不变都是 stuct list_head*。
传统链表-Linux内核链表

2. 链表操作

初始化链表头

/*
 * Simple doubly linked list implementation.
 *
 * Some of the internal functions ("__xxx") are useful when
 * manipulating whole lists rather than single entries, as
 * sometimes we already know the next/prev entries and we can
 * generate better code by using them directly rather than
 * using the generic single-entry routines.
 */

#define LIST_HEAD_INIT(name) { &(name), &(name) }
// 实例化list_head,并通过结构体直接赋值方式初始化
#define LIST_HEAD(name) \
    struct list_head name = LIST_HEAD_INIT(name)

static inline void INIT_LIST_HEAD(struct list_head *list)
{
    list->next = list;
    list->prev = list;
}

从链表头插入节点(插入头节点时)

/**
 * 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.
 * 在heah节点后面插入new节点,适用于实现栈
 * This is good for implementing stacks.
 */
static inline void list_add(struct list_head *new, struct list_head *head)
{
    // 在head和head->next之间插入new节点
    __list_add(new, head, head->next);
}

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_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.
 * 在heah节点前面插入new节点,适用于实现队列
 * 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_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;
}

删除节点

static inline void list_del(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
    entry->next = LIST_POISON1;
    entry->prev = LIST_POISON2;
}

/*
 * Delete a list entry by making the prev/next entries
 * point to each other.
 *
     * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
    next->prev = prev;
    prev->next = next;
}

提取数据结构

/**
 * list_entry - get the struct for this entry
 * @ptr:    the &struct list_head pointer.
 * @type:   the type of the struct this is embedded in.
 * @member: the name of the list_struct within the struct.
 * 已知数据结构(type型)中的节点指针ptr,找出数据结构
 */
#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)

遍历

/**
 * list_for_each    -   iterate over a list
 * @pos:    the &struct list_head to use as a loop cursor.
 * @head:   the head for your list.
 * pos指向以head节点开头的链表里的每一个节点
 */
#define list_for_each(pos, head) \
    for (pos = (head)->next; pos != (head); pos = pos->next)

3. 应用例子

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/list.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("XY");
MODULE_DESCRIPTION("List Module");
MODULE_ALIAS("List module");

struct student
{
    cahr name [100];
    int num;
    struct list_head list;
};

struct student *pstudent;
struct student *tmp_student;
struct list_head student_list;
struct list_head *pos;

int mylist_init()
{
    int i =0;

    INIT_LIST_HEAD(&student_list);

    pstudent = kmalloc(sizeof(struct student)*5,GFP_KERNEL);
    memset(pstudent,0,sizeof(struct student)*5);

    for(i=0;i<5;i++)
    {
        sprintf(pstudent[i].name,"Student%d",i+1);
        pstudent[i].num = i+1;
        list_add(&(pstudent[i].list),&student_list);
    }

    list_for_each(pos,&student_list)
    {
        tmp_student=list_entry(pos,struct student,list);
        printk("<0>student %d name: %s\n",tmp_student->num,tmp_student->name);
    }

    return 0;
}


void mylist_exit()
{
    int i;
    for(i=0;i<5;i++)
    {
        list_del(&(&pstudent[i].list));
    }
    kfree(pstudent);
}

module_init(mylist_init);
module_exit(mylist_exit);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值