首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

linux链表中的__list_add Vs list_add

基础概念

在Linux内核中,链表是一种常用的数据结构,用于存储一系列元素。链表中的每个元素称为节点,每个节点包含数据和指向下一个节点的指针。Linux内核提供了多种链表操作函数,其中__list_addlist_add是两个常用的函数。

相关优势

  • 动态内存分配:链表允许动态地添加或删除节点,不需要预先分配固定大小的内存。
  • 灵活性:链表中的元素可以分散在内存中,插入和删除操作的时间复杂度为O(1)(不考虑查找节点的时间)。
  • 通用性:链表适用于各种场景,如任务调度、内存管理等。

类型

Linux内核中的链表主要有以下几种类型:

  1. 单向链表:每个节点只有一个指向下一个节点的指针。
  2. 双向链表:每个节点有两个指针,分别指向前一个节点和后一个节点。
  3. 循环链表:链表的最后一个节点指向第一个节点,形成一个环。

应用场景

链表在Linux内核中的应用非常广泛,例如:

  • 任务调度:Linux内核使用链表来管理进程和线程。
  • 内存管理:链表用于管理内存块。
  • 设备驱动:链表用于管理设备对象。

__list_add Vs list_add

__list_add

__list_add函数用于在链表中插入一个新节点。它的原型如下:

代码语言:txt
复制
static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next);
  • new:要插入的新节点。
  • prev:新节点的前一个节点。
  • next:新节点的后一个节点。

__list_add函数不会检查链表是否为空,也不进行任何边界检查。

list_add

list_add函数是__list_add的一个封装,用于在链表的头部插入一个新节点。它的原型如下:

代码语言:txt
复制
static inline void list_add(struct list_head *new, struct list_head *head);
  • new:要插入的新节点。
  • head:链表的头节点。

list_add函数会自动处理链表为空的情况,并且只适用于在链表头部插入节点。

示例代码

以下是一个使用list_add函数在链表头部插入新节点的示例:

代码语言:txt
复制
#include <linux/list.h>

struct my_node {
    int data;
    struct list_head list;
};

int main() {
    struct list_head my_list;
    struct my_node node1, node2;

    // 初始化链表头
    INIT_LIST_HEAD(&my_list);

    // 初始化节点1
    node1.data = 1;
    INIT_LIST_HEAD(&node1.list);

    // 在链表头部插入节点1
    list_add(&node1.list, &my_list);

    // 初始化节点2
    node2.data = 2;
    INIT_LIST_HEAD(&node2.list);

    // 在链表头部插入节点2
    list_add(&node2.list, &my_list);

    // 遍历链表并打印数据
    struct list_head *pos;
    struct my_node *entry;
    list_for_each(pos, &my_list) {
        entry = list_entry(pos, struct my_node, list);
        printk(KERN_INFO "Node data: %d\n", entry->data);
    }

    return 0;
}

参考链接

常见问题及解决方法

问题:链表插入节点时出现内存访问错误

原因:可能是由于指针未正确初始化或指向无效内存地址。

解决方法:确保所有链表节点的内存分配和指针初始化都正确无误。使用INIT_LIST_HEAD宏初始化链表头和节点的链表指针。

问题:链表遍历时出现死循环

原因:可能是由于链表中存在循环引用,或者遍历逻辑错误。

解决方法:检查链表的插入和删除操作,确保没有引入循环引用。遍历时使用list_for_each等标准宏,避免手动遍历时出现逻辑错误。

通过以上解释和示例代码,希望你能更好地理解Linux链表中的__list_addlist_add函数及其应用。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

C 链表 - linux 如何实现

