初识内核链表
前面我們說(shuō)過(guò)如何用C實(shí)現(xiàn)通用類(lèi)型的鏈表,比如void*的指針,零長(zhǎng)數(shù)組等。可是小菜鳥(niǎo)畢竟趕不上大師,還是Linux內(nèi)核巧妙啊,這里面的鏈表,才是鏈表中的“奇葩”。
源碼的路徑是include/linux/list.h
我們先找?guī)讉€(gè)簡(jiǎn)單的讀一讀吧。
struct list_head { struct list_head *next, *prev; };這個(gè)是在include/linux/types.h中定義的。雖然名字叫l(wèi)ist_head,讓人感覺(jué)是頭節(jié)點(diǎn),其實(shí)這就是通用的節(jié)點(diǎn),和頭一點(diǎn)關(guān)系都沒(méi)有。你也許會(huì)奇怪,怎么沒(méi)有數(shù)據(jù)域?是的,就是沒(méi)有數(shù)據(jù)域。那數(shù)據(jù)怎么辦?后面你就知道了,這個(gè)節(jié)點(diǎn)是內(nèi)嵌在用戶數(shù)據(jù)中的。
static inline void INIT_LIST_HEAD(struct list_head *list) {list->next = list;list->prev = list; }
節(jié)點(diǎn)的初始化函數(shù),自己指向自己。
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; }在兩個(gè)節(jié)點(diǎn)中間插入一個(gè)新節(jié)點(diǎn)。
static inline void list_add(struct list_head *new, struct list_head *head) {__list_add(new, head, head->next); }假如head是頭結(jié)點(diǎn)的話,這個(gè)就是頭插了。
static inline void list_add_tail(struct list_head *new, struct list_head *head) {__list_add(new, head->prev, head); }head->prev就是最后一個(gè)節(jié)點(diǎn)了,這個(gè)節(jié)點(diǎn)的后面那個(gè)就是head,所以這個(gè)函數(shù)是尾插。
static inline void __list_del(struct list_head * prev, struct list_head * next) {next->prev = prev;prev->next = next; }刪除掉一個(gè)節(jié)點(diǎn)(必須知道這個(gè)節(jié)點(diǎn)的前驅(qū)和后繼)
static inline void list_del(struct list_head *entry) {__list_del(entry->prev, entry->next);}刪除某個(gè)節(jié)點(diǎn)。
static inline int list_empty(struct list_head *head) {return head->next == head; }判斷鏈表是不是為空,這里的head是頭結(jié)點(diǎ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_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)安全遍歷,看注釋就明白,這個(gè)在遍歷的過(guò)程中刪除節(jié)點(diǎn)也是可以用的,因?yàn)楫?dāng)前節(jié)點(diǎn)的下一個(gè)已經(jīng)保存了哦。
/*** 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)關(guān)于container_of宏,我在另一篇博文中已經(jīng)解釋了,這里不再贅述。其實(shí)這個(gè)宏就是給container_of換了個(gè)馬甲,叫
list_entry, ptr是指向list_head 的指針(簡(jiǎn)稱(chēng)小指針),type是結(jié)構(gòu)體的類(lèi)型,比如struct XXXX, member 是list_head 在結(jié)構(gòu)體里的名字。最后就得到了指向struct XXXX的指針(簡(jiǎn)稱(chēng)大指針)。我們把這個(gè)宏叫做“小指針轉(zhuǎn)大指針”。
/*** 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)ptr在這里表示頭指針,這個(gè)宏得到了第一個(gè)結(jié)構(gòu)體的地址(大指針)。
/*** 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)pos在這里是一個(gè)結(jié)構(gòu)體的指針(大指針),這個(gè)宏得到了下一個(gè)結(jié)構(gòu)體的地址。
/*** 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))遍歷鏈表中的每個(gè)大指針。
pos:大指針,head:頭結(jié)點(diǎn),member:list_head 在結(jié)構(gòu)體中的名字
基本的就說(shuō)到這里,下面我們寫(xiě)個(gè)例子。
#include <stdio.h> #include "list.h"struct person {char name[12];struct list_head list;char sex;unsigned char age; };int main() {struct person persons[] = {{"Marry", NULL,NULL,'f',20},{"Mike",NULL,NULL,'m',23},{"Leslie",NULL,NULL,'m', 36},{"Ann",NULL,NULL,'f',27}};LIST_HEAD(head);//頭結(jié)點(diǎn)int i;for (i = 0; i < sizeof(persons)/sizeof(persons[0]); ++i) {list_add(&persons[i].list, &head);}struct list_head *cur = NULL;struct person *pdata = NULL;list_for_each(cur, &head) {pdata = container_of(cur, struct person, list);printf("%s:%d\n", pdata->name, pdata->age);}printf("list_for_each_entry\n"); list_for_each_entry(pdata, &head, list) {printf("%s:%d\n", pdata->name, pdata->age);}return 0; }
運(yùn)行結(jié)果:
Ann:27
Leslie:36
Mike:23
Marry:20
list_for_each_entry
Ann:27
Leslie:36
Mike:23
Marry:20
(完)
總結(jié)
- 上一篇: 2020快手用户及营销报告
- 下一篇: 二叉查找树的C语言实现(一)