linux内核入门之list介绍(1)

1.介绍

linux内核中双向链表的核心数据结构struct list_head如下:
<linux/list.h>
struct list_head {
        struct list_head *next, *prev;
};

从上面定义可以看出,linux将双向链表关系关系抽离成单独的数据结构,只要包含了list_head类型成员的对象都能成为链表节点,这样就可以将具体的数据类型与数据之间的关系解耦合。

如果把包含list_head的对象称为节点,那么:
next指向下个节点的list_head;
prev指向上个节点的list_head。

使用list_head时,一般将list_head嵌套到自己的结构体中。如下:
struct my_struct {
        struct list_head list;
        void *data
};

链表使用前需要初始化,有两种初始化方式:
a.动态初始化,则运行时初始化
struct my_struct *p;
p = (struct my_struct *)kmalloc(sizeof(struct my_struct), GFP_KERNEL);
INIT_LIST_HEAD(&p->list);
p->data = NULL;

INIT_LIST_HEAD是一个inline函数。

b.静态初始化,即编译时初始化
struct my_struct mine = {
        .list = LIST_HEAD_INIT(mine.list),
        .data = NULL
};

LIST_HEAD_INIT是一个宏。

使用LIST_HEAD(list)直接创建一个list节点.

2.链表操作

如果把head当作头节点,则head->next为first节点,head->prev为last节点。
     +---------------------------------------------------------------------+
     |     +-----------+       +-----------+              +----------+     |
     +-----|   prev    | <-----|   prev    |<---      <---|   prev   | <---+
           +-----------+       +-----------+     ----     +----------+
     +---> |   next    | ----->|   next    |--->      --->|   next   | ----+
     |     +-----------+       +-----------+              +----------+     |
     +---------------------------------------------------------------------+
              head                  first                      last

a.添加新节点
list_add(struct list_head *new, struct list_head *head);
list_add_tail(struct list_head *new, struct list_head *head);
list_add将new节点添加在head与head->next之间;而list_add_tail则添加在head->prev与head之间。

b.删除节点
list_del(struct list_head *entry);
list_del_init(struct list_head *entry);
删除entry节点后,list_del把entry->next赋值为LIST_POISON1,entry->prev赋值为LIST_POISON2;
而list_del_init对entry执行了INIT_LIST_HEAD(entry)操作,即将entry的prev和next指针指向自身.


c.移动节点
list_move(struct list_head *list, struct list_head *head);
list_move_tail(struct list_head *list, struct list_head *head);
list_move节点list移到head之后,成为first节点;而list_move_tail则移动了head之前,即成为last节点.

d.判断链表是否为空
list_empty(struct list_head *head);

e.拼接两个链接
list_splice(struct list_head *list, struct list_head *head);
list_splice_init(struct list_head *list, struct list_head *head);
list_splice将list的first到last链表拼接到head与head->next之间;list_splice_init同时把list执行了INIT_LIST_HEAD操作.

f.获取包含list_head的结构体
list_entry(ptr, type, member)
prt : 为结构体中list_head指针;
type : 为结构体类型,如struct my_struct;
member : 为结构体内的list_head成员名称.
struct my_struct *my;
my = list_entry(p, struct my_struct, list);

g.遍历链表
list_for_each(pos, head)
list_for_each_safe(pos, n, head)
pos : 用于存放每次遍历时的元素指针
head : 遍历的链表头节点

list_for_each_safe用n指针临时存放pos的下个节点,避免被删除操作破坏.

struct list_head *p;
struct my_struct *my;
list_for_each(p, &mine->list) {
        my = list_entry(p, struct my_struct, list);
}

3.示例
a.目录结构
|--list_test.c
|--Makefile

b.源代码

文件list_test.c:


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

struct my_struct {
        struct list_head list;
        char ch;
};

static void prt_list(const char *msg, struct list_head *head)
{
        struct list_head *p;
        struct my_struct *my;

        printk(KERN_ALERT "%s", msg);
        list_for_each(p, head) {
                my = list_entry(p, struct my_struct, list);
                printk(KERN_ALERT "%c\n", my->ch);
        }
}
 
static int test_init(void)
{
        struct my_struct a, b, c, d;
        struct my_struct oa, ob, oc;

        /*define head node*/
        LIST_HEAD(head);
        LIST_HEAD(ohead);

        a.ch = 'a';
        b.ch = 'b';
        c.ch = 'c';

        list_add(&a.list, &head);
        list_add(&b.list, &head);
        list_add(&c.list, &head);

        prt_list("print list:\n", &head);

        list_del(&b.list);
        prt_list("after delete 'b':\n", &head);

        d.ch = 'd';
        list_add_tail(&d.list, &head);/*add 'd' to tail */
        prt_list("after add 'd' to tail:\n", &head);

        list_move(&d.list, &head);/*move 'd' to head*/
        prt_list("after move 'd' to head:\n", &head);

        oa.ch = 'A';
        ob.ch = 'B';
        oc.ch = 'C';
        list_add_tail(&oa.list, &ohead);
        list_add_tail(&ob.list, &ohead);
        list_add_tail(&oc.list, &ohead);
        prt_list("print other list:\n", &ohead);

        list_splice_init(&ohead, &head);
        prt_list("after splice other list to first list:\n", &head);
        if (list_empty(&ohead))
                printk(KERN_ALERT "other list is empty\n");

        return 0;
}

static void test_exit(void)
{
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ice");

/*
print list:
c
b
a
after delete 'b':
c
a
after add 'd' to tail:
c
a
d
after move 'd' to head:
d
c
a
print other list:
A
B
C
after splice other list to first list:
A
B
C
d
c
a
other list is empty
*/

文件Makefile:


obj-m := list_test.o

KERNELDIR ?= /lib/modules/`uname -r`/build
PWD := `pwd`

default:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学习嵌入式Linux内核是一个需要循序渐进的过程。首先需要了解Linux内核的基础知识,可以参考《深入理解Linux内核》这本书。这本书非常经典,通过对内核源码的学习,可以帮助你更好地理解Linux内核的工作原理和机制。 在学习过程中,可以按照以下学习路线进行: 1. 学习Linux基础知识:了解Linux操作系统的基本原理和概念,包括进程管理、内存管理、文件系统等。 2. 学习C语言编程:作为Linux内核的开发语言,掌握C语言编程是必不可少的。可以通过学习C语言的语法和特性来提高自己的编程能力。 3. 研究Linux内核源码:深入理解Linux内核需要对其源码进行研究。可以选择一些经典的书籍,如《深入理解Linux内核》,并结合实际的内核代码进行学习。 4. 参与开源项目:参与开源项目是提高自己对Linux内核理解的一个很好的方式。可以选择一些感兴趣的项目,并贡献自己的代码或者参与讨论,与其他开发者一起学习和成长。 总结来说,嵌入式Linux内核的学习需要从基础知识到深入研究内核源码,并通过实践和参与开源项目来提高自己的技能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [关于如何快速学好,学懂Linux内核。内含学习路线](https://blog.csdn.net/m0_74282605/article/details/127999926)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [嵌入式Linux入门指南(一)——学习路线篇](https://blog.csdn.net/weixin_51627076/article/details/122588888)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值