【脚踢数据结构】内核链表

  • (꒪ꇴ꒪ ),Hello我是祐言QAQ
  • 我的博客主页:C/C++语言,Linux基础,ARM开发板,软件配置等领域博主🌍
  • 快上🚘,一起学习,让我们成为一个强大的攻城狮!
  • 送给自己和读者的一句鸡汤🤔:集中起来的意志可以击穿顽石!
  • 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏

        内核链表(Kernel Linked List)是操作系统内核中常用的一种数据结构,用于管理和维护一系列数据元素(节点)。它也是一种线性数据结构,其中每个节点包含了数据元素本身以及指向下一个节点的指针。内核链表在操作系统中广泛应用于管理进程、文件描述符、内存分配等诸多场景。

一、内核链表概述

        内核链表通常由一个特定的数据结构定义,该数据结构包含一个或多个指向链表中首个和最后一个节点的指针,以及其他用于操作和管理链表的属性。在C语言中,内核链表的定义示例:

//数据
typedef int Datatype;//节点(大结构体)
typedef struct Kernel_node
{Datatype data;			//数据域struct kernel_list list;	//指针域
}k_node;//指针节点(小结构体)
struct kernel_list {struct kernel_list *prev;//前驱指针struct kernel_list *next;//后继指针
};


        kernel_list结构体定义了一个内核链表的节点,其中prev指向前一个节点,next指向下一个节点,这一点在某种意义上与常规的双向循环链表一致,但是还是有区别的。我们再来看看之前所学习的双向循环链表的节点定义:

//数据
typedef int Datatype;//节点
typedef struct Node
{DataType data;		//数据域struct Node *prev;	//指针域:前驱指针struct Node *next;	//指针域:后继指针
}node;

        这样一对比,就会发现常规链表与内核链表最大的区别就是嵌套,内核链表的节点结构通常嵌套在某个容器数据结构中,可以理解为能单独对指针域进行操作,而不影响数据。

 所以这也暴露了常规链表的缺陷:

        (1)每一种应用中,节点都是特殊的,导致每一条链表都是特殊的,因此每一种链表的增删查改这些算法也都是特殊的;

        (2)当一个节点处于变化的数据结构网络中的时候,节点指针无法指向稳定不变的节点。

        这样的描述或许不能让你有直观的感觉,但当你往下看,真正去理解了内核链表再回过头来,你就会豁然开朗。

二、内核链表的原理

        内核链表的节点结构通常嵌套在某个容器数据结构中,以实现紧密关联。节点结构中至少包含两个指针,一个指向前一个节点,一个指向后一个节点。这样的设计使得在链表中插入、删除节点的操作更加高效,无需像数组那样移动大量元素。我们可以这样理解:

        (1)将链表的结构抽象出来,去除节点中的具体数据,只保留逻辑的双向指针,形成一条只包含逻辑的“纯粹的链表”。

        (2)将链表“寄宿”于具体的数据节点之中,使他贯穿这些节点,可以借助一定的方式通过“纯粹链表“的指针域得到数据节点。

            内核链表做到了将数据和逻辑(指针)分开,在红色方框内的这样只有前后两指针的链表形式被称为Linux标准双向循环链表

三、内核链表的操作

        在学习内核链表的操作之前,我们先要来了解一下list.h文件。list.h 是一个常见的头文件,通常用于实现内核链表(双向链表)的相关功能。在 Linux 内核中,list.h 提供了一种高效的方式来操作链表,包括插入、删除、遍历等操作。完整的该文件可从网上各处找到,接下来我们将只举例用到的结构体。

1. 节点声明

//数据
typedef int Datatype;//链表节点(大结构体)
typedef struct Kernel_node
{Datatype data;			//数据域struct list_head list;	//指针域
}k_node;

         list_head 是在list.h中已经定义好的指针域部分,我们可以将其称之为小结构体,用它来构建“纯粹链表”。

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

2.初始化链表

