linux内核数据结构之链表【转】

简介:

转自:http://www.cnblogs.com/Anker/archive/2013/12/15/3475643.html

1、前言

   最近写代码需用到链表结构,正好公共库有关于链表的。第一眼看时,觉得有点新鲜,和我之前见到的链表结构不一样,只有前驱和后继指针,而没有数据域。后来看代码注释发现该代码来自linux内核,在linux源代码下include/Lish.h下。这个链表具备通用性,使用非常方便。只需要在结构定义一个链表结构就可以使用。

2、链表介绍

  链表是非常基本的数据结构,根据链个数分为单链表、双链表,根据是否循环分为单向链表和循环链表。通常定义定义链表结构如下:

typedef struct node
{
     ElemType data;      //数据域
     struct node *next; //指针域 }node, *list;

链表中包含数据域和指针域。链表通常包含一个头结点,不存放数据,方便链表操作。单向循环链表结构如下图所示:

双向循环链表结构如下图所示:

  这样带数据域的链表降低了链表的通用性,不容易扩展。linux内核定义的链表结构不带数据域,只需要两个指针完成链表的操作。将链表节点加入数据结构,具备非常高的扩展性,通用性。链表结构定义如下所示:

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

链表结构如下所示:

  需要用链表结构时,只需要在结构体中定义一个链表类型的数据即可。例如定义一个app_info链表,

复制代码
复制代码
1 typedef struct application_info
2 { 3  uint32_t app_id; 4  uint32_t up_flow; 5  uint32_t down_flow; 6 struct list_head app_info_head; //链表节点 7 }app_info;
复制代码
复制代码

定义一个app_info链表,app_info app_info_list;通过app_info_head进行链表操作。根据C语言指针操作,通过container_of和offsetof,可以根据app_info_head的地址找出app_info的起始地址,即一个完整ap_info结构的起始地址。可以参考:http://www.cnblogs.com/Anker/p/3472271.html

3、linux内核链表实现

  内核实现的是双向循环链表,提供了链表操作的基本功能。

(1)初始化链表头结点

复制代码
复制代码
#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
    struct list_head name = LIST_HEAD_INIT(name)

static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; }
复制代码
复制代码

LIST_HEAD宏创建一个链表头结点,并用LIST_HEAD_INIT宏对头结点进行赋值,使得头结点的前驱和后继指向自己。

INIT_LIST_HEAD函数对链表进行初始化,使得前驱和后继指针指针指向头结点。

(2)插入节点

复制代码
复制代码
 1 static inline void __list_add(struct list_head *new,  2 struct list_head *prev,  3 struct list_head *next)  4 {  5 next->prev = new;  6 new->next = next;  7 new->prev = prev;  8 prev->next = new;  9 } 10 11 static inline void list_add(struct list_head *new, struct list_head *head) 12 { 13 __list_add(new, head, head->next); 14 } 15 16 static inline void list_add_tail(struct list_head *new, struct list_head *head) 17 { 18 __list_add(new, head->prev, head); 19 }
复制代码
复制代码

  插入节点分为从链表头部插入list_add和链表尾部插入list_add_tail,通过调用__list_add函数进行实现,head->next指向之一个节点,head->prev指向尾部节点。

(3)删除节点

复制代码
复制代码
 1 static inline void __list_del(struct list_head * prev, struct list_head * next)  2 {  3 next->prev = prev;  4 prev->next = next;  5 }  6  7 static inline void list_del(struct list_head *entry)  8 {  9 __list_del(entry->prev, entry->next); 10 entry->next = LIST_POISON1; 11 entry->prev = LIST_POISON2; 12 }
复制代码
复制代码

  从链表中删除一个节点,需要改变该节点前驱节点的后继结点和后继结点的前驱节点。最后设置该节点的前驱节点和后继结点指向LIST_POSITION1和LIST_POSITION2两个特殊值,这样设置是为了保证不在链表中的节点项不可访问,对LIST_POSITION1和LIST_POSITION2的访问都将引起页故障

复制代码
复制代码
/*
 * These are non-NULL pointers that will result in page faults
 * under normal circumstances, used to verify that nobody uses
 * non-initialized list entries.
 */
#define LIST_POISON1  ((void *) 0x00100100 + POISON_POINTER_DELTA)
#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)
复制代码
复制代码

(4)移动节点

复制代码
复制代码
 1 /**
 2  * list_move - delete from one list and add as another's head  3  * @list: the entry to move  4  * @head: the head that will precede our entry  5 */  6 static inline void list_move(struct list_head *list, struct list_head *head)  7 {  8 __list_del(list->prev, list->next);  9  list_add(list, head); 10 } 11 12 /** 13  * list_move_tail - delete from one list and add as another's tail 14  * @list: the entry to move 15  * @head: the head that will follow our entry 16 */ 17 static inline void list_move_tail(struct list_head *list, 18 struct list_head *head) 19 { 20 __list_del(list->prev, list->next); 21  list_add_tail(list, head); 22 }
复制代码
复制代码

move将一个节点移动到头部或者尾部。

(5)判断链表

复制代码
复制代码
 1 /**
 2  * list_is_last - tests whether @list is the last entry in list @head  3  * @list: the entry to test  4  * @head: the head of the list  5 */  6 static inline int list_is_last(const struct list_head *list,  7 const struct list_head *head)  8 {  9 return list->next == head; 10 } 11 12 /** 13  * list_empty - tests whether a list is empty 14  * @head: the list to test. 15 */ 16 static inline int list_empty(const struct list_head *head) 17 { 18 return head->next == head; 19 }
复制代码
复制代码

list_is_last函数判断节点是否为末尾节点,list_empty判断链表是否为空。

(6)遍历链表

复制代码
复制代码
 1 /**
 2  * list_entry - get the struct for this entry  3  * @ptr: the &struct list_head pointer.  4  * @type: the type of the struct this is embedded in.  5  * @member: the name of the list_struct within the struct.  6 */  7 #define list_entry(ptr, type, member) \  8  container_of(ptr, type, member)  9 10 /** 11  * list_first_entry - get the first element from a list 12  * @ptr: the list head to take the element from. 13  * @type: the type of the struct this is embedded in. 14  * @member: the name of the list_struct within the struct. 15  * 16  * Note, that list is expected to be not empty. 17 */ 18 #define list_first_entry(ptr, type, member) \ 19 list_entry((ptr)->next, type, member) 20 21 /** 22  * list_for_each - iterate over a list 23  * @pos: the &struct list_head to use as a loop cursor. 24  * @head: the head for your list. 25 */ 26 #define list_for_each(pos, head) \ 27 for (pos = (head)->next; prefetch(pos->next), pos != (head); \ 28 pos = pos->next)
复制代码
复制代码

宏list_entity获取链表的结构,包括数据域。list_first_entry获取链表第一个节点,包括数据源。list_for_each宏对链表节点进行遍历。

4、测试例子

编写一个简单使用链表的程序,从而掌握链表的使用。

自定义个类似的list结构如下所示:mylist.h

复制代码
复制代码
 1 # define POISON_POINTER_DELTA 0
 2 
 3 #define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)  4 #define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)  5  6 //计算member在type中的位置  7 #define offsetof(type, member) (size_t)(&((type*)0)->member)  8 //根据member的地址获取type的起始地址  9 #define container_of(ptr, type, member) ({ \ 10 const typeof(((type *)0)->member)*__mptr = (ptr); \ 11 (type *)((char *)__mptr - offsetof(type, member)); }) 12 13 //链表结构 14 struct list_head 15 { 16 struct list_head *prev; 17 struct list_head *next; 18 }; 19 20 static inline void init_list_head(struct list_head *list) 21 { 22 list->prev = list; 23 list->next = list; 24 } 25 26 static inline void __list_add(struct list_head *new, 27 struct list_head *prev, struct list_head *next) 28 { 29 prev->next = new; 30 new->prev = prev; 31 new->next = next; 32 next->prev = new; 33 } 34 35 //从头部添加 36 static inline void list_add(struct list_head *new , struct list_head *head) 37 { 38 __list_add(new, head, head->next); 39 } 40 //从尾部添加 41 static inline void list_add_tail(struct list_head *new, struct list_head *head) 42 { 43 __list_add(new, head->prev, head); 44 } 45 46 static inline void __list_del(struct list_head *prev, struct list_head *next) 47 { 48 prev->next = next; 49 next->prev = prev; 50 } 51 52 static inline void list_del(struct list_head *entry) 53 { 54 __list_del(entry->prev, entry->next); 55 entry->next = LIST_POISON1; 56 entry->prev = LIST_POISON2; 57 } 58 59 static inline void list_move(struct list_head *list, struct list_head *head) 60 { 61 __list_del(list->prev, list->next); 62  list_add(list, head); 63 } 64 65 static inline void list_move_tail(struct list_head *list, 66 struct list_head *head) 67 { 68 __list_del(list->prev, list->next); 69  list_add_tail(list, head); 70 } 71 #define list_entry(ptr, type, member) \ 72  container_of(ptr, type, member) 73 74 #define list_first_entry(ptr, type, member) \ 75 list_entry((ptr)->next, type, member) 76 77 #define list_for_each(pos, head) \ 78 for (pos = (head)->next; pos != (head); pos = pos->next)
复制代码
复制代码

mylist.c如下所示:

复制代码
复制代码
 1 /**@brief 练习使用linux内核链表,功能包括:
 2  * 定义链表结构,创建链表、插入节点、删除节点、移动节点、遍历节点  3  *  4  *@auther Anker @date 2013-12-15  5  **/  6 #include <stdio.h>  7 #include <inttypes.h>  8 #include <stdlib.h>  9 #include <errno.h> 10 #include "mylist.h" 11 //定义app_info链表结构 12 typedef struct application_info 13 { 14  uint32_t app_id; 15  uint32_t up_flow; 16  uint32_t down_flow; 17 struct list_head app_info_node;//链表节点 18 }app_info; 19 20 21 app_info* get_app_info(uint32_t app_id, uint32_t up_flow, uint32_t down_flow) 22 { 23 app_info *app = (app_info*)malloc(sizeof(app_info)); 24 if (app == NULL) 25  { 26 fprintf(stderr, "Failed to malloc memory, errno:%u, reason:%s\n", 27  errno, strerror(errno)); 28 return NULL; 29  } 30 app->app_id = app_id; 31 app->up_flow = up_flow; 32 app->down_flow = down_flow; 33 return app; 34 } 35 static void for_each_app(const struct list_head *head) 36 { 37 struct list_head *pos; 38 app_info *app; 39 //遍历链表 40  list_for_each(pos, head) 41  { 42 app = list_entry(pos, app_info, app_info_node); 43 printf("ap_id: %u\tup_flow: %u\tdown_flow: %u\n", 44 app->app_id, app->up_flow, app->down_flow); 45 46  } 47 } 48 49 void destroy_app_list(struct list_head *head) 50 { 51 struct list_head *pos = head->next; 52 struct list_head *tmp = NULL; 53 while (pos != head) 54  { 55 tmp = pos->next; 56  list_del(pos); 57 pos = tmp; 58  } 59 } 60 61 62 int main() 63 { 64 //创建一个app_info 65 app_info * app_info_list = (app_info*)malloc(sizeof(app_info)); 66 app_info *app; 67 if (app_info_list == NULL) 68  { 69 fprintf(stderr, "Failed to malloc memory, errno:%u, reason:%s\n", 70  errno, strerror(errno)); 71 return -1; 72  } 73 //初始化链表头部 74 struct list_head *head = &app_info_list->app_info_node; 75  init_list_head(head); 76 //插入三个app_info 77 app = get_app_info(1001, 100, 200); 78 list_add_tail(&app->app_info_node, head); 79 app = get_app_info(1002, 80, 100); 80 list_add_tail(&app->app_info_node, head); 81 app = get_app_info(1003, 90, 120); 82 list_add_tail(&app->app_info_node, head); 83 printf("After insert three app_info: \n"); 84 for_each_app(head); 85 //将第一个节点移到末尾 86 printf("Move first node to tail:\n"); 87 list_move_tail(head->next, head); 88 for_each_app(head); 89 //删除最后一个节点 90 printf("Delete the last node:\n"); 91 list_del(head->prev); 92 for_each_app(head); 93 destroy_app_list(head); 94 free(app_info_list); 95 return 0; 96 }
复制代码
复制代码

测试结果如下所示:

参考网址:

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







本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/7130890.html,如需转载请自行联系原作者


相关文章
|
6天前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
29 4
|
10天前
|
缓存 算法 Linux
深入理解Linux内核调度器:公平性与性能的平衡####
真知灼见 本文将带你深入了解Linux操作系统的核心组件之一——完全公平调度器(CFS),通过剖析其设计原理、工作机制以及在实际系统中的应用效果,揭示它是如何在众多进程间实现资源分配的公平性与高效性的。不同于传统的摘要概述,本文旨在通过直观且富有洞察力的视角,让读者仿佛亲身体验到CFS在复杂系统环境中游刃有余地进行任务调度的过程。 ####
31 6
|
9天前
|
缓存 资源调度 安全
深入探索Linux操作系统的心脏——内核配置与优化####
本文作为一篇技术性深度解析文章,旨在引领读者踏上一场揭秘Linux内核配置与优化的奇妙之旅。不同于传统的摘要概述,本文将以实战为导向,直接跳入核心内容,探讨如何通过精细调整内核参数来提升系统性能、增强安全性及实现资源高效利用。从基础概念到高级技巧,逐步揭示那些隐藏在命令行背后的强大功能,为系统管理员和高级用户打开一扇通往极致性能与定制化体验的大门。 --- ###
30 9
|
7天前
|
缓存 负载均衡 Linux
深入理解Linux内核调度器
本文探讨了Linux操作系统核心组件之一——内核调度器的工作原理和设计哲学。不同于常规的技术文章,本摘要旨在提供一种全新的视角来审视Linux内核的调度机制,通过分析其对系统性能的影响以及在多核处理器环境下的表现,揭示调度器如何平衡公平性和效率。文章进一步讨论了完全公平调度器(CFS)的设计细节,包括它如何处理不同优先级的任务、如何进行负载均衡以及它是如何适应现代多核架构的挑战。此外,本文还简要概述了Linux调度器的未来发展方向,包括对实时任务支持的改进和对异构计算环境的适应性。
26 6
|
8天前
|
缓存 Linux 开发者
Linux内核中的并发控制机制:深入理解与应用####
【10月更文挑战第21天】 本文旨在为读者提供一个全面的指南,探讨Linux操作系统中用于实现多线程和进程间同步的关键技术——并发控制机制。通过剖析互斥锁、自旋锁、读写锁等核心概念及其在实际场景中的应用,本文将帮助开发者更好地理解和运用这些工具来构建高效且稳定的应用程序。 ####
28 5
|
8天前
|
算法 Unix Linux
深入理解Linux内核调度器:原理与优化
本文探讨了Linux操作系统的心脏——内核调度器(Scheduler)的工作原理,以及如何通过参数调整和代码优化来提高系统性能。不同于常规摘要仅概述内容,本摘要旨在激发读者对Linux内核调度机制深层次运作的兴趣,并简要介绍文章将覆盖的关键话题,如调度算法、实时性增强及节能策略等。
|
10天前
|
存储 监控 安全
Linux内核调优的艺术:从基础到高级###
本文深入探讨了Linux操作系统的心脏——内核的调优方法。文章首先概述了Linux内核的基本结构与工作原理,随后详细阐述了内核调优的重要性及基本原则。通过具体的参数调整示例(如sysctl、/proc/sys目录中的设置),文章展示了如何根据实际应用场景优化系统性能,包括提升CPU利用率、内存管理效率以及I/O性能等关键方面。最后,介绍了一些高级工具和技术,如perf、eBPF和SystemTap,用于更深层次的性能分析和问题定位。本文旨在为系统管理员和高级用户提供实用的内核调优策略,以最大化Linux系统的效率和稳定性。 ###
|
9天前
|
Java Linux Android开发
深入探索Android系统架构:从Linux内核到应用层
本文将带领读者深入了解Android操作系统的复杂架构,从其基于Linux的内核到丰富多彩的应用层。我们将探讨Android的各个关键组件,包括硬件抽象层(HAL)、运行时环境、以及核心库等,揭示它们如何协同工作以支持广泛的设备和应用。通过本文,您将对Android系统的工作原理有一个全面的认识,理解其如何平衡开放性与安全性,以及如何在多样化的设备上提供一致的用户体验。
|
11天前
|
Linux 数据库
Linux内核中的锁机制:保障并发操作的数据一致性####
【10月更文挑战第29天】 在多线程编程中,确保数据一致性和防止竞争条件是至关重要的。本文将深入探讨Linux操作系统中实现的几种关键锁机制,包括自旋锁、互斥锁和读写锁等。通过分析这些锁的设计原理和使用场景,帮助读者理解如何在实际应用中选择合适的锁机制以优化系统性能和稳定性。 ####
28 6
|
11天前
|
机器学习/深度学习 负载均衡 算法
深入探索Linux内核调度机制的优化策略###
本文旨在为读者揭开Linux操作系统中至关重要的一环——CPU调度机制的神秘面纱。通过深入浅出地解析其工作原理,并探讨一系列创新优化策略,本文不仅增强了技术爱好者的理论知识,更为系统管理员和软件开发者提供了实用的性能调优指南,旨在促进系统的高效运行与资源利用最大化。 ###