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

  • (꒪ꇴ꒪ ),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)开发板实战

📢写在最后

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Remote Sensing,2023 | 基于SBL的分布式毫米波相干雷达成像的高效实现

Remote Sensing,2023 | 基于SBL的分布式毫米波相干雷达成像的高效实现 注1&#xff1a;本文系“无线感知论文速递”系列之一&#xff0c;致力于简洁清晰完整地介绍、解读无线感知领域最新的顶会/顶刊论文(包括但不限于 Nature/Science及其子刊; MobiCom, Sigcom, MobiSys, NSDI…

WEB集群——LVS-DR 群集、nginx负载均衡

1、基于 CentOS 7 构建 LVS-DR 群集。 2、配置nginx负载均衡。 一、 LVS-DR 群集 1、LVS-DR工作原理 LVS-DR&#xff08;Linux Virtual Server Director Server&#xff09; 名称缩写说明 虚拟IP地址(Virtual IP Address) VIPDirector用于向客户端计算机提供服务的IP地址真实…

TCP网络服务器设计

最近设计了一个网络服务器程序&#xff0c;对于4C8G的机器配置&#xff0c;TPS可以达到5W。业务处理逻辑是简单的字符串处理。服务器接收请求后对下游进行类似广播的发送。在此分享一下设计方式&#xff0c;如果有改进思路欢迎大家交流分享。 程序运行在CentOS7.9操作系统上&a…

【uniapp】uniapp设置安全区域:

