Linux双向链表(五)——简单使用示例

本文通过一个简单的示例展示了如何使用Linux环境下双链表API进行基本操作,包括创建、遍历、查找、删除及插入等,并提供了完整的源代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

针对前面解析的双链表API,本文做一个简单使用实例。正所谓:千言万语,不如举一个好例子!不过,这个例子很简单,没有做安全机制,只是简单的使用,而且是链表的常用API,希望对读者能起到抛砖引玉的作用!

list_module.c

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

static char *str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
//定义一个用户数据类型
struct info_struct{
		char name[32];
		unsigned int age;

		struct list_head entry;
};

//双链表表头
static LIST_HEAD(head);

//遍历链表
static void print_list(void)
{
	struct info_struct *pos;
	struct list_head *list_head = &head;
	int i = 0;

	list_for_each_entry(pos, list_head, entry)
	{
		++i;
		printk(KERN_INFO"name:%s\tage:%d\n", pos->name, pos->age);
	}
	printk(KERN_INFO"sum:%d\n", i);
}

//销毁链表
static void destroy_list(void)
{
	struct info_struct *pos, *n;
	struct list_head *list_head = &head;

	list_for_each_entry_safe(pos, n, list_head, entry)
	{
		list_del(&pos->entry);
		kfree(pos);
	}
	INIT_LIST_HEAD(list_head);
}

static void creat_list(void)
{
	//向链表添加元素
	int i;
	struct info_struct *buff;

	for(i = 0; i < 10; ++i)
	{
		buff = kmalloc(sizeof(struct info_struct), GFP_ATOMIC);
		if(!buff)
		{
			printk(KERN_ALERT"ERROR:kmalloc memory fail!\n");
			break;
		}

		//初始化数据
		memcpy(buff->name, str + i, 17);
		buff->name[17] = 0;
		buff->age = 26 - i;

		//放入双链表
		list_add(&buff->entry, &head);
	}
}

//查找并删除元素
static void search_and_del_entry(unsigned int age)
{
	struct info_struct *pos, *n;
	
	list_for_each_entry_safe(pos, n, &head, entry)
	{
		if(pos->age == age)
		{
			list_del(&pos->entry);
			kfree(pos);
			printk(KERN_INFO"age:%d delete!\n", age);
		}
	}
}

//查找位置并插入之前
static void search_and_insert_entry(unsigned int age, struct info_struct *buff)
{
	struct info_struct *pos;
	
	list_for_each_entry(pos, &head, entry)
	{
		if(pos->age == age)
		{
			list_add_tail(&buff->entry, &pos->entry);
			printk(KERN_INFO"age:%d prev insert an entry!\n", age);
			break;
		}
	}
}

static int __init list_init(void)
{
	struct info_struct *buff;

	creat_list();
	print_list();

	search_and_del_entry(18);
	print_list();

	buff = kmalloc(sizeof(struct info_struct), GFP_ATOMIC);
	if(buff)
	{
		memcpy(buff->name, "Hello List, I'm newer!", 22);
		buff->name[22] = 0;
		buff->age = 0;
		search_and_insert_entry(24, buff);
		print_list();
	}
	printk(KERN_INFO"list_module insert successfully!\n");
	return 0;
}

static void __exit list_exit(void)
{
	destroy_list();
	printk(KERN_INFO"list_module remove successfully!\n");
}
module_init(list_init);
module_exit(list_exit);
MODULE_LICENSE("GPL");

Makefile

ifneq ($(KERNELRELEASE),)
	obj-m :=list_module.o
else
	KERNELDIR := /lib/modules/$(shell uname -r)/build
	PWD := $(shell pwd)
default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

endif

步骤

make(编译模块)->insmod list_module.ko(root权限添加模块)->dmesg(查看运行结果)->rmmod list_module(root权限卸载模块)over!


### Nachos 中双向链表的实现与用法 在操作系统模拟环境 Nachos 中,双向链表是一种重要的数据结构,用于管理各种资源和对象。以下是关于其实现与用法的具体分析: #### 1. 双向链表的基本概念 双向链表是由节点组成的线性数据结构,其中每个节点包含三个部分:前驱指针、数据域以及后继指针。这种设计使得可以方便地从前向后或者从后向前遍历链表。 在 Nachos 的实现中,通常会利用 C/C++ 提供的指针操作来构建链表节点,并通过宏定义简化复杂度[^2]。例如,在 Linux 内核风格的链表实现中,`container_of` 宏被广泛应用于定位某个字段所属的对象实例[^3]。 #### 2. `container_of` 宏的作用 为了更高效地访问链表中的元素及其关联的数据结构,Nachos 借鉴了 Linux 内核的设计思路,引入了 `container_of` 宏。该宏的主要功能是从成员变量地址推导出包含它的父结构体地址。其实现逻辑如下所示: ```c #define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) *mptr = (ptr); \ (type *)((char *)mptr - offsetof(type, member)); }) ``` 上述代码片段展示了如何计算偏移量并返回完整的结构体指针。这里的关键在于 `offsetof` 函数能够动态获取指定成员相对于结构体起始位置的距离。 #### 3. 在 Nachos 中的应用场景 - **线程管理** 使用双向链表存储待调度的任务列表(即就绪队列)。每当创建新线程时,将其加入到对应优先级级别的子队列;当某一线程完成运行周期后,则重新调整它所在的位置以支持不同的调度策略[^1]。 - **内存分配跟踪** 对于虚拟页帧这样的有限资源来说,也需要借助类似的机制记录哪些页面当前处于忙碌状态以及它们分别属于哪个进程等等信息[^4]。 #### 4. 示例代码说明 假设我们需要维护一个简单的线程等待队列,那么可以通过以下方式初始化及操作此类容器: ```cpp // 定义单个节点所对应的结构体形式 struct ThreadNode { struct ListElement listElem; // 链接其他同类项的部分 int priorityLevel; // 自定义属性之一 }; class Scheduler { // 调度类声明 public: void AddToQueue(Thread* thr); private: List readyList_; // 存储所有候选者的集合 }; void Scheduler::AddToQueue(Thread* thr){ ThreadNode* newNode=new ThreadNode(); newNode->priorityLevel=thr->getPriority(); readyList_.Append(&(newNode->listElem)); } ``` 以上伪代码片段演示了一个典型例子——将新建好的线程插入至全局可用池当中去的过程。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值