双链表的分析

一、引言-机制与策略分离的现状

机制(mechanism),即需要提供什么样的功能;策略(policy),即怎么样实现这些功能。操作系统是处于硬件和应用程序之间的一个中间层,由此可知,操作系统同时承担了两个角色,一个角色是对硬件资源的管理者,另一个角色则是对应用程序的服务者。对于操作系统和硬件的关系,硬件提供了许多功能,也就是提供了机制,而操作系统屏蔽掉了硬件的细节,给予了具体的实现,也就是提供了策略;对于操作系统和应用程序的关系,操作系统提供了许多功能,也就是提供了机制,而应用程序屏蔽了操作系统的细节,给予了具体的实现,也就是提供了策略。这就是机制与策略分离的思想。此思想不应该局限于分层的概念中,即使同一层次的编程问题,也是可以使用机制与策略分离的思想。现在使用的机制与策略分离思想的方法有将应用按照一个库来编写,还可以将应用程序分为协作的前端和后端进程。具体来说,就如内核list.h中提供了很多双向链表的方法,我们可以进行创建、删除链表等操作。1

二、双向链表机制的设计思想分析

双向链表,从名字上理解就是双向的,指的是各节点之间的逻辑关系是双向的,可向前或向后查找。双向链表可以在一个节点的前边和后边添加一个新的结点。双向链表可以实现自我删除,删除节点时只需遍历链表找到要删除的结点,将该结点从表中摘除即可。双向链表通过遍历找到存储该数据元素的结点,直接更改其数据域即可。双向链表虽然消耗的内存更多,但是它的性能更好。
双向链表各结点包含以下三部分:
1.指针域(prior):用于指向当前结点的直接前驱结点;
2.数据域:用于存储数据元素;
3.指针域(next):用于指向当前结点的直接后继结点。
在这里插入图片描述

图1.双向链表结构示意图
(1)C语言中,一个基本的双向链表定义:
struct my_list{
void *mydata;
struct my_list *next;
struct my_list *prev;
}

(2)Linux内核对双向链表的定义:

struct list_head{
void list_head *next,*prev;
} ;
struct my_list{
void *mydata;
struct list_head list;
}

Linux内核把双向链表作为一个基本类型:
(1)若减少一个指针域,就退化成单链表;
(2)若只能对链表的首尾进行插入或删除操作,就演变为队结构;
(3)若只能对链表的头进行插入或删除操作,就退化为栈结构;
(4)若前驱和后继表示左右孩子,则演变为一棵二叉树。

三、对其中的核心函数一个或者多个进行拆解,进行深入分析并给出实验结果和分析(可以在用户态下来实验)

3.1 结构体的初始化

void INIT_LIST_HEAD(struct list_head *list)
{
    list->next = list;
    list->prev = list;
}

初始化结构体list,使它的next和prev指针都指向它自己,这是一个链表的初始状态,判断一个链表是否为空的方法就是判断它的next是否指向它本身。

3.2 增加结点

void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next)
{
    next->prev = new;
    new->next = next;
    new->prev = prev;
    prev->next = new;
 }

Linux中对于相同的代码做了很好的封装,头插法和尾插法的内部实现相同,只是插入的结点位置不同,内核中的链表为双向循环链表,所以头插法是将名字为new的结点插入到head结点和head->next结点之间;同样的,尾插法是将名字为new的结点插入到head->prev结点和head之间。

void list_add_tail(struct list_head *new, struct list_head *head)
{
    __list_add(new, head->prev, head);
}

本次实验采用了尾插法。

3.3 打印链表

本次打印链表首先通过list_for_each(pos,&head->list)遍历链表,其中通过list_entry(pos,NumNode,list)得到相对应的结点,依次打印。

3.4 删除结点

void __list_del(struct list_head * prev,struct list_head * next)
{
    next->prev=prev;
    prev->next=next;
}
void list_del(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
    entry->next = LIST_POISON1;
    entry->prev = LIST_POISON2;
}

删除结点的核心操作是__list_del函数,该函数是表示将entry结点的前一个和后一个结点建立联系的步骤。在这又是体现linux封装思想的一个地方,如果你想要删除名字为entry的结点,只需将entry的前一个和后一个结点作为参数传给这个函数即可。
List_del函数把entry的prev和next指针分别设为LIST_POSITION2和LIST_PSITION1两个特殊值,这样设置是为了保证不在链表中的结点项不可访问(对LIST_POSITION2和LIST_PSITION1的访问都将引起页故障)。

3.5 实验结果

在这里插入图片描述
2

四、双向链表机制的应用(编写内核模块,并给出调试结果)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3 4

五、结论

(1)通过本次的实验,我对linux内核模块有了更深的了解,对list.h有了更深的理解,内核模块的确方便,从现实情况看,内核模块的小瑕疵和优点比起来简直不值一提,内核模块的引入,其实也是一种“机制与策略分离”的思想。
(2)内核文件list.h里的各种函数和宏真的非常巧妙,封装性非常的好,内核提供的一组宏,以方便对链表进行管理。


  1. 引用博客第一部分 ↩︎

  2. 用户态双链表实现 ↩︎

  3. 内核模块调试 ↩︎

  4. 刘大师神作-一文搞懂 ↩︎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值