想起前段时间, 看到FreeRTOS提供链表处理方式(《 FreeRTOS 任务调度 List 组织 》), 将链表结构定义和实际使用时具体节点数据内容分开定义, 供系统各个模块使用。...查看linux源码, 发现linux也为我们提供了相似的实现(源码), 把一些共性统一起来。 类是 python for_each处理,有些意思。...linux链表定义在文件 include/linux/types.h, 采用是双向列表 struct list_head { struct list_head *next, *prev;...list 利用这个定义, 我定义了一个自己list数据结构, 并copy了一些接口实现,感受下,linux 是如何管理链表。...另外一种做法是定义list_head, 包含一个成员变量,指向其所属,FreeRTOS是如此做

2.7K30

工作当中非常实用Linux内核链表

前言: 在上期文章,已经给大家分享过offsetof()和container_of两个宏函数,这两个宏函数在Linux内核链表里面有大量应用,对于我们平时工作写代码有很大帮助。...下面是Linux内核链表内容分享。...做内核驱动开发经常会使用linux内核最经典双向链表 list_head, 以及它拓展接口(或者宏定义): list_add , list_add_tail, list_del , list_entry...; }; 然后就开始围绕这个结构开始构建链表,然后插入、删除节点 ,遍历整个链表等等,其实内核已经提供好了现成接口,接下来就让我们进入 kernel/include/linux/list.h: 一...2. list_add_tail 接口 : 上面所讲list_add接口是从链表头header后添加节点。同样,内核也提供了从链表尾处向前添加节点接口list_add_tail.

