【2022-05-21】linux 内核 plist (优先级链表)

本文详细介绍了优先级链表的数据结构,包括结构定义、节点操作如添加和删除。优先级链表通过两个链表分别维护优先级不同的节点和所有节点,确保高效查找和操作。在添加节点时,根据优先级进行排序;删除节点时,处理可能的优先级变化。此数据结构适用于需要快速按优先级访问的场景。
摘要由CSDN通过智能技术生成

数据结构定义

/* 优先级链表头 */
struct plist_head {
	struct list_head node_list;    
};

/* 优先级链表节点 */
struct plist_node {
	int			        prio;        /* 当前节点的优先级 */
	struct list_head	prio_list;   /* 用于串联优先级链表中不同优先级的节点 */
	struct list_head	node_list;   /* 用于串联优先级链表中所有节点 */
};

数据结构示意图

  • node_list 中包含该优先级链表的所有元素,按优先级由小到大的顺序排列,对于相同优先级的节点,按照先后顺序依次位置
  • prio_list 中仅包含链表中优先级不同的节点,同样按照优先级由小到大的顺序排列,主要用于在链表元素过多时,做快速的优先级查找

优先级链表

链表节点操作

添加节点

void plist_add(struct plist_node *node, struct plist_head *head)
{
	struct plist_node *first, *iter, *prev = NULL;
	struct list_head *node_next = &head->node_list;

	WARN_ON(!plist_node_empty(node));       // 检查新节点的 plist_node->node_list 未挂入链表
	WARN_ON(!list_empty(&node->prio_list)); // 检查新节点的 plist_node->prio_list 未挂入链表 

	if (plist_head_empty(head))  // 如果该链表为空,则为插入第一个节点,直接维护插入到 node_list 即可
		goto ins_node;

	first = iter = plist_first(head);  // 取出 node_list 的第一个元素

	do {
		if (node->prio < iter->prio) {
			node_next = &iter->node_list;
			break;
		}

		prev = iter;
		iter = list_entry(iter->prio_list.next,   // 通过 prio_list 找到下一个优先级的节点
				struct plist_node, prio_list);
	} while (iter != first);

	/*
	 * prev 为空,说明要插入新节点为所有节点中优先级最小的节点
	 * 如果 prev 不为空,且 prev 节点的优先级不等于新节点,说明来了全新优先级的节点
	 * 以上两种情况,都需要将新节点插入到 prio_list 中
	 */
	if (!prev || prev->prio != node->prio)  
		list_add_tail(&node->prio_list, &iter->prio_list);
	/*
	 * 不管新节点的优先级是否出现过,都要插入到 node_list 中,
	 * 前面的轮询操作,找到了新节点的 node_next 节点(比新节点优先级大的节点)
	 * 下一步要将新节点插入到 node_next 之前,
	 * 如果新节点为优先级最大的节点,则 node_next 为初始化的 &head->node_list
	 * 		对于双向链表,插入到 &head->node_list 之前,就是插入到链表尾部
	 */
ins_node:
	list_add_tail(&node->node_list, node_next);  // 将新节点,插入到 node_next 之前
}

删除节点

void plist_del(struct plist_node *node, struct plist_head *head)
{
	/* 
	 * 如果要删除的节点被挂在 prio_list 上了,则要做一些额外处理 
	 */
	if (!list_empty(&node->prio_list)) {
		/* 
		 * 如果要删除的节点不是 node_list 的尾节点,都要做进一步处理
		 */	
		if (node->node_list.next != &head->node_list) {
			struct plist_node *next;
			
			/* 这里首先取出待删除节点,在 node_list 中的下一个节点 */
			next = list_entry(node->node_list.next,
					struct plist_node, node_list);

			/* 如果该 next 节点不在 prio_list 上
			 * 要将其加到 prio_list 中待删除节点之后
			 * 
			 * 上面啰里啰嗦的检查一堆,都是为了这一步的操作,
			 * 本质上针对图示中要删除 Node1, Node4 这种场景
			 * 由于 node_list 中还有相同优先级的节点在,
			 * 所以删除这些节点时,要把其他相同优先级的节点,替换到 prio_list 上。
			 */
			if (list_empty(&next->prio_list))
				list_add(&next->prio_list, &node->prio_list);
		}
		/*
		 * 将节点从 prio_list 中删除
		 */
		list_del_init(&node->prio_list);
	}
	
	/*
	 * 将节点从 node_list 中删除
	 */
	list_del_init(&node->node_list);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值