目录
前言
在Linux内核中使用了大量的链表结构来组织数据,包括设备列表以及各种功能模块中的数据组织。这些链表大多采用在[include/linux/list.h]实现的一个相当精彩的链表数据结构。事实上,内核链表就是采用双循环链表机制。内核链表有别于传统链表就在节点本身不包含数据域,只包含指针域。故而可以很灵活的拓展数据结构。
一、list_head结构
list_head整个结构没有数据域,只有两个指针域。这个结构本身意义不大,不过在内核链表中,起着整个衔接作用,可以说是内核链表的核心不为过。
struct list_head {
struct list_head *next, *prev;
};
二、链表初始化
list_head 本身不包含数据域,就使得整个链表上的数据更加灵活,具备通用性。使用接口初始化比较直接明了,直接将链表头指针的前驱和后继都指向自己
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
三、节点添加
linux list使用list_add来添加节点,最终调用的是__list_add 函数,根据注释可知,list_add 是头部插入,总是在链表的头部插入一个新的节点。
list_add结构如下所示:
/**
* 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.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
__list_add结构如下所示:
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
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_add_tail与list_add类似,list_add_tail是实现尾部数据插入,list_add是头部插入。
/**
* 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);
}
四、节点删除
linux list定了删除节点的接口为list_del,定义如下所示:
static inline void list_del(struct list_head *entry)
{
/* __list_del_entry(entry) 也行*/
__list_del(entry->prev, entry->next);
/* 指向特定的位置,反初始化 */
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
__list_del:这个接口,根据prev/next 删除其节点,删除的节点必须是已知的并且 prev 和 next 不为空
/*
* 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;
}
五、遍历操作
linux list定义遍历接口为list_entry 宏,以list 指针为节点组成的一条双链表,遍历的过程中只能得到list的地址,那么对于其所有者地址就是通过这个宏获取的。
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
list_for_each:它实际上是一个for循环,利用传入的pos作为循环变量,从表头head开始,逐项向后(next方向)移动pos,直至又回到head
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
list_for_each_entry:遍历每一个list,然后获取其宿主结构地址
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#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))
list_for_each_entry_safe:更加安全的遍历迭代接口,防止误删除节点操作
/**
* 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_head within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member), \
n = list_next_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
六、应用程序接口设计
下面根据list链表提供的接口来设计一个数据链表结构,实现先进先出链表数据操作来存储应用数据,具体如下所示:
a、链表头结构体定义:
typedef struct _T_MVB_DATA_LIST_HEAD
{
struct list_head listHead;
int iCount;
}T_MVB_DATA_LIST_HEAD;
b、应用数据节点结构体定义:
typedef struct _T_MVB_DATA_LIST_NODE
{
struct list_head list;
int iLen;
char *pMVBData;
}T_MVB_DATA_LIST_NODE;
c、链表初始化接口定义:
int Mvb_DataListInit(T_MVB_DATA_LIST_HEAD * pMVBDataList)
{
if(pMVBDataList == NULL)
{
return -1;
}
pthread_mutex_init(&g_MVBDataListMux, NULL);
memset(pMVBDataList, 0, sizeof(T_MVB_DATA_LIST_HEAD));
pMVBDataList->iCount = 0;
INIT_LIST_HEAD(&pMVBDataList->listHead);
return 0;
}
d、链表数据插入接口(尾部插入):
int Mvb_DataListAddTail(T_MVB_DATA_LIST_HEAD * pMVBDataList, const char *pMVBData, int iLen)
{
char *pBuff = NULL;
T_MVB_DATA_LIST_NODE * pMVBDataNode = NULL;
if(pMVBDataList == NULL || pMVBData == NULL)
{
return -1;
}
pMVBDataNode = (T_MVB_DATA_LIST_NODE *)malloc(sizeof(T_MVB_DATA_LIST_NODE));
if(pMVBDataNode == NULL)
{
return -1;
}
memset(pMVBDataNode, 0, sizeof(T_MVB_DATA_LIST_NODE));
pBuff = (char *)malloc(iLen);
if(pBuff == NULL)
return -1;
pMVBDataNode->pMVBData = pBuff;
memset(pMVBDataNode->pMVBData, 0x00, iLen);
pMVBDataNode->iLen = iLen;
memcpy(pMVBDataNode->pMVBData, pMVBData, iLen);
pthread_mutex_lock(&g_MVBDataListMux);
list_add_tail(&pMVBDataNode->list, &pMVBDataList->listHead);
pMVBDataList->iCount ++;
pthread_mutex_unlock(&g_MVBDataListMux);
return pMVBDataList->iCount;
}
e、获取节点数据(头结点数据):
int Mvb_DataListGetFirst(T_MVB_DATA_LIST_HEAD * pMVBDataList, char *pMVBData, int *pLen)
{
int iRet = -1;
T_MVB_DATA_LIST_NODE *pos = NULL;
T_MVB_DATA_LIST_NODE *n = NULL;
if(pMVBDataList == NULL || pMVBDataList->iCount == 0 || pMVBData == NULL)
{
return -1;
}
pthread_mutex_lock(&g_MVBDataListMux);
if(list_empty(&pMVBDataList->listHead))
{
pthread_mutex_unlock(&g_MVBDataListMux);
return -1;
}
list_for_each_entry_safe(pos, n, &pMVBDataList->listHead, list)
{
if(pos != NULL)
{
memcpy(pMVBData, pos->pMVBData, pos->iLen);
*pLen = pos->iLen;
iRet = 0;
break;
}
}
pthread_mutex_unlock(&g_MVBDataListMux);
return iRet;
}
f、节点删除操作接口
int Mvb_DataListDelFirst(T_MVB_DATA_LIST_HEAD * pMVBDataList)
{
T_MVB_DATA_LIST_NODE *pos = NULL;
T_MVB_DATA_LIST_NODE *n = NULL;
if(pMVBDataList == NULL || pMVBDataList->iCount == 0)
{
return -1;
}
pthread_mutex_lock(&g_MVBDataListMux);
if(list_empty(&pMVBDataList->listHead))
{
pthread_mutex_unlock(&g_MVBDataListMux);
return -1;
}
list_for_each_entry_safe(pos, n, &pMVBDataList->listHead, list)
{
if(pos != NULL)
{
list_del(&pos->list);
free(pos->pMVBData);
free(pos); /* 是不是free了两次?? */
pos = NULL;
pMVBDataList->iCount --;
break;
}
}
pthread_mutex_unlock(&g_MVBDataListMux);
return pMVBDataList->iCount;
}
g、链接节点数获取接口:
int Mvb_DataListCount(T_MVB_DATA_LIST_HEAD * pFilesList)
{
int iCount = 0;
if(pFilesList == NULL || pFilesList->iCount == 0)
{
return 0;
}
pthread_mutex_lock(&g_MVBDataListMux);
if(list_empty(&pFilesList->listHead))
{
pthread_mutex_unlock(&g_MVBDataListMux);
return 0;
}
iCount = pFilesList->iCount;
pthread_mutex_unlock(&g_MVBDataListMux);
return iCount;
}
总结
本文主要讲述linux内核list链表基本接口介绍,以及根据list链表设计应用程序接口,仅供大家参考使用!