1K10
  • linux内核源码 -- list链表

    linux kernellist估计已经被各位前辈们写烂了,但是我还是想在这里记录一下; linux kernel里很多数据结构都很经典, list链表就是其中之一 本篇要介绍内容: list.../linux/types.h找到; 定义 实际上这就是一个双向循环链表, 且有一个头指针 list head定义: struct list_head { struct list_head *next...思想很巧妙, 对用户定义数据结构侵入性很小, 实现了c++std::List模板功能; 虽然这个定义是叫head, 但其实嵌入到用户定义数据结构也是这个....__list_del_entry(list); // 添加到新链表头部 list_add(list, head); } 将一个元素移动到另一个list队尾 static...struct,这个宏就是由这个list_head ptr来获取当前所处struct对象指针, 用了linux经典宏定义 container_of #define list_entry(ptr,

    2.4K10

    Linux 内核通用链表学习小结

    描述 在linux内核中封装了一个通用双向链表库,这个通用链表库有很好扩展性和封装性,它给我们提供了一个固定指针域结构体,我们在使用时候,只需要在我们定义数据域结构体包含这个指针域结构体就可以了...传统链表结构 struct node{ int key; int val; node* prev; node* next; } linux 内核通用链表库结构 提供给我们指针域结构体...内核是一个常用宏,用于从包含在某个 //结构指针获得结构本身指针,通俗地讲就是通过结构体变 //量某个成员首地址进而获得整个结构体变量首地址 #define container_of(ptr...方式一 list_add(&a->list,&head); list_add(&b->list,&head); //插入链表 方式二 list_add_tail(&a->list,&head); list_add_tail...list_add-先入先出模式 我们链表节点,实际在内存展示形态 ?

    1.3K21

    如何移植并使用Linux内核通用链表(附完整代码实现)

    在实际工作,我们可能会经常使用链表结构来存储数据,特别是嵌入式开发,经常会使用linux内核最经典双向链表 list_head。...本篇文章详细介绍了Linux内核通用链表是如何实现,对于经常使用函数都给出了详细说明和测试用例,并且移植了Linux内核链表结构,在任意平台都可以方便调用内核已经写好函数。...Linux内核链表   上面介绍了普通链表实现方式,可以看到数据域都是包裹在节点指针,通过节点指针访问下一组数据。...但是 Linux内核链表实现可以说比较特殊,只有前驱和后继指针,而没有数据域。链表头文件是在include/list.h(Linux2.6内核)下。...在实际工作,也可以将内核链表拷贝出来供我们使用,就需不要造轮子了。 链表定义   内核链表只有前驱和后继指针,并不包含数据域,这个链表具备通用性,使用非常方便。

    1.5K20

    Linux内核双向链表经典实现

    概要 本文对双向链表进行探讨,介绍内容是Linux内核双向链表经典实现和用法。其中,也会涉及到Linux内核中非常常用两个经典宏定义offsetof和container_of。...内容包括: 1.Linux两个经典宏定义 2.Linux双向链表经典实现 Linux两个经典宏定义 倘若你查看过Linux Kernel源码,那么你对 offsetof 和 container_of...在linux内核include/linux/kernel.h定义。...Linux双向链表经典实现 1.Linux双向链表介绍 Linux双向链表定义主要涉及到两个文件: include/linux/types.h include/linux/list.h Linux...双向链表使用思想 它是将双向链表节点嵌套在其它结构体;在遍历链表时候,根据双链表节点指针获取"它所在结构体指针",从而再获取数据。

    2.6K30

    002 Linux内核双向链表经典实现

    概要 本文对双向链表进行探讨,介绍内容是Linux内核双向链表经典实现和用法。其中,也会涉及到Linux内核中非常常用两个经典宏定义offsetof和container_of。...内容包括: 1.Linux两个经典宏定义 2.Linux双向链表经典实现 Linux两个经典宏定义 倘若你查看过Linux Kernel源码,那么你对 offsetof 和 container_of...在linux内核include/linux/kernel.h定义。...Linux双向链表经典实现 1.Linux双向链表介绍 Linux双向链表定义主要涉及到两个文件: include/linux/types.h include/linux/list.h Linux...双向链表使用思想 它是将双向链表节点嵌套在其它结构体;在遍历链表时候,根据双链表节点指针获取"它所在结构体指针",从而再获取数据。

    1.8K20

    linux通用链表

    引言 链表实现是基于结构体与指针两者实现,常用链表数据结构如下: //将int起别名ELEMTYPE,是为了方便修改链表数据域类型。...在Linux设计了一种适合于各种类型数据域都可以使用通用型链表: struct list_head { struct list_head *prev, *next; }; 摒弃掉数据域,只保留头尾指针...内核链表 链表主要意义就是将分散地址数据域通过指针排列成有序队列。因此数据域是链表不可或缺一部分,但是在实际使用需要不同类型数据域,因此也就限制了链表通用。...Linux在声明抛弃了数据域,也就解决掉了这一问题。 原理 Linux使用链表方法:使用时,自定义结构体包含数据域+链表结构体。...链表.png 如上图所示,将结构体A、B、C内核结构体指针构建成双链表,这样结构体A、B、C链表成员就可以互相索引了。

    1.1K20

    一文搞懂 Linux 内核链表(深度分析)

    Linux 内核中使用最多数据结构就是链表了,其中就包含了许多高级思想。 比如面向对象、类似C++模板实现、堆和栈实现。 1....如果去掉前驱指针,就是单循环链表。 ? 2. 内核链表Linux内核中使用了大量链表结构来组织数据,包括设备列表以及各种功能模块数据组织。...这些链表大多采用在[include/linux/list.h]实现一个相当精彩链表数据结构。事实上,内核链表就是采用双循环链表机制。 内核链表有别于传统链表就在节点本身不包含数据域,只包含指针域。...2.3 添加节点 内核相应提供了添加节点接口: list_add list_add 如下,最终调用是__list_add 函数,根据注释可知,list_add 是头部插入,总是在链表头部插入一个新节点...总结 本文详细分析了 linux 内核 链表结构,以图文方式旨在帮助大家理解。

    8.2K77

    C语言-链表(单向链表、双向链表)

    链表结构介绍 在前面章节已经学习了数组使用,数组空间是连续空间,数组大小恒定,在很多动态数据存储应用场景下,使用不方便;而这篇文章介绍链表结构,支持动态增加节点,释放节点,比较适合存储动态数据应用场景...实现功能如下: 初始化链表头 插入节点函数(链表任意位置插入,链表尾插入) 删除节点函数(链表任意位置删除、链表尾删除) 遍历链表,输出链表所有信息 #include #include...在链表尾插入数据 list_add(10,list_head); list_add(11,list_head); list_add(12,list_head); list_add...在链表尾插入数据 list_add(10,list_head); list_add(11,list_head); list_add(12,list_head); list_add...添加链表节点*/ list_add(10,list_head); list_add(11,list_head); list_add(12,list_head); list_add

    2.1K30

    内存分配算法 伙伴系统

    伙伴系统是常用内存分配算法,linux内核底层页分配算法就是伙伴系统,伙伴系统优点就是分配和回收速度快,减少外部碎片。...然后又看了一下linux4.8buddy system实现,linuxbuddy system主要进行page分配也是linux最底层分配,其他分配算法都是以这个分配为基础,在x86架构下一个page...linux对内存进行了分区包括低端内存区,高端内存区,dma区,而且还对numa架构做了很多处理,对页面也进行了分类,这些不是讨论重点,现在主要是提取linuxbuddy算法,只提取核心部分,可以在控制台下运行...buddy system数据结构就是下图所示,看着像哈希表拉链法,每个链表保存相同大小内存块。最大是10,也就是1024个基本单位,所以linux在x86下一次最多可分配4MB内存。  ...顺便学习下linux内核对链表各种操作,代码实现 #include #include #include #include <assert.h

    1.6K10

    内核链表介绍

    应要求分享一下内核链表结构,故写了本blog。本文对内核链表做一个简单介绍,以及引出内核中大量使用分离思想和数据结构定义。...传统链表困境 内核数据结构千变万化,采用传统链表结构形式,需要为各种数据都定义出一个链表。...内核链表 内核链表正是采用了如上思想进行设计,内核链表位于内核代码include/linux/list.h,该链表定义为双向循环链表,所有的相关操作都定义在该头文件,该文件每个函数极为简洁。...(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } 使用内核链表方式,将链表节点嵌入到数据结构体...*fops; struct list_head list; /* 用内核链表管理所有注册在内核misc设备 */ struct device *parent; struct

    30220

    数据结构2——linuxC(双向循环链表+内核链表

    \n"); return; } // 2.操作pos前后节点,使他们联系起来。...1.kernel_list.h #ifndef __DLIST_H #define __DLIST_H /* This file is from Linux Kernel (include/linux...\n"); return; } // 1.遍历链表,对比找到指定节点,找到则跳出break // 如果在遍历删除、移动节点,必须使用安全模式 // 如果只是遍历访问,不修改节点,使用安全...node, list); // if(get_node->data%2 == 0) // list_move_tail(pos, &head->list); // } // 自行添加前序遍历安全模式...\n"); return; } // 1.遍历链表,对比找到指定节点,找到则跳出break // 如果在遍历删除、移动节点,必须使用安全模式 // 如果只是遍历访问,不修改节点,使用安全

    1.2K42

    ucore-lab2

    } list_del(&(page->page_link)); //删掉原先链表节点 nr_free -= n; ClearPageProperty(page...考虑first-fit算法,在释放页时候应该按照大小顺序插入链表,但该函数里面仍采用了list_add函数, static void default_free_pages(struct Page *base...算法是通过采用链表来实现查找,实际上可以采用更高效数据结构比如bst,红黑树这些。...challenge 1 我建议是仔细阅读文档里给链接:coolshell 伙伴分配实质就是一种特殊“分离适配”,即将内存按2幂进行划分,相当于分离出若干个块大小一致空闲链表,搜索该链表并给出同需求最佳匹配大小...Kiprey就是采用双向链表实现,内存上开销会比较小,但在双向链表难以确定块之间合并操作。ZebornDuan采用了二叉树实现,我决定参考他代码写一写。

    65330

    数据结构 | TencentOS-tiny双向循环链表实现及使用

    相较于其他形式链表,双向循环链表添加节点,删除节点,遍历节点都非常简单。 2. 双向循环链表实现 TencentOS-tiny双向链表实现在tos_list.h。 2.1....插入节点 向双向链表插入一个节点非常简单: void _list_add(k_list_t *node, k_list_t *prev, k_list_t *next) { next->prev...插入前双向循环链表如下: ? 插入后双向循环链表如下: ? 图中四个插入过程分别对应代码四行代码。...{ _list_add(node, list->prev, list); } 因为是双向循环链表,所以尾部插入是在第一个节点和最后一个节点之间插入。...TencentOS-tiny依然提供了两个宏定义来解决这一问题,在tos_klib.h

    90420

    TencentOS-tiny双向循环链表实现及使用

    本文讨论是不带头节点双向循环链表,如下图: [qowp0vrk7c.png] 2. 双向循环链表实现 TencentOS-tiny双向链表实现在tos_list.h。 2.1....插入节点 向双向链表插入一个节点非常简单: void _list_add(k_list_t *node, k_list_t *prev, k_list_t *next) { next->prev...插入前双向循环链表如下: [12x9hk0jf4.png] 插入后双向循环链表如下: [g8b3e5w8ks.png] 图中四个插入过程分别对应代码四行代码。...{ _list_add(node, list->prev, list); } 因为是双向循环链表,所以尾部插入是在第一个节点和最后一个节点之间插入。...TencentOS-tiny依然提供了两个宏定义来解决这一问题,在tos_klib.h

    1.1K1313
    领券