linux内核提供了一个经典通用的双向循环链表list的实现,任何模块都可以借助该接口实现自己的内部循环链表。因为是通用的,可以直接移植到用户态中使用,下面介绍相关的接口与一个简单操作例子,包括链表的插入、查询、修改和删除操作。想深入了解的话直接阅读内核list源代码,代码不是很多,只有list.h 和 types.h。内核源码可以直接下载也可以使用下文给出的链接。

内核定义了链表的结构体,任何链表的实现只要在相关结构体中包含下面这个结构体就可以使用。

struct list_head {struct list_head *next, *prev;
}; 

链表组织成如下:

相关API接口:

1. 链表头的初始化有两种, 初始化完成就相当于创建了一个链表,接下来就可以进行对链表的增删查改操作。

LIST_HEAD(name)  //name就是链表头的名字,该宏会自动定义一个名字为name 的 list_head 结构体。

第二种如下,先声明一个list_head变量,然后使用该宏初始化

    struct list_head head;    //声明链表头INIT_LIST_HEAD(&head);    //链表头初始化

2. 插入链表:

list_add(struct list_head *new, struct list_head *head)        //加入链表头部
list_add_tail(struct list_head *new, struct list_head *head)   //加入到链表尾部

3. 查询链表:

list_for_each(pos, head)    //遍历链表,可以结合list_entry()接口对数据操作
list_for_each_safe(pos, n, head)    //如果在遍历过程中涉及结点删除操作,则需要使用这个接口

4. 删除结点

list_del(struct list_head *entry)

5.在内核list源码中还定义了很多其它的链表操作,这里不再一一列举:

list_empty(const struct list_head *head)     //判断链表是否为空

下面是一个使用链表的例子,包括常见的增加、删除、修改、查询等操作。

/**  Description : linux应用层编程之内核链表list的使用*  Date        :20180610*  Author      :mason*  Mail        : mrsonko@126.com**/#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "listdemo.h"#define log(fmt, arg...) printf(""fmt,##arg)void main() {int quit = 0;int flag = 0;int opt, key;struct list_head msg_head, *pos, *n;    //声明链表首部,注意这pos, n的定义,遍历链表会使用struct msg msg1, msg2, *pmsg;           //自定义的链表结点,结构体中嵌套list_head结构体INIT_LIST_HEAD(&msg_head);     //链表初始化log("*********************\n\n""input options:\n""1: insert\n"           //插入"2: serach\n"           //查询"3: search all \n"      //查询所有"4: modify\n"           //修改"5: delete\n"           //删除"6: delete all\n"       //删除全部"0: quit\n""\n*********************\n");while(!quit) {log("\ninput options:");scanf("%d",  &opt);switch(opt){//插入链表,相同结点可重复插入,也可以在加入前比较判断防止插入重复case 1:pmsg = calloc(1, sizeof(struct msg));if(!pmsg){log("insert fail \n");break;}log("input msgid : ");scanf("%d", &pmsg->msgid);log("input msg info:");getchar();gets(pmsg->msginfo);log("[Your have input : msgid : %d, msginfo : %s ]\n", pmsg->msgid, pmsg->msginfo);list_add_tail(&pmsg->list, &msg_head);  //插入尾部,list_add()插入首部break;//查询case 2:flag = 0 ;log("input msg id:");scanf("%d", &key);   //遍历查询list_for_each_entry(pmsg, &msg_head, list) {if(pmsg->msgid == key){ log("[msgid: %d, msginfo: %s ]\n", key, pmsg->msginfo);flag = 1;break;}}if(!flag)  log("%d not found! \n", key);break;             //查询所有case 3:log("\n***** msg start *****\n\n");list_for_each_entry(pmsg, &msg_head, list){log("[msgid: %d, msginfo: %s ]\n", key, pmsg->msginfo);}log("\n***** msg end   *****\n");break;//修改结点case 4:flag = 0;log("input msg id:");scanf("%d", &key);   //修改list_for_each_entry(pmsg, &msg_head, list) {if(pmsg->msgid == key){ log("[msgid: %d, msginfo: %s ]\n", key, pmsg->msginfo);log("input msg you want to set:");        memset(pmsg->msginfo, 0, sizeof(pmsg->msginfo));getchar();gets(pmsg->msginfo);flag = 1;}}if(!flag){log("msgid : %d not fount !\n", key);}flag = 0;break;//删除结点     case 5:flag = 0;log("input msg id:");scanf("%d", &key);  //遍历链表并且执行删除操作需要使用这个接口 _safelist_for_each_safe(pos, n, &msg_head){pmsg = list_entry(pos, struct msg, list);if(pmsg->msgid == key){log("[delete msg, msgid:%d, msginfo:%s]\n", pmsg->msgid, pmsg->msginfo);list_del(pos);free(pmsg);flag = 1;}}if(!flag)log("msgid : %d not found!\n", key);flag = 0;break;//删除所有case 6:list_for_each_safe(pos, n, &msg_head){pmsg = list_entry(pos, struct msg, list);log("[delete msg, msgid:%d, msginfo:%s]\n", pmsg->msgid, pmsg->msginfo);list_del(pos);free(pmsg);}break;  default://退出前释放资源list_for_each_safe(pos, n, &msg_head){pmsg = list_entry(pos, struct msg, list);list_del(pos);free(pmsg);}quit = 1;break;}}return ;
}

