Linked list of linux kernel data structure

1. Foreword

Recently, the linked list structure is needed to write code. It happens that the public library has information about the linked list. At first glance, I feel a little fresh. It is different from the linked list structure I saw before. There are only precursor and successor pointers, but no data fields. Later, looking at the code comments, I found that the code comes from the linux kernel under the linux source code include/Lish.h. This linked list is universal and easy to use. You only need to define a linked list structure in the structure.

2. Introduction to linked list

Linked list is a very basic data structure. It is divided into single linked list and double linked list according to the number of chains. It is divided into one-way linked list and circular linked list according to whether it is circular or not. Generally, the linked list structure is defined as follows:

typedef struct node
{
     ElemType data;      //Data domain
     struct node *next;  //Pointer field
}node, *list;

The linked list contains data fields and pointer fields. The linked list usually contains a head node, which does not store data and is convenient for linked list operation. The structure of one-way circular linked list is shown in the following figure:

The two-way circular linked list structure is shown in the following figure:

In this way, the linked list with data field reduces the universality of the linked list and is not easy to expand. The linked list structure defined by the linux kernel does not have a data field, and only two pointers are needed to complete the operation of the linked list. Adding the linked list node to the data structure has very high scalability and versatility. The linked list structure is defined as follows:

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

The linked list structure is as follows:

When you need to use a linked list structure, you only need to define a linked list type of data in the structure. For example, define an app_info linked list,

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;  //struct Node 
7 }app_info;

Define an app_info linked list, app_info app_info_list; Via app_info_head for linked list operation. According to the C language pointer operation, through the container_of and offsetof, which can be based on app_info_ Find the app from the address of the head_ The starting address of info, that is, a complete AP_ The starting address of the info structure.

[article welfare] Xiaobian recommends his own Linux kernel technology exchange group: [1143996416] sorted out some learning books and video materials that I think are better to share in the group file. If necessary, you can add them yourself!!! (including video tutorials, e-books, practical projects and codes)

3. linux kernel linked list implementation

The kernel implements a two-way circular linked list, which provides the basic functions of linked list operation.

(1) Initialize chain header node

#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;
}

The LIST_HEAD macro creates a chain header node, and uses the LIST_HEAD_INIT macro to assign a value to the header node, so that the predecessor and successor of the header node point to themselves.

INIT_LIST_HEAD function initializes the linked list so that the precursor and successor pointers point to the head node.

(2) Insert node

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 }

Inserting nodes includes inserting list_add from the head of the linked list and inserting list_add_tail from the tail of the linked list. It is realized by calling the _list_add function. Head - > next points to one node and head - > prev points to the tail node.

(3) Delete node

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 }

To delete a node from the linked list, you need to change the successor node of the predecessor node of the node and the predecessor node of the successor node. Finally, set the predecessor node and successor node of the node to point to the two special values of LIST_POSITION1 and LIST_POSITION2. This setting is to ensure that the node items not in the linked list are inaccessible. Accessing LIST_POSITION1 and LIST_POSITION2 will cause page failure fault

/*
 * 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) Mobile node

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 moves a node to the head or tail.

(5) Judgment linked list

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 }

The list_is_last function determines whether the node is the end node, and list_empty determines whether the linked list is empty.

(6) Traversal linked list

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)

The macro list_entity obtains the structure of the linked list, including the data field. list_first_entry obtains the first node of the linked list, including the data source. The list_for_each macro traverses the linked list nodes.

Recommend Linux kernel learning website;

https://ke.qq.com/course/4032547?flowToken=1040330ke.qq.com

4. Test example

Write a simple program to use the linked list, so as to master the use of the linked list.

Customize a similar list structure as follows: 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 //Calculate the position of member in type
 7 #define offsetof(type, member)  (size_t)(&((type*)0)->member)
 8 //Get the starting address of type according to the address of member
 9 #define container_of(ptr, type, member) ({          \
10         const typeof(((type *)0)->member)*__mptr = (ptr);    \
11     (type *)((char *)__mptr - offsetof(type, member)); })
12 
13 //Linked list structure
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 //Add from header
36 static inline void list_add(struct list_head *new , struct list_head *head)
37 {
38     __list_add(new, head, head->next);
39 }
40 //Add from tail
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 is as follows:

1 /**@brief Practice using the linux kernel linked list. The functions include:
 2  * Define linked list structure, create linked list, insert node, delete node, move node and traverse node
 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 //Define app_info linked list structure
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;//struct 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     //Traversal linked list
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     //Create an 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     //Initialize linked list header
74     struct list_head *head = &app_info_list->app_info_node;
75     init_list_head(head);
76     //Insert three 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     //Move the first node to the end
86     printf("Move first node to tail:\n");
87     list_move_tail(head->next, head);
88     for_each_app(head);
89     //Delete last node
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 }

The test results are as follows:

Original link; https://www.cnblogs.com/Anker/p/3475643.html

Tags: Linux data structure linked list

Posted on Mon, 06 Dec 2021 15:24:32 -0500 by morpheus.100