鏈表結(jié)構(gòu)告抄,結(jié)合Linux內(nèi)核中 list_head 常見使用方法
list_head 定義
list_head 結(jié)構(gòu)體定義肝集,kernel/inclue/linux/types.h 如下:
struct list_head {
struct list_head *next, *prev;
};
? 然后就開始圍繞這個結(jié)構(gòu)開始構(gòu)建鏈表,然后插入、刪除節(jié)點(diǎn) ,遍歷整個鏈表等等,其實(shí)內(nèi)核已經(jīng)提供好了現(xiàn)成的接口含末,接下來就讓我們進(jìn)入kernel/include/linux/list.h中:
一. 創(chuàng)建鏈表
內(nèi)核提供了下面的這些接口來初始化鏈表:
#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)
{
WRITE_ONCE(list->next, list);
list->prev = list;
}
所以可以通過這個方法來初始化一個鏈表,如下是將listhead作為一個結(jié)構(gòu)體成員存放了即舌,為什么要這么做呢佣盒?后面在實(shí)際的例子應(yīng)用中會使用到
struct data_list {
int data_len;
char buf[64];
struct list_head list_node;
};
struct data_list * iic_data_head;
static void init_data_head(void*) {
iic_data_head = malloc(sizeof(struct data_list ));
if (!iic_data_head) {
KLOGD("no mem\n");
return -ENOMEM;
}
memset(iic_data_head, 0x0, sizeof(struct data_list ));
INIT_LIST_HEAD(&iic_data_head->**list_node**);
}
二. 添加節(jié)點(diǎn)
內(nèi)核已經(jīng)提供了添加節(jié)點(diǎn)的接口了
1. list_add
如下所示。 根據(jù)注釋可知侥涵,是在鏈表頭head后方插入一個新節(jié)點(diǎn)new沼撕。
/**
* 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再調(diào)用__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!
*/
#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
其實(shí)就是在head 鏈表頭后和鏈表頭后第一個節(jié)點(diǎn)之間插入一個新節(jié)點(diǎn)宋雏。然后這個新的節(jié)點(diǎn)就變成了鏈表頭后的第一個節(jié)點(diǎn)了。
2. list_add_tail 接口
上面所講的list_add接口是從鏈表頭header后添加的節(jié)點(diǎn)务豺。 同樣磨总,內(nèi)核也提供了從鏈表尾處向前添加節(jié)點(diǎn)的接口list_add_tail. 讓我們來看一下它的具體實(shí)現(xiàn)。
/**
* 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 也是調(diào)用了 __list_add 笼沥,不過傳遞的參數(shù)同list_add 方法不一樣蚪燕,
所以,很清楚明了奔浅, list_add_tail就相當(dāng)于在鏈表頭前方依次插入新的節(jié)點(diǎn)(也可理解為在鏈表尾部開始插入節(jié)點(diǎn)馆纳,此時,header節(jié)點(diǎn)既是為節(jié)點(diǎn)汹桦,保持不變)
三. 刪除節(jié)點(diǎn)
內(nèi)核同樣在list.h文件中提供了刪除節(jié)點(diǎn)的接口 list_del()鲁驶, 讓我們看一下它的實(shí)現(xiàn)流程
/**
* 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_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
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_entry(struct list_head *entry);
extern void list_del(struct list_head *entry);
#endif
利用list_del(struct list_head *entry) 接口就可以刪除鏈表中的任意節(jié)點(diǎn)了,但需注意舞骆,前提條件是這個節(jié)點(diǎn)是已知的钥弯,既在鏈表中真實(shí)存在,切prev督禽,next指針都不為NULL脆霎。
四. 鏈表遍歷
內(nèi)核是同過下面這些宏定義來完成對list_head鏈表進(jìn)行遍歷的,如下 :
/**
* 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_prev - iterate over a list backwards
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); pos = pos->prev)
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_prev_safe(pos, n, head) \
for (pos = (head)->prev, n = pos->prev; \
pos != (head); \
pos = n, n = pos->prev)
而且狈惫,list.h 中也提供了list_replace( 節(jié)點(diǎn)替換) list_move(節(jié)點(diǎn)移位) 睛蛛,翻轉(zhuǎn),查找等接口胧谈,有需要可以往對應(yīng)源碼中查看忆肾。
五. 宿主結(jié)構(gòu)
1.找出宿主結(jié)構(gòu) list_entry(ptr, type, member)
上面的所有操作都是基于list_head這個鏈表進(jìn)行的,涉及的結(jié)構(gòu)體也都是:
struct list_head {
struct list_head *next, *prev;
};
如前面一開始創(chuàng)建鏈表的時候定義的結(jié)構(gòu)體 data_list 我們這里把它叫做list_head的宿主結(jié)構(gòu)體
struct data_list{
int data_len;
char buf[64];
struct list_head list_node;
};
list.h中提供了list_entry宏來實(shí)現(xiàn)對應(yīng)地址的轉(zhuǎn)換菱肖,但最終還是調(diào)用了container_of宏
/**
* 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_head within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* 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) );})
而offsetof定義在 kernel/include/linux/stddef.h 难菌,如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
看下container_of宏的注釋:
(1)根據(jù)結(jié)構(gòu)體重的一個成員變量地址導(dǎo)出包含這個成員變量mem的struct地址。
(2)參數(shù)解釋:
? ptr : 成員變量mem的地址
? type: 包含成員變量mem的宿主結(jié)構(gòu)體的類型
? member: 在宿主結(jié)構(gòu)中的mem成員變量的名稱
Demo:
struct data_list {
int data_len;
char buf[64];
struct list_head list_node;
};
struct data_list* test_list;
container_of(&test_list->list_node, struct data_list, list_node); 這個調(diào)用返回的是 test_list 的指針
//ptr:&test_list->list_node
//type:struct data_list
//member:list_node
2. 宿主結(jié)構(gòu)的遍歷
? 因?yàn)槲覀兛梢愿鶕?jù)結(jié)構(gòu)體中成員變量的地址找到宿主結(jié)構(gòu)的地址蔑滓, 并且我們可以對成員變量所建立的鏈表進(jìn)行遍歷,那我們是不是也可以通過某種方法對宿主結(jié)構(gòu)進(jìn)行遍歷呢遇绞?
? 其實(shí)內(nèi)核中有很多這種用法键袱,ex:Asoc中的control.c, 內(nèi)核在list.h中提供了下面的宏:
/**
* 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_first_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_reverse - iterate backwards 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_reverse(pos, head, member) \
for (pos = list_last_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_prev_entry(pos, member))
其中摹闽,list_first_entry 和 list_next_entry宏都定義在list.h中蹄咖,分別代表:獲取第一個真正的宿主結(jié)構(gòu)的地址; 獲取下一個宿主結(jié)構(gòu)的地址付鹿。它們的實(shí)現(xiàn)都是利用list_entry宏澜汤。
/**
* list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* list_next_entry - get the next element in list
* @pos: the type * to cursor
* @member: the name of the list_struct within the struct.
*/
#define list_next_entry(pos, member) \
list_entry((pos)->member.next, typeof(*(pos)), member)
最終使用 for 循環(huán)實(shí)現(xiàn)了宿主結(jié)構(gòu)的遍歷
首先pos定位到第一個宿主結(jié)構(gòu)地址蚜迅,然后循環(huán)獲取下一個宿主結(jié)構(gòu)地址,如果查到宿主結(jié)構(gòu)中的member成員變量(宿主結(jié)構(gòu)中struct list_head定義的字段)地址為head俊抵,則退出谁不,從而實(shí)現(xiàn)了宿主結(jié)構(gòu)的遍歷。如果要循環(huán)對宿主結(jié)構(gòu)中的其它成員變量進(jìn)行操作徽诲,這個遍歷操作就顯得特別有意義了刹帕。
List_head 實(shí)現(xiàn)隊(duì)列用法
這是個demo
static struct my_data_list * iic_data_head;
static int my_queue_data_lists(u8* data, int data_len)
{
struct my_data_list * list_node_data = NULL;
int ret = 0;
if (data==NULL || data_len<=0) {
KLOGD("error data to add to queue");
ret = -EINVAL;
}
list_node_data = malloc(sizeof(struct my_data_list));
list_node_data->data_buff.data = malloc(data_BUFFER_SIZE);
if (list_node_data) {
list_node_data->list_node.next = NULL;
list_node_data->list_node.prev = NULL;
pthread_mutex_lock(&queue_mutex);
list_node_data->data_buff.len = data_len;
memcpy(list_node_data->data_buff.data, data, data_len);
list_add_tail(&list_node_data->list_node, &iic_data_head->list_node);
KLOGD("Add list_node:%p value:%s len:%d cmd:0x%02x%02x\n",
iic_data_head->list_node.prev,
list_node_data->data_buff.data, list_node_data->data_buff.len,
data[data_CMD_H_INDEX], data[data_CMD_L_INDEX]);
pthread_mutex_unlock(&queue_mutex);
} else {
ret = -ENOMEM;
}
return ret;
}
/**
\* 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) );})
static int my_dequeue_data_lists(u8* value)
{
struct list_head* list_node;
struct my_data_list* list_node_data;
int value_len = 0;
// mutex_lock(&iic_data_head->read_mutex);
pthread_mutex_lock(&queue_mutex);
if (!list_empty(&iic_data_head->list_node))
{
list_node = iic_data_head->list_node.next;
list_node_data = container_of(list_node,struct my_data_list, list_node);
value_len = list_node_data->data_buff.len;
memcpy(value, list_node_data->data_buff.data, value_len);
KLOGD("Out list_node:%p value:%s len:%d cmd:0x%02x%02x\n",
list_node, list_node_data->data_buff.data,
list_node_data->data_buff.len,
value[data_CMD_H_INDEX], value[data_CMD_L_INDEX]);
list_del(list_node);
free(list_node_data->data_buff.data);
free(list_node_data);
}
// mutex_unlock(&iic_data_head->read_mutex);
pthread_mutex_unlock(&queue_mutex);
return value_len;
}
static int my_init_list_head(void)
{
iic_data_head = malloc(sizeof(struct my_data_list));
memset(iic_data_head, 0x0, sizeof(struct my_data_list));
if (!iic_data_head) {
KLOGD("no mem\n");
return -ENOMEM;
}
INIT_LIST_HEAD(&iic_data_head->list_node);
return 0;
}