In the study of the Linux kernel with the dmatest.c driver process found that some of the link operation, very confusing, so the record down some of the information after the experience. 0 The characteristics of the core linked list
Common linked list operations, usually containing data fields and pointer fields 2 contents are shown below.
typedef struct Node { Elemtype data; Data fields struct Node *next; Pointer field node, *list; |
The Linux kernel defines a linked list with no data fields, and requires only two pointers to complete the operation of the linked list. It has very high expansibility and universality. The list structure is defined as shown below.
struct List_head { struct List_head *next, *prev; }; |
There are usually definitions of the following format, and it is generally recommended to combine containner_of and offset_of to gain greater flexibility and maneuverability. For example, you can find the starting address of the app_info from the address of App_info_head, that is, the starting address of a complete app_info structure.
typedef struct APPLICATION_INFO { uint32_t app_id; uint32_t Up_flow; uint32_t Down_flow; struct List_head app_info_head; Linked list node }app_info; |
1 Linked list operation and realization principle
(1) Initialization of the Chain header node
The effect of initialization is to make both the predecessor and the successor pointer point to the header node.
Here you need to be very careful about the interface of INIT (at first you didn't notice the code that led to the error understanding) List_head_init, List_head, and Init_list_head.
#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; } |
So there are two different uses. One is a macro extension and the other is a function call.
Way One static struct info_t{ struct List_head channels; }Info = { . Channels = list_head_init(info. Channels); } Mode two Init_list_head(&info. Channels); |
(2) Insert operation
List_add and List_add_tail are inserted in the table head and the end of the table, but are implemented through the __list_add, because the core implementation of the linked list is a two-way list, so Head->prev is the end of the table, and after the Head->next is the head of the table. The kernel is implemented as shown in the following table.
| 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; } static inline void List_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } static inline void List_add_tail(struct list_head *new, struct list_head * Head) { __list_add(new, head->prev, head); } |
(3) Delete operation
List_del, the predecessor and successor node of the node are deleted. Note that the deletion also requires that the predecessor and successor of the node to be deleted be pointed to POSITION1 and POSITION2 respectively. Operations on both POSITION1 and POSITION2 will cause page failures.
| static inline void __list_del(struct list_head * prev, struct list_head * next) /c4> { Next->prev = prev; Prev->Next = next; } static inline void List_del(struct list_head *entry) { __list_del(entry->prev, entry->next); Entry->next = list_poison1; Entry->prev = list_poison2; } /* * These are non-null pointers that would 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) Judgment list
Whether null (LIST_EMPTY) is the last node (list_is_last).
| /** * List_is_last-tests Whether @list is the last entry in list @head * @list: the entry to test * @head: The head of the list */ static inline int list_is_last(const struct List_head *list, const struct List_head *head) { return list->Next = head; } /** * List_empty-tests Whether a list is empty * @head: The list to test. */ static inline int List_empty(const struct List_head *head) { return head->Next = head; } |
(5) Traverse list
Note that List_for_each is just a macro substitution.
| /** * List_entry-get the struct for this entry * @ptr: The &struct list_head pointer. * @type: The type of the struct this are embedded in. * @member: The name of the list_struct within the struct. */ #define List_entry (PTR, type, member) \ Container_of (PTR, type, member) /** * List_first_entry-get the "a" list * @ptr: The "list head" to take the element from. * @type: The type of the struct this are embedded in. * @member: The name of the list_struct within the struct. * * Note, the list is expected to being not empty. */ #define List_first_entry (PTR, type, member) \ List_entry ((PTR)->next, type, member) /** * 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; prefetch (pos->next), Pos!= (head); \ pos = pos->next) |
You often need to use container_of and offset when traversing, such as List_for_each_entry (POS, head, member), which is to traverse the head list, and the head list pointer type is member (string), Member is one of the members of the POS type structure, and then the structure body pointer is obtained based on container_of. [This technique is often found in kernel source code] 2 Examples of the use of linked lists
The following is an example of dmatest.c. Requirements: The DMA test program needs to implement multiple channels, and the channel supports multiple threads and requires support to access each other.
First, because the requirements are accessed each other, the struct member variable is immediately thought of as shown in the following code.
struct channel_t{ struct thread_t used[m]; } struct info_t{ struct channel_t used[m]; }; static struct info_t info; |
However, the disadvantage is also obvious, the application of fixed size space, either waste resources, or insufficient resources. Write to this, you can immediately launch the use of linked lists, but the core provided by the list does not have data fields are pointer fields, how to design a key.
Here, DMATEST.C gives the reference answer by adding a node node to each member as the mediation node. as shown below.
| struct thread_t { struct List_head node ; } struct channel_t { struct List_head node ; struct List_head threads ; } struct info_t { struct LIST_ Head channels ; }; Static info_t Info = |