文章目录 一、效果图:二、实现代码: 一、效果图: 二、实现代码: {"path": "pages/index/index","style": {"navigationStyle": "custom","navigationBarTextStyle": "white","navigationBarTitle…

Unity之ShaderGraph 节点介绍 UV节点

UV节点 Flipbook&#xff08;翻页或纹理帧动画&#xff09; Polar Coordinates&#xff08;将输入 UV 的值转换为极坐标。&#xff09; Radial Shear&#xff08;径向剪切变形&#xff09; Rotate&#xff08;将UV 的值旋转&#xff09; Spherize&#xff08;鱼眼镜头的球形变…

CentOS7连接网络

1.下载centos7镜像文件 2.安装centos7 3.修改网卡,ens33. 注意: 这里使用的是dhcp,设置IPADDR192.168.31.64一方面是为了后面使用crt或者MobaXterm连接,另一方面它和windows电脑的网卡要一致.这样才可以连接到网络.win r,输入cmd,打开命令窗口输入ipconfig.可以看到IPv4: 102…

windows安装apache-jmeter-5.6.2教程

目录 一、下载安装包&#xff08;推荐第二种&#xff09; 二、安装jmeter 三、启动jmeter 一、下载安装包&#xff08;推荐第二种&#xff09; 1.官网下载&#xff1a;Apache JMeter - Download Apache JMeter 2.百度云下载&#xff1a;链接&#xff1a;https://pan.baidu.…

FLStudio21水果最新中文版升级下载

FLStudio21最新中文版是一款非常专业的后期编曲音频处理软件&#xff0c;对于音乐编辑处理的领域内的人而言&#xff0c;是非常能够满足需求的一款工具。FL Studio21拥有强大且专业的创作工具&#xff0c;这是先进的创作工具&#xff0c;让你的音乐突破想象力的限制。FL Studio…

数据结构-栈的实现(C语言版)

前言 栈是一种特殊的线性表&#xff0c;只允许在固定的一端进行插入和删除的操作&#xff0c;进行数据插入和删除的一端叫做栈顶&#xff0c;另一端叫做栈底。 栈中的数据元素遵循后进先出的的原则。 目录 1.压栈和出栈 2. 栈的实现 3.测试代码 1.压栈和出栈 压栈&#xff…

C数据结构与算法——无向图(邻接矩阵) 应用

实验任务 (1) 掌握图的邻接矩阵存储及基本算法&#xff1b; (2) 掌握该存储方式下的DFS和BFS算法。 实验内容 实现图的邻接矩阵存储结构实现基于邻接矩阵的相关算法及遍历算法 实验源码 #include <malloc.h> #include <stdio.h>#define MAXSIZE 1000 #define …

(6)(6.3) 复合连接的故障处理

文章目录 6.3 复合连接的故障处理 6.4 相关话题 6.3 复合连接的故障处理 带有 F7 或 H7 处理器并有 CAN 接口的自动驾驶仪使用的固件提供两个 USB 接口。一个用于正常的 MAVLink 连接&#xff0c;一个用于 SLCAN 串行连接到 CAN 接口进行配置和固件更新。这被称为复合型 USB…

WebRTC | 实现数据流的一对一通信

目录 一、浏览器对WebRTC的支持 二、MediaStream与MediaStreamTrack 三、RTCPeerConnection 1. RTCPeerConnection与本地音视频数据绑定 2. 媒体协商SDP 3. ICE &#xff08;1&#xff09;Candidate信息 &#xff08;2&#xff09;WebRTC收集Candidate &#xff08;3&…

常见的几大排序问题

前言&#xff1a;排序问题&#xff0c;是数据结构中的一大重要的组成板块&#xff0c;很多的面试机试中都会多多少少的涉及到排序问题&#xff0c;之前在上数据结构的那个学期整理过排序问题&#xff0c;不过大都是囫囵吞枣&#xff0c;不求甚解&#xff0c;今天&#xff0c;我…

如果你需要使用重试机制,请使用Spring官方的Spring Retry

Spring Retry 是 Spring Framework 中的一个模块&#xff0c;提供了一种简单的方式来在应用程序中实现重试机制。 在应用程序中&#xff0c;如果遇到了一些不可避免的错误&#xff0c;比如网络连接失败、数据库连接失败等&#xff0c;我们通常需要对这些错误进行重试&#xff…

SQL 语句解析过程详解

SQL 语句解析过程详解&#xff1a; 1&#xff0e;输入SQL语句 2&#xff0e;词法分析------flex 使用词法分析器&#xff08;由Flex生成&#xff09;将 SQL 语句分解为一个个单词&#xff0c;这些单词被称为“标记“。标记包括关键字、标识符、运算符、分隔符等。 2.1 flex 原…

环保行业如何开发废品回收微信小程序

废品回收是近年来受到越来越多人关注的环保行动。为了推动废品回收的普及和方便&#xff0c;我们可以利用微信小程序进行制作&#xff0c;方便人们随时随地参与废品回收。 首先&#xff0c;我们需要注册并登录乔拓云账号&#xff0c;并进入后台。乔拓云是一个提供微信小程序制作…

CAS服务端入门使用实践

CAS服务端入门使用实践 一、前言 1.简介 CAS 是一个企业多语言单点登录解决方案&#xff0c;支持大量附加身份验证协议和功能&#xff0c;满足身份验证和授权需求的综合平台。 2.环境 Windows 10JDK 1.8git version 2.41.0.windows.3Tomcat 9.0.78Maven 3.5.3cas-overlay-…

SpringMVC的架构有什么优势?——视图与模型(二)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

String(字符串)

1、String概述 java.lang.String类代表字符串&#xff0c;Java程序中的所有字符串文字&#xff08;例如“abc”&#xff09;都为此类的对象。 1.1、String的注意点 字符串的内容是不会发生改变的&#xff0c;它的对象在创建后不能被更改。 1.2、总结 String是Java定义好的一个类…

【计算机组成原理】24王道考研笔记——第四章 指令系统

第四章 指令系统 一、指令系统 指令是指示计算机执行某种操作的命令&#xff0c;是计算机运行的最小功能单位。一台计算机的所有指令的集合构成该 机的指令系统&#xff0c;也称为指令集。 指令格式&#xff1a; 1.1分类 按地址码数目分类&#xff1a; 按指令长度分类&…