//初始化链表
k_node *init_kernel_list()
{k_node *head = malloc(sizeof(k_node));if (head==NULL){perror("malloc");exit(0);}else{		INIT_LIST_HEAD(&(head->list));//带参宏,初始化小结构体里面的这两个指针成员}return head;
}

        INIT_LIST_HEAD也是list.h中已经定义好的带参宏,原型如下:

#define INIT_LIST_HEAD(ptr) do { \(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)

3.创建节点

//创建节点
k_node *create_kernel_node(Datatype data)
{k_node *new = malloc(sizeof(k_node));if (new != NULL){new->data = data;new->list.next = NULL;new->list.prev = NULL;}return new;
}

4.遍历链表


//遍历链表
void display(k_node *head)
{struct list_head *pos = NULL;k_node *Node = NULL;list_for_each(pos, &(head->list)){Node = list_entry(pos, k_node, list);printf("%d ", Node->data);}printf("\n");
}

        其中list_for_each和list_entry也是在list.h中定义好的带参宏:


//遍历链表的for循环,每一次循环体内得到的就是一个小结构体指针
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); \
pos = pos->next)//小结构体指针ptr,大结构体类型type,小结构体在大结构体内部的成员名
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

        做完这些操作,我们就可以通过一个实例来验证我们的操作是否正确,我们可以创建一个10个节点的链表并打印。

         实例代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "list.h"//数据