使用截图,查询、修改和删除操作。

头文件如下,宏定义直接从内核list.h文件中移植过来,这里只取了用到的部分,list.h还定义了很多其它的操作。

#ifndef _LISTDEMO_H
#define _LISTDEMO_H//通过结构体成员指针获取结构体指针位置
#define container_of(ptr, type, member) ({          \const typeof( ((type *)0)->member ) *__mptr = (ptr);   \(type *)( (char *)__mptr - offsetof(type,member) );})//链表结构体
struct list_head {struct list_head *next, *prev;
};//链表初始化
static inline void INIT_LIST_HEAD(struct list_head *list)
{list->next = list;list->prev = list;
}#ifndef CONFIG_DEBUG_LIST
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;
}
#else
extern void __list_add(struct list_head *new,struct list_head *prev,struct list_head *next);
#endif//添加至链表首部
static inline void list_add(struct list_head *new, struct list_head *head)
{__list_add(new, head, head->next);
}//添加到链表尾部
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{__list_add(new, head->prev, head);
}//判断链表是否为空
/*** list_empty - tests whether a list is empty* @head: the list to test.*//* if empty return 1,else 0 */
static inline int list_empty(const struct list_head *head)
{return head->next == head;
}/** Delete a list entry by making the prev/next entries* point to each other.** This is only for internal list manipulation where we know* the prev/next entries already!*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{next->prev = prev;prev->next = next;
}//删除操作
static inline void list_del(struct list_head *entry)
{__list_del(entry->prev, entry->next);entry->next = NULL;entry->prev = NULL;
}//获取链表的数据指针
#define list_entry(ptr, type, member) \container_of(ptr, type, member)//遍历链表
#define list_for_each(pos, head) \for (pos = (head)->next; pos != (head); pos = pos->next)//遍历过程中如果对链表有删除操作需要使用这个接口
#define list_for_each_safe(pos, n, head) \for (pos = (head)->next, n = pos->next; pos != (head); \pos = n, n = pos->next)//遍历链表元素
#define list_for_each_entry(pos, head, member)              \for (pos = list_entry((head)->next, typeof(*pos), member); \&pos->member != (head);    \pos = list_entry(pos->member.next, typeof(*pos), member))#define list_for_each_entry_safe(pos, n, head, member)            \for (pos = list_entry((head)->next, typeof(*pos), member), \n = list_entry(pos->member.next, typeof(*pos), member);    \&pos->member != (head);                    \pos = n, n = list_entry(n->member.next, typeof(*n), member))//取第一个元素
#define list_first_entry(ptr, type, member) \list_entry((ptr)->next, type, member)//自定义消息结构体
struct msg {int msgid;char msginfo[50];struct list_head list;
};#endif

代码可以直接从github上克隆下来编译运行,其中包含了内核list源码可做参考:

git clone git@github.com:FuYuanDe/list.git    //下载源码后进入目录make后运行
参考资料:

https://www.ibm.com/developerworks/cn/linux/kernel/l-chain/

linux 应用层编程之内核链表list的使用相关推荐

  1. Linux环境编程 哈希链表结构 hlist 介绍与用例

    hlist原本是定义在内核list.h里面的结构体,主要用在解决哈希表冲突时使用链接(chaining)方法时候用到的结构体.结构体定义简单.相关的接口也比较丰富,可以直接拿到用户层使用.最常见的一种 ...

  2. 代码示例_网络编程_select_内核链表

    select_list 1.头文件 1 #pragma once 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include ...

  3. Linux内核链表深度分析【转】

    本文转载自:http://blog.csdn.net/coding__madman/article/details/51325646 链表简介: 链表是一种常用的数据结构,它通过指针将一系列数据节点连 ...

  4. Linux环境编程 用户层定时器使用一 timerfd的使用

    timerfd是linux提供的定时器机制,基于文件描述符,定时器精度最高可达纳秒级别,接口包括定时器创建.启动定时器.关闭定时器和删除定时器.下面介绍一下timerfd  API接口和一个结合epo ...

  5. Linux驱动编程 step-by-step (十) Linux 内核链表

    终于可以清闲下来打理一下我的blog了,台资企业真的事情很多很烦-- 前几篇文章对字符设备有个简单介绍,并以简单的一个字符设备驱动作结尾,其实linux上大部分驱动程序都是字符设备程序,Linux源码 ...

  6. Linux网络编程(六)-高并发服务器03-I/O多路复用03:epoll【红黑树;根节点为监听节点】【无宏FD_SETSIZE限制;不需每次都将要监听的文件描述符从应用层拷贝到内核;不需遍历树】

    一.epoll概述 epoll的本质是一个[红黑树].监听结点为根节点. 大量并发,少量活跃效率高. epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并 ...

  7. linux内核链表分析

    一.常用的链表和内核链表的区别 1.1  常规链表结构        通常链表数据结构至少应包含两个域:数据域和指针域,数据域用于存储数据,指针域用于建立与下一个节点的联系.按照指针域的组织以及各个节 ...

  8. Linux内核链表交换节点,[笔记]Linux内核链表:结点的插入、删除以及链表的遍历...

    Linux内核链表:结点的插入.删除以及链表的遍历 1. Linux内核链表的核心思想是:在用户自定义的结构A中声明list_head类型的成员p,这样每个结构类型为A的变量a中,都拥有同样的成员p, ...

  9. Linux内核链表实现剖析

    Linux内核使用环形双向链表,无所谓头结点和尾节点. 内核链表详细信息见 include/ linux / list.h . 1. 定义和初始化内核链表 struct list_head { str ...

最新文章

  1. web服务器错误配置文件,web服务器http配置文件
  2. 修改Activity响应音量控制键修改的音频流
  3. 与猜数问题有关的游戏C语言,猜数字游戏(C语言版)
  4. 如何改变iframe滚动条的样式?
  5. Springboot文件上传提示:failed to convert java.lang.String to org.springframework.util.unit.DataSize
  6. 每日程序C语言19-求阶乘的前20项和
  7. 前端学习之day02-CSS基础
  8. WinForm立体饼状图实现(附源码示例) 之配餐系统的开发
  9. MMKV集成与原理,成功跳槽阿里!
  10. PAT 乙级1001 害死人不偿命的(3n+1)猜想
  11. java 正規表示 group_经验分享|Java+百度AI实现人脸识别
  12. js 获取中括号里面字符串_西门子SCL编程入门教程连载(18)——字符串
  13. static数据的初始化
  14. BS7799、ISO/IEC 17799、ISO/IEC 27001 的关系
  15. 哔哩哔哩电脑版下载后音频与画面分离,视频没有声音
  16. switch语句(C++)
  17. Debian Iptables 配置教程
  18. JavaScript实现网页轮播图
  19. 湖北智禾网店指导:新开卖家必须要了解的淘宝交易流程及交易规则。
  20. 领域驱动设计的重要性

热门文章

  1. Java Android 32位16位 MD5加密
  2. Remoting技术简介
  3. sql not in 用法_SQL 语法速成手册
  4. 1.1 计算机视觉-深度学习第四课《卷积神经网络》-Stanford吴恩达教授
  5. 5.10 程序示例--模型选择-机器学习笔记-斯坦福吴恩达教授
  6. 可能是目前最给力的开源硬件——ESPlay Micro V2,童芯派劲敌他来了
  7. 创客常用开发板“四剑客”对比,谁最“快”?
  8. H∞控制的simulink仿真
  9. 进程间通信-共享内存实例
  10. 张学友演唱会成犯罪分子噩梦,阿里云云盾人发布脸对比功能将进一步提升罪犯监察力度...