链表(循环双向链表)是Linux内核中最简单、最常用的一种数据结构。

1、链表的定义

struct list_head {

struct list_head *next, *prev;

}

这个不含数据域的链表,可以嵌入到任何数据结构中,例如可按如下方式定义含有数据域的链表:

struct my_list {

void  * mydata;

struct list_head  list;

} ;

2、链表的声明和初始化宏

struct list_head 只定义了链表结点,并没有专门定义链表头.那么一个链表结点是如何建立起来的?

内核代码 list.h 中定义了两个宏:

#defind  LIST_HEAD_INIT(name)    { &(name), &(name) }      //仅初始化

#defind  LIST_HEAD(name)     struct list_head  name = LIST_HEAD_INIT(name)  //声明并初始化

如果要声明并初始化链表头mylist_head,则直接调用:LIST_HEAD(mylist_head),之后,

mylist_head的next、prev指针都初始化为指向自己。这样,就有了一个带头结点的空链表。

判断链表是否为空的函数:

static inline int list_empty(const struct list_head  * head) {

return head->next  ==  head;

}    //返回1表示链表为空,0表示不空

3、在链表中增加一个结点

(内核代码中,函数名前加两个下划线表示内部函数)

static inline 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 ;

}

list.h 中增加结点的两个函数为:

(链表是循环的,可以将任何结点传递给head,调用这个内部函数以分别在链表头和尾增加结点)

static inline void list_add(struct list_head *new, struct llist_head *head)

{

__list_add(new, head, head -> next) ;

}

static inline void list_add_tail(struct list_head 8new, struct list_head *head)

{

__list_add(new, head -> prev, head) ;

}

附:给head传递第一个结点,可以用来实现一个队列,传递最后一个结点,可以实现一个栈。

static 加在函数前,表示这个函数是静态函数,其实际上是对作用域的限制,指该函数作用域仅局限

于本文件。所以说,static 具有信息隐蔽的作用。而函数前加 inline 关键字的函数,叫内联函数,表

示编译程序在调用这个函数时,立即将该函数展开。

4、 遍历链表

list.h 中定义了如下遍历链表的宏:

#define   list_for_each(pos, head)    for(pos = (head)-> next ;  pos != (head) ;  pos = pos -> next)

这种遍历仅仅是找到一个个结点的当前位置,那如何通过pos获得起始结点的地址,从而可以引用结

点的域?list.h 中定义了 list_entry 宏:

#define   list_entry( ptr, type, member )  \

( (type *) ( (char *) (ptr)  - (unsigned long) ( &( (type *)0 )  ->  member ) ) )

分析:(unsigned long) ( &( (type *)0 )  ->  member ) 把 0 地址转化为 type 结构的指针,然后获取该

结构中 member 域的指针,也就是获得了 member 在type 结构中的偏移量。其中  (char *) (ptr) 求

出的是 ptr 的绝对地址,二者相减,于是得到 type 类型结构体的起始地址,即起始结点的地址。

5、链表的应用

一个用以创建、增加、删除和遍历一个双向链表的Linux内核模块

点击(此处)折叠或打开

#include

#include

#include

#include

MODULE_LICENCE("GPL");

MODULE_AUTHOR("LUOTAIJIA");

#define N 10

struct numlist {

int num;

struct list_head list;

};

struct numlist numhead;

static int __init doublelist_init(void)

{

//初始化头结点

struct numlist * listnode; //每次申请链表结点时所用的指针

struct list_head * pos;

struct numlist * p;

int i;

printk("doublelist is starting...\n");

INIT_LIST_HEAD(&numhead.list);

/*

* static inline void INIT_LIST_HEAD(struct list_head *list)

* {

* list->next = list;

* list->prev = list;

* }

*/

//建立N个结点,依次加入到链表当中

for (i=0; i

listnode = (struct numlist *)kmalloc(sizeof(struct numlist), GFP_KERNEL);

//void *kmalloc(size_t size, int flages)

//分配内存,size 要分配内存大小,flags 内存类型

listnode->num = i+1;

list_add_tail(&listnode->list, &numhead.list);

printk("Node %d has added to the doublelist...\n", i+1);

}

//遍历链表

i = 1;

list_for_each(pos, &numhead.list) {

p = list_entry(pos, struct numlist, list);

printk("Node %d's data: %d\n", i, p->num);

i++;

}

return 0;

}

static void __exit doublelist_exit(void)

{

struct list_head *pos, *n;

struct numlist *p;

int i;

//依次删除N个结点

i = 1;

list_for_each_safe(pos, n, &numhead.list) {

//为了安全删除结点而进行的遍历

list_del(pos); //从链表中删除当前结点

p = list_entry(pos, struct numlist, llist);

//得到当前数据结点的首地址,即指针

kfree(p); //释放该数据结点所占空间

printk("Node %d has removed from the doublelist...\n", i++);

}

printk("doublelist is exiting...\n");

}

module_init(doublelist_init);

module_exit(doublelist_exit);

参考资料:Linux操作系统原理与应用(第2版)    陈莉君、康华 编著

