当前位置: 首页 > news >正文

数据结构---->内核链表

一、内核链表的基本概念

1. 定义

内核链表是一种线性数据结构,其中每个节点包含了数据元素本身以及指向下一个节点的指针。在Linux内核中,这种链表通常被实现为双向链表或循环链表,以支持更高效的插入、删除和遍历操作。

2. 节点结构

内核链表的节点通常包含至少两个指针:一个指向前一个节点(prev),另一个指向后一个节点(next)。在某些实现中,还可能包含一个指向数据本身的指针,但在Linux内核的链表中,节点本身并不直接存储用户数据,而是将用户数据保存在包含链表节点的结构体中。

3. 链表头

链表头是一个特殊的节点,它通常不包含有效数据,但用于标识链表的开始位置。在双向链表中,链表头还可能包含指向链表最后一个节点的指针,以及一个指向链表第一个节点的指针。

二、内核链表的特点

1. 高效性

        内核链表通过指针将节点连接在一起,使得插入、删除和遍历操作可以在O(1)或O(n)的时间

复杂度内完成,这取决于操作的具体位置和链表的长度。

2. 灵活性

        由于节点之间通过指针连接,因此不需要像数组那样在物理内存中占用连续的空间。这使得

内核链表能够灵活地管理动态分配的内存,并且可以根据需要动态地添加或删除节点。

3. 稳定性

        内核链表在操作系统内核中广泛使用,因此其实现必须稳定可靠。Linux内核中的链表实现经

过了严格的测试和验证,以确保在各种情况下都能正常工作。

三、内核链表的操作

1. 初始化

        在创建链表之前,需要先对链表头进行初始化。这通常包括设置链表头的指针为NULL(对于

单向链表)或指向自身(对于双向循环链表)。

typedef struct knode
{struct knode *ppre;struct knode *pnext;}Knode_t;typedef struct klink
{Knode_t *phead;int clen;pthread_mutex_t mutex;
}Klink_t;

2. 插入节点

        在链表中插入节点时,需要找到插入位置的前一个节点,并修改该节点和待插入节点的指

针。对于双向链表,还需要更新待插入节点的前驱指针。

int push_klink_head(Klink_t *pklink, void *p)
{Knode_t *pnode = (Knode_t *)p;pnode->pnext = NULL;pnode->ppre = NULL;pnode->pnext = pklink->phead;if (pklink->phead != NULL){pklink->phead->ppre = pnode;}pklink->phead = pnode;pklink->clen++;return 0;
}
int push_klink_tail(Klink_t *pklink,void *p)
{Knode_t *pnode = (Knode_t *)p;pnode->pnext =NULL;pnode->ppre =NULL;if(pklink->phead!=NULL){Knode_t *p = pklink->phead;while(p->pnext!=NULL){p=p->pnext;}p->pnext=pnode;pnode->ppre=p;}else if(pklink->phead==NULL){pklink->phead = pnode;}pklink->clen++;return 0;
}

3. 删除节点

        删除链表中的节点时,需要找到该节点的前一个节点和后一个节点,并修改它们的指针以绕

过被删除的节点。对于双向链表,还需要更新被删除节点的前驱指针的指向。

int pop_klink_head(Klink_t *pklink)
{if(pklink->phead ==NULL){return 0;}Knode_t *p = pklink->phead;pklink->phead = p->pnext;p->ppre = NULL;free(p);if(pklink->phead !=NULL){pklink->phead->ppre=NULL;}pklink->clen--;return 0;
}
int pop_klink_tail(Klink_t *pklink)
{if(pklink->phead==NULL){return 0;}Knode_t *p = pklink->phead;while(p->pnext != NULL){p=p->pnext;}if(p->ppre!= NULL){p->ppre->pnext=NULL;}else{pklink->phead = NULL;}free(p);pklink->clen--;return 0;
}

4. 遍历链表

        遍历链表通常从链表头开始,依次访问每个节点直到链表末尾。在双向链表中,可以从链表

头或链表尾开始遍历。

void klink_for_each(Klink_t *pklink, void (*pfun)(void *))
{Knode_t *pnode = pklink->phead;while (pnode != NULL){pfun(pnode);pnode = pnode->pnext;}printf("\n");
}
5.查找
KNode_t *find_klink(KLink_t *pklink, void *t, CMP_t pfun)
{KNode_t *pnode = pklink->phead;while (pnode != NULL){if (pfun(t, pnode)){return pnode;}pnode = pnode->pnext;}return NULL;
}
6.销毁
int is_empty_klink(KLink_t *pklink)
{return NULL == pklink->phead;
}
void destroy_klink(KLink_t *pklink)
{while (!is_empty_klink(pklink)){pop_klink_head(pklink);}free(pklink);
}