typedef int Datatype;//链表节点
typedef struct Kernel_node
{Datatype data;			//数据域struct list_head list;	//指针域
}k_node;//初始化链表
k_node *init_kernel_list()
{k_node *head = malloc(sizeof(k_node));if (head==NULL){perror("malloc");exit(0);}else{INIT_LIST_HEAD(&(head->list));}return head;
}//创建节点
k_node *create_kernel_node(Datatype data)
{k_node *new = malloc(sizeof(k_node));if (new != NULL){new->data = data;new->list.next = NULL;new->list.prev = NULL;}return new;
}//遍历链表
void display(k_node *head)
{struct list_head *pos = NULL;k_node *Node = NULL;list_for_each(pos, &(head->list)){Node = list_entry(pos, k_node, list);printf("%d ", Node->data);}printf("\n");
}int main(int argc, char const *argv[])
{k_node *head = init_kernel_list();for (int i = 0; i < 10; ++i){k_node *new = create_kernel_node(i+1);//插入节点到链表list_add_tail(&(new->list), &(head->list));}//遍历链表display(head);return 0;
}

        更安全的遍历列表方法补充:

void display2(k_node *head)
{struct list_head *pos = NULL;struct list_head *n = NULL;//为了防止你在遍历的时候,删除节点,找不到下一个节点的地址k_node *Node;list_for_each_safe(pos, n, &(head->list)){Node = list_entry(pos, k_node, list);printf("%d ", Node->data);}printf("\n");
}void display3(k_node *head)
{k_node *pos = NULL;list_for_each_entry(pos, &(head->list), list){printf("%d ", pos->data);}printf("\n");
}void display4(k_node *head)
{k_node *pos = NULL;k_node *n = NULL;list_for_each_entry_safe(pos, n, &(head->list), list){printf("%d ", pos->data);}printf("\n");
}

5.删除节点

void delete_node(k_node *head, Datatype data)
{struct list_head *pos = NULL;struct list_head *n = NULL;//为了防止你在遍历的时候,删除节点,找不到下一个节点的地址k_node *Node = NULL;list_for_each_safe(pos, n, &(head->list)){Node = list_entry(pos, k_node, list);if (Node->data == data){list_del(&(Node->list));}		}
}

6.移动节点

void move_node_head(k_node *head, Datatype d1, Datatype d2)
{k_node *p1 = find_node(head, d1);k_node *p2 = find_node(head, d2);if (p1==NULL || p2==NULL){return;}list_move(&(p1->list), &(p2->list));
}void move_node_tail(k_node *head, Datatype d1, Datatype d2)
{k_node *p1 = find_node(head, d1);k_node *p2 = find_node(head, d2);if (p1==NULL || p2==NULL){return;}list_move_tail(&(p1->list), &(p2->list));//尾插
}
	//移动节点move_node_head(head, 1, 7);display4(head);

7.判断是否为空链表

k_node *list_empty(k_node *head)
{return head->next == head;
}

8.合并链表

int main(int argc, char const *argv[])
{k_node *head = init_kernel_list();for (int i = 0; i < 10; ++i){k_node *new = create_kernel_node(i+1);//插入节点到链表list_add_tail(&(new->list), &(head->list));//尾插}display4(head);k_node *head1 = init_kernel_list();for (int i = 10; i < 15; ++i){k_node *new = create_kernel_node(i+1);// 插入节点到链表list_add_tail(&(new->list), &(head1->list));//尾插}display4(head1);if (list_empty(&(head1->list))){printf("这是空链表\n");}else{printf("非空,可合并\n");}// head的头节点下面的第一个节点的前驱指针会断开,head的尾节点的后继指针会断开,head后面不能在使用list_splice_init(&(head->list), &(head1->list));display4(head1);return 0;
}

        运行结果:

 

留个作业吧

        用内核链表创建一个数据集合,初始包含数据:1 2 3 4 5 6 7 8 9 ,将其重新排列成:9 7 5 3 1 2 4 6 8 (奇数降序,偶数升序)并显示出来。

        答案评论区见!

四、总结

        与一般链表相比,内核链表有一些特点和优势:

        1.容器结构体: 在内核链表中,节点通常嵌套在某个数据结构中,这个数据结构即所谓的容器结构体。这种设计可以让数据元素和链表节点紧密关联,方便数据的操作和管理。

        2.稳定性和预测性: 内核链表在操作系统内核中广泛应用,而且内核链表的实现通常被优化为在各种情况下都能够稳定和预测地工作,不容易受到异常情况的影响。

        3.特定操作: 内核链表通常提供一系列针对特定操作的函数,如插入、删除、遍历等。这些函数经过精心的设计和优化,能够高效地处理链表操作。

        总之,内核链表是操作系统内核中的一种重要数据结构,用于高效地管理各种资源和数据。它与一般链表相比,更注重稳定性和性能优化,以满足操作系统的特定需求。

        更多C语言Linux系统ARM板实战数据结构相关文章,关注专栏:

   手撕C语言

            玩转linux

                    脚踢数据结构

                            6818(ARM)开发板实战

📢写在最后

  • 今天的分享就到这啦~
  • 觉得博主写的还不错的烦劳 一键三连喔~
  • 🎉感谢关注🎉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/91289.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ide internal errors【bug】

ide internal errors【bug】 前言版权ide internal errors错误产生相关资源解决1解决2 设置虚拟内存最后 前言 2023-8-15 12:36:59 以下内容源自《【bug】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN日星月云 博客主页是h…

8.15号经典模型复习笔记

文章目录 Deep Residual Learning for Image Recognition(CVPR2016)方法 Densely Connected Convolutional Networks&#xff08;CVPR2017&#xff09;方法 EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks&#xff08;ICML2019&#xff09;方法 Re…

深入了解 Rancher Desktop 设置

Rancher Desktop 设置的全面概述 Rancher Desktop 拥有方便、强大的功能&#xff0c;是最佳的开发者工具之一&#xff0c;也是在本地构建和部署 Kubernetes 的最快捷方式。 本文将介绍 Rancher Desktop 的功能和特性&#xff0c;以及 Rancher Desktop 作为容器管理平台和本地…

KU Leuven TU Berlin 推出“RobBERT”,一款荷兰索塔 BERT

荷兰语是大约24万人的第一语言&#xff0c;也是近5万人的第二语言&#xff0c;是继英语和德语之后第三大日耳曼语言。来自比利时鲁汶大学和柏林工业大学的一组研究人员最近推出了基于荷兰RoBERTa的语言模型RobBERT。 谷歌的BERT&#xff08;来自Transformers的B idirectional …

vant van-tabs van-pull-refresh van-list 标签栏+上拉加载+下拉刷新

<template><div class"huibj"><div class"listtab"><!--顶部导航--><div class"topdh"><topnav topname"余额明细"></topnav></div><!--Tab 标签--><van-tabs v-model"…

linux 查看文件被那个进程所调用

使用lsof 命令 显示文件被哪个进程所占用 lsof /var/log/messagesCOMMAND&#xff1a;进程的名称PID&#xff1a;进程标识符USER&#xff1a;进程所有者FD&#xff1a;文件描述符&#xff0c;应用程序通过文件描述符识别该文件。如cwd、txt等TYPE&#xff1a;文件类型&#…

设计HTML5文档结构

定义清晰、一致的文档结构不仅方便后期维护和拓展&#xff0c;同时也大大降低了CSS和JavaScript的应用难度。为了提高搜索引擎的检索率&#xff0c;适应智能化处理&#xff0c;设计符合语义的结构显得很重要。 1、头部结构 在HTML文档的头部区域&#xff0c;存储着各种网页元…

力扣:63. 不同路径 II(Python3)

题目&#xff1a; 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”&#xff09;。 现在考虑网格中有障碍物。那么从…

淘宝搜索店铺列表API:关键字搜索店铺信息 获取店铺主页 店铺所在地 服务评级

接口名称&#xff1a;item_search_seller 基本功能介绍 该API可以通过传入关键字&#xff0c;获取到淘宝商城的店铺列表&#xff0c;支持翻页显示。指定参数page获取到指定页的数据。返回的店铺信息包括&#xff1a;店铺名、店铺ID、店铺主页、宝贝图片、掌柜名字、店铺所在地…

Python爬虫的应用场景与技术难点:如何提高数据抓取的效率与准确性

作为专业爬虫程序员&#xff0c;我们在数据抓取过程中常常面临效率低下和准确性不高的问题。但不用担心&#xff01;本文将与大家分享Python爬虫的应用场景与技术难点&#xff0c;并提供一些实际操作价值的解决方案。让我们一起来探索如何提高数据抓取的效率与准确性吧&#xf…

【广州华锐互动】物联网工程VR虚拟课件有哪些特色?

物联网工程VR虚拟课件由广州华锐互动制作&#xff0c;是一种利用虚拟现实技术&#xff0c;将物联网的概念和应用场景通过模拟的方式呈现给学生的教学工具。相比传统的教学方式&#xff0c;物联网工程VR虚拟课件具有以下特色&#xff1a; 1.交互性强 物联网工程VR虚拟课件可以让…

选择最适合自己的NIO, 一探流技术

目录 一、Channel1、FileChannel代码示例2、DatagramChannel代码示例3、SocketChannel 和 ServerSocketChannel代码示例 二、Buffer1、ByteBuffer示例代码2、CharBuffer示例代码3、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer 等示例代码 三、Selector1、S…

转行软件测试四个月学习,第一次面试经过分享

我是去年上半年从销售行业转行到测试的&#xff0c;从销售公司辞职之后选择去培训班培训软件测试&#xff0c;经历了四个月左右的培训&#xff0c;在培训班结课前两周就开始投简历了&#xff0c;在结课的时候顺利拿到了offer。在新的公司从事软件测试工作已经将近半年有余&…

贝锐蒲公英:助力企业打造稳定高效的智能安防监控网络

随着技术的快速发展和物联网的普及&#xff0c;企业面临着许多安全威胁和风险&#xff0c;如盗窃、入侵、信息泄露等&#xff0c;企业需要建立安防监控系统来保护其资产、员工和业务运营的安全。 然而&#xff0c;企业在搭建安防监控系统的过程中&#xff0c;可能会面临一些难…

手机的发展历史

目录 一.人类的通信方式变化 二.手机对人类通信的影响 三.手机的发展过程 四.手机对现代人的影响 一.人类的通信方式变化 人类通信方式的变化是一个非常广泛和复杂的话题&#xff0c;随着技术的进步和社会的发展&#xff0c;人类通信方式发生了许多重大的变化。下面是一些主…

python技术栈 之 单元测试中mock的使用

一、什么是mock&#xff1f; mock测试就是在测试过程中&#xff0c;对于某些不容易构造或者不容易获取的对象&#xff0c;用一个虚拟的对象来创建以便测试的测试方法。 二、mock的作用 特别是开发过程中上下游未完成的工序导致当前无法测试&#xff0c;需要虚拟某些特定对象…

fiddler抓包工具的用法以及抓取手机报文定位bug

前言&#xff1a; fiddler抓包工具是日常测试中常用的一种bug定位工具 一 抓取https报文步骤 使用方法&#xff1a; 1 首先打开fiddler工具将证书导出 点击TOOLS------Options------Https-----Actions---选中第二个选项 2 把证书导出到桌面后 打开谷歌浏览器 设置---高级…

数据结构算法--2 冒泡排序,选择排序,插入排序

基础排序算法 冒泡排序 思想就是将相邻元素两两比较&#xff0c;当一个元素大于右侧相邻元素时&#xff0c;交换他们的位置&#xff0c;小于右侧元素时&#xff0c;位置不变&#xff0c;最终序列中的最大元素&#xff0c;像气泡一样&#xff0c;到了最右侧。 这时冒泡排序第一…

分类预测 | MATLAB实现CNN-BiGRU-Attention多输入分类预测

分类预测 | MATLAB实现CNN-BiGRU-Attention多输入单输出分类预测 目录 分类预测 | MATLAB实现CNN-BiGRU-Attention多输入单输出分类预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 Matlab实现CNN-BiGRU-Attention多特征分类预测&#xff0c;卷积双向门控循环…

SpringBoot系列---【SpringBoot在多个profiles环境中自由切换】

SpringBoot在多个profiles环境中自由切换 1.在resource目录下新建dev&#xff0c;prod两个目录&#xff0c;并分别把dev环境的配置文件和prod环境的配置文件放到对应目录下&#xff0c;可以在配置文件中指定激活的配置文件&#xff0c;也可以默认不指定。 2.在pom.xml中最后位置…