如下为利用Linux内核链表创建,Linux内核中链表的实现与应用相关推荐

  1. SWUST OJ#1051(数据结构之输出利用先序遍历创建的二叉树中的指定结点的Child结点)

    目录 题目 思路 数据结构代码 小结 题目 思路 从根节点开始遍历,在节点不为空的前提下,进行类似与数学的分类讨论!!!接下来请各位看代码部分,有疑问评论区留言,有问必答!!! 数据结构代码 #inc ...

  2. java数组和链表的区别_java中链表和数组的区别?

    综述:数组是线性结构,可以直接索引,即要去第i个元素,a[i]即可.链表也是线性结构,要取第i个元素,只需用指针往后遍历i次就可.貌似链表比数组还要麻烦些,而且效率低些. 想到这些相同处中的一些细微的 ...

  3. linux boot分区创建,Linux 更换 Boot分区 磁盘 示例

    在之前的2篇博客中分别看了linux boot分区的修复过程: Linux boot 分区损坏重建 示例 https://www.cndba.cn/dave/article/3534https://w ...

  4. linux虚拟串口创建,linux虚拟串口编程

    环境: unbuntu14 虚拟机,已设置可联网 目的1:在linux环境下读写虚拟串口(2虚拟串口连接) 步骤: 一,创建虚拟串口 1.安装虚拟软件 apt-get install socat 2. ...

  5. linux 在硬盘中创建文件系统,linux mkfs命令创建Linux文件系统

    功能描述 使用mkfs命令可以在分区上创建各种文件系统.mkfs命令本身并不执行建立文件系统的工作,而是去调用相关的程序来执行.这里的文件系统是要指定的,如ext4.ext3.vfat或msdos等. ...

  6. linux oracle 用户创建,LINUX下Oracle数据库用户创建方法详解

    本文实例分析了LINUX下Oracle数据库用户创建方法.分享给大家供大家参考,具体如下: 1)登录linux,以oracle用户登录(如果是root用户登录的,登录后用 su - oracle命令切 ...

  7. linux批量用户创建,linux 批量用户的创建

    (1)先编辑一个文本用户文件,每一列按照/etc/passwd密码文件的格式书写,要注意每个用户的用户名.UID.宿主目录都不可以相同,其中密码栏可以留做空白或输入x号.一个范例文件user.txt内 ...

  8. vbox共享文件夹 linux,Vbox下创建Linux和Windows的共享文件夹

    我的Vbox版本是4.3.6...在这里以win8和Ubuntu12.04之间共享文件举例 首先运行虚拟机,然后安装增强功能..这个增强功能很碉堡...能开启无缝模式和系统间的剪贴板共享等牛X功能 然 ...

  9. linux中mkfs创建文件系统,linux mkfs命令创建Linux文件系统

    功能描述 使用mkfs命令可以在分区上创建各种文件系统.mkfs命令本身并不执行建立文件系统的工作,而是去调用相关的程序来执行.这里的文件系统是要指定的,如ext4.ext3.vfat或msdos等. ...

最新文章

  1. MongoDB安装和MongoChef可视化管理工具的使用
  2. 一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之登录窗口调试...
  3. ,改变LI背景颜色与背景图片
  4. u-boot Makefile完全解读
  5. python中数组的维度_Python数组维度问题
  6. Exp1 PC平台逆向破解 20164309 欧阳彧骁
  7. Microsoft Power BI Desktop概念学习系列之Microsoft Power BI Desktop的官网自带示例数据(图文详解)...
  8. 带有Java和Axis2的JSON Web服务
  9. 七:构造方法与自定义构造方法
  10. 算法笔记 --- 布隆过滤器
  11. 10GE DWDM SFP+彩色光模块应用案例
  12. 乔布斯的创新故事_创新工作的真实故事
  13. df满足条件的值修改_python – 如何根据其他列中的条件将pandas df列中的多个值更改为np.nan?...
  14. 2021最新 上海互联网公司排名
  15. Java编写五线谱上的音符_五线谱音符(五线谱1234567表示图)
  16. matlab emd imf波形,emd分解后画出IMF的波形
  17. 小丁在美国的惬意生活 日常学学英语吃吃BBQ-猎豹体育网
  18. Gmail:如何撤回发出的邮件?
  19. HbuilderX 自有证书生成
  20. 微信小程序--获取当前时间

热门文章

  1. Ubuntu 忘记密码的处理方法
  2. windows(win7,win8,xp)hosts文件找不到原因分析及解决方法
  3. 理解Node.js的event loop
  4. 背景选择器selector替换按钮默认背景
  5. unix环境高级编程 pdf_UNIX环境高级编程——记录锁
  6. python csv模块用法_python使用csv模块如何将数据存放在一张表的不同行?
  7. drcom linux怎么运行,linux下使用drcom登录认证
  8. matlab for循环不覆盖,将输出保存到文本文件而不覆盖和打印矩阵中的N个条目[matlab]...
  9. java自定义线程池池,线程池使用及自定义线程池
  10. python打开word后再关闭再打开出错_用Python写了个程序调用word,运行完后再手动打开word文档就变慢了,这是为啥?...