四、内核链表的应用

        内核链表在操作系统内核中广泛应用于各种场景,如管理进程、文件描述符、内存分配等。

通过内核链表,操作系统可以有效地组织和访问这些数据元素,从而提高系统的整体性能和稳定

性。

五.内核链表与双向链表区别

        双向链表:实现方式相对简单直接,每个节点包含数据和两个指针。

        内核链表:在Linux内核中,链表节点(list_head)与具体的数据结构分离,通过包含

list_head的结构体来实现链表的构建。这种方式提高了链表的通用性和灵活性,使得可以在不修

改链表本身的情况下,轻松地扩展或修改数据结构。

六.函数指针

        函数指针是指向函数的指针。在C语言中,函数名在大多数表达式中会被转换为指向该函数的

指针。这意味着你可以定义一个指针变量,让它存储一个函数的地址,然后通过这个指针来调用该

函数。

七.指针函数

        指针函数实际上是一个函数,这个函数返回的是一个指针。指针函数的重点在于它是一个函

数,只不过这个函数的返回值是一个指针。

        静态分配的内存:指针函数可以返回一个指向静态分配内存的指针。

        动态分配的内存:更常见的情况是,指针函数返回一个指向动态分配(如使用malloc

callocreallocnew等)的内存的指针。

        栈上分配的内存:理论上,指针函数也可以返回一个指向栈上分配的内存的指针。

        全局或静态局部变量的地址:指针函数还可以返回一个指向全局变量或静态局部变量的指

针。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 解决:使用Charles查看本机的ip地址
  • 数学建模常见模型(下)
  • 【HTTP、Web常用协议等等】前端八股文面试题
  • 【 WPF 中常用的Brush类的简要介绍、使用方法和适用场景】
  • 微服务面试题
  • 安卓逆向(之)真机root(红米手机)
  • 什么是Java中的模板方法模式?请给出示例。Java中的设计模式有哪些?请列举几个并解释其应用场景。
  • .net core 管理用户机密
  • 加密技术.
  • 编程式路由跳转
  • (二十九)STL map容器(映射)与STL pair容器(值对)
  • 基于微信的热门景点推荐小程序的设计与实现(论文+源码)_kaic
  • Java设计模式之装饰器模式详细讲解和案例示范
  • Springboot3.x.x使用SpringSecurity6(一文包搞定)
  • 【数据分析预备】Numpy入门
  • [LeetCode] Wiggle Sort
  • 【翻译】babel对TC39装饰器草案的实现
  • 【刷算法】求1+2+3+...+n
  • interface和setter,getter
  • Invalidate和postInvalidate的区别
  • iOS 颜色设置看我就够了
  • js算法-归并排序(merge_sort)
  • Spark RDD学习: aggregate函数
  • SpiderData 2019年2月23日 DApp数据排行榜
  • Spring框架之我见(三)——IOC、AOP
  • 安装python包到指定虚拟环境
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 服务器之间,相同帐号,实现免密钥登录
  • 力扣(LeetCode)357
  • 浏览器缓存机制分析
  • 使用 @font-face
  • 世界编程语言排行榜2008年06月(ActionScript 挺进20强)
  • 手写一个CommonJS打包工具(一)
  • 小而合理的前端理论:rscss和rsjs
  • 原生JS动态加载JS、CSS文件及代码脚本
  • 责任链模式的两种实现
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​十个常见的 Python 脚本 (详细介绍 + 代码举例)
  • !!Dom4j 学习笔记
  • # Redis 入门到精通(九)-- 主从复制(1)
  • #1015 : KMP算法
  • #pragam once 和 #ifndef 预编译头
  • (1) caustics\
  • (2020)Java后端开发----(面试题和笔试题)
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (70min)字节暑假实习二面(已挂)
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (Java入门)学生管理系统
  • (二)pulsar安装在独立的docker中,python测试
  • (附源码)计算机毕业设计SSM保险客户管理系统
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (四)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (一)基于IDEA的JAVA基础12
  • (转)微软牛津计划介绍——屌爆了的自然数据处理解决方案(人脸/语音识别,计算机视觉与语言理解)...
  • (转载)OpenStack Hacker养成指南