linux内核list模块的使用

List模块是linux内核提供的循环链表函数集,头文件是:<linux/list.h>。

主要数据结构:

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

这是一个双向链表。

#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(name)

宏参数name是链表头定义名。

例如:

LIST_HEAD(listHead)

使用list模块中的方法时,你的数据结构中必须包含struct list_head类成员,例如:

Struct myNode{
      Int a;
      Struct list_head listNode;
};
Struct myNode nodeVar;

插入操作

/*
 * Insert a new entry between two known consecutive entries.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
#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
/**
 * list_add - add a new entry
 * @new: new entry to be added
 * @head: list head to add it after
 *
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 */
#ifndef CONFIG_DEBUG_LIST
static inline void list_add(struct list_head *new, struct list_head *head)
{
        __list_add(new, head, head->next);
}
#else
extern void list_add(struct list_head *new, struct list_head *head);
#endif

调用list_add函数可以将一个数据节点加入到链表中.

List_add(&nodeVar->listNode,&listHead)

将元素加入到链表首位置,即链表头listHead的下一个位置。

/**
 * list_add_tail - add a new entry
 * @new: new entry to be added
 * @head: list head to add it before
 *
 * Insert a new entry before the specified head.
 * This is useful for implementing queues.
 */
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
        __list_add(new, head->prev, head);
}

List_add_tail(&nodeVar->listNode,&listHead);

其实它是把nodeVar中的listNode元素接入链表末尾

删除操作

/*
 * 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;
}
/**
 * list_del - deletes entry from list.
 * @entry: the element to delete from the list.
 * Note: list_empty on entry does not return true after this, the entry is
 * in an undefined state.
 */
#ifndef CONFIG_DEBUG_LIST
static inline void list_del(struct list_head *entry)
{
        __list_del(entry->prev, entry->next);
        entry->next = LIST_POISON1;
        entry->prev = LIST_POISON2;
}
#else
extern void list_del(struct list_head *entry);
#endif

查找操作

#define list_for_each_entry(pos, head, member)                                  for (pos = list_entry((head)->next, typeof(*pos), member);                   prefetch(pos->member.next), &pos->member != (head);                     pos = list_entry(pos->member.next, typeof(*pos), member))
#define list_entry(ptr, type, member) 	container_of(ptr, type, member)

其中pos是暂存的结构体指针,member是结构体内部的struct listhead结构变量,head是链表头。container_of()见最后注。

假如有n个struct myNode变量加入了链表,查找元素a =100的节点的操作如下

Struct myNode* Pos
list_for_each_entry(Pos, &listHead,listNode)
{
       If(Pos->a== 100)
       {
       …….
       }
}

如果涉及到Pos的指针内存操作,例如释放等,可以使用函数:

/**
 * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
 * @pos:        the type * to use as a loop cursor.
 * @n:          another type * to use as temporary storage
 * @head:       the head for your list.
 * @member:     the name of the list_struct within the struct.
 */
#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))

这个函数使用n来保存pos的值,以免pos释放后继续操作pos造成内存错误。

例子:

Struct myNode* cPos, nPos;
list_for_each_entry_safe(cPos, nPos, &listHead, listNode)
{
	list_del(cPos);
	free(cPos);
}

注:

container_of(ptr, type, member)是那就是根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针。

例如:container_of(&nodeVar->listNode, struct myNode, listNode)

得到的将是nodeVar的地址。

关于container_of见kernel.h中:

/**
* container_of - cast a member of a structure out to the containing structure
* @ptr:     the pointer to the member.
* @type:     the type of the container struct this is embedded in.
* @member:     the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({             /
         const typeof( ((type *)0)->member ) *__mptr = (ptr);     /
         (type *)( (char *)__mptr - offsetof(type,member) );})

container_of在Linux Kernel中的应用非常广泛,它用于获得某结构中某成员的入口地址.

关于offsetof见stddef.h中:

#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER

TYPE是某struct的类型,0是一个假想TYPE类型struct,MEMBER是该struct中的一个成员。
由于该struct的基地址为0, MEMBER的地址就是该成员相对与struct头地址的偏移量.

关于typeof,这是gcc的C语言扩展保留字,用于声明变量类型。

const typeof( ((type *)0->member ) *__mptr =(ptr);意思是声明一个与member同一个类型的指针常量 *__mptr,并初始化为ptr。

(type *)( (char *)__mptr - offsetof(type,member));意思是__mptr的地址减去member在该struct中的偏移量得到的地址,
再转换成type型指针,该指针就是member的入口地址了。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-05 18:08:00

linux内核list模块的使用的相关文章

Linux内核(6) - 模块机制与“Hello World!

有一种感动,叫内牛满面,有一种机制,叫模块机制.显然,这种模块机制给那些Linux的发烧友们带来了方便,因为模块机制意味着人们可以把庞大的Linux内核划分为许许多多个小的模块.对于编写设备驱动程序的开发者来说,从此以后他们可以编写设备驱动程序却不需要把她编译进内核,不用reboot机器,她只是一个模块,当你需要她的时候,你可以把她抱入怀中(insmod),当你不再需要她的时候,你可以把她一脚踢开(rmmod). 于是,忽如一夜春风来,内核处处是模块.让我们从一个伟大的例子去认识模块.这就是传说

Linux 内核与模块调试

一.简介 内核开发比用户空间开发更难的一个因素就是内核调试艰难.内核错误往往会导致系统宕机,很难保留出错时的现场.调试内核的关键在于你的对内核的深刻理解.   二.方法总结 1)内核调试指南 http://blog.csdn.net/blizmax6/article/details/6747601/ 2)调试方法总结 http://my.oschina.net/fgq611/blog/113249

Linux内核及模块管理基础

原文地址:https://www.cnblogs.com/jacky1982/p/9180878.html

解析Linux内核的基本的模块管理与时间管理操作---超时处理【转】

转自:http://www.jb51.net/article/79960.htm 这篇文章主要介绍了Linux内核的基本的模块管理与时间管理操作,包括模块加载卸载函数的使用和定时器的用法等知识,需要的朋友可以参考下 内核模块管理Linux设备驱动会以内核模块的形式出现,因此学会编写Linux内核模块编程是学习linux设备驱动的先决条件. Linux内核的整体结构非常庞大,其包含的组件非常多.我们把需要的功能都编译到linux内核,以模块方式扩展内核功能. 先来看下最简单的内核模块 ? 1 2

【转】6.4.6 将驱动编译进Linux内核进行测试

原文网址:http://www.apkbus.com/android-98520-1-1.html 前面几节都是将Linux驱动编译成模块,然后动态装载进行测试.动态装载驱动模块不会随着Android系统的启动而自动装载,因此Android系统每次启动都必须使用insmod或modprobe命令装载Linux驱动模块. 对于嵌入式系统(包括嵌入式Android.嵌入式Linux等)一般都采用将Linux驱动编译进内核的方式.这样做虽然没有动态装载灵活,但Linux驱动会随着Android的启动而

你为什么看不懂Linux内核驱动源码?

学习嵌入式Linux驱动开发,最核心的技能就是能够编写Linux内核驱动.深入理解Linux内核.而做到这一步的基础,就是你要看得懂Linux内核源码,了解其基本的框架和具体实现,了解其内核API的使用方法,然后才能根据自己的需求写出高质量的内核驱动程序. 说易行难,很多新人.甚至工作1-2年的开发者刚接触Linux内核时,别说写了,看内核代码可能都是一脸懵逼:明明是C语言,但是就是看不懂是什么意思,除了根据函数名.函数参数.函数的返回值以及注释,了解整个函数的基本功能外,一旦分析其细节,你会发

解析 Linux 内核可装载模块的版本检查机制

转自:http://www.ibm.com/developerworks/cn/linux/l-cn-kernelmodules/ 为保持 Linux 内核的稳定与可持续发展,内核在发展过程中引进了可装载模块这一特性.内核可装载模块就是可在内核运行时加载到内核的一组代码.通常 , 我们会在两个版本不同的内核上装载同一模块失败,即使是在两个相邻的补丁级(Patch Level)版本上.这是因为内核在引入可装载模块的同时,对模块采取了版本信息校验.这是一个与模块代码无关,却与内核相连的机制.该校验机

初探linux内核编程,参数传递以及模块间函数调用

一.前言                                  我们一起从3个小例子来体验一下linux内核编程.如下: 1.内核编程之hello world 2.模块参数传递 3.模块间函数调用 二.准备工作                           首先,在你的linux系统上面安装linux头文件,debian系列: 1 $:sudo apt-get install linux-headers-`uname -r` 安装后,在你的/lib/modules/目录下有你刚

升级Linux内核导致vmware无法使用(vmnet模块无法编译)解决方式

近期将ubuntu升级到了14.04,出现了vmware无法启动的情况. 详细表现为:每次启动的时候都会弹出一个VMWare Kernel Module Updater的对话框,要求依据当前内核版本号又一次编译一些内核模块.可是当中网络模块vmnet总是编译失败. 查找相关资料发现原因在于升级到ubuntu 14.04之后如今的Linux内核版本号是3.13.这个内核版本号改动了一些底层函数,而VMWare的相关源代码包还没有来得及改动相关代码.因为是内核版本号的问题,所以相同的问题也大量出如今