Data doubly linked list of Linux kernels
Guide |
The Linux kernel itself implements a doubly linked list, which can be found in include/linux/list.h. We will begin by introducing the data structure in the kernel from a doubly linked list structure. Why? Because it is used extensively in the kernel, you just need to retrieve it in free-electrons.com. |
first, let's look at the main structure in Include/linux/types.h:
struct List_head { struct list_head *next, *prev;};
you may have noticed that this is different from the way you've seen it before in a doubly linked list.
For example, this is true in the glib library:
struct GList {gpointer data; GList *next; GList *prev;};
in general, a linked list structure contains a pointer to an item.
But the list implementation in the Linux kernel did not do so. So here's the question: Where does the linked list keep the data? In fact, the linked list implemented in the kernel is an intrusive list (intrusive list). An intrusive list does not hold data within a node-its nodes contain only pointers to the front and back nodes, and pointers to the data portion of the linked list node-the data is appended to the list. This makes the data structure universal and does not need to consider the type of node data.
For example:
struct Nmi_desc {spinlock_t lock;struct list_head head;};
let's look at a few examples to understand how the List_head is used in the kernel.
As mentioned above, there are many, many different places in the kernel that use lists. Let's take a look at an example of using the miscellaneous character driver inside. The miscellaneous character driver API in DRIVERS/CHAR/MISC.C is used to write small drives that handle small hardware or virtual devices. These drivers share the same main device number:
#define MISC_MAJOR 10
but all have their own different secondary device numbers.
For example:
Ls-l/dev | grep 10crw-------1 root root 235 mar 12:01 autofsdrwxr-xr-x root root 12:01 cpucrw-------1 root Roo T, 12:01 cpu_dma_latencycrw-------1 root root, 203 mar 12:01 cusedrwxr-xr-x 2 root root 21 12: Dricrw-rw-rw-1 root root, 229 mar 12:01 fusecrw-------1 root root, 228 Mar 12:01 hpetcrw-------1 root ro OT, 183 Mar 12:01 hwrngcrw-rw----+ 1 root KVM, 232 mar 12:01 KVMCRW-RW----1 root disk, 237 Mar 12:01 Lo OP-CONTROLCRW-------1 root root, 227 mar 12:01 mcelogcrw-------1 root root, Mar 12:01 MEMORY_BANDWIDTHCRW -------1 root root, 12:01 network_latencycrw-------1 root root, and 12:01 network_throughputcrw-r- ----1 root kmem, 144 mar 12:01 nvrambrw-rw----1 root disk 1, Mar 12:01 ram10crw--w----1 root TTY 4, Mar 12:01 TTY10CRW-RW----1 root dialout 4, 12:01 ttys10crw-------1 root root, April 12:01 VGA_ARBITERCRW -------1 ROot Root, 137 Mar 12:01 VHCI
Now let's see how it uses the linked list. First look at the structure of the body Miscdevice:
struct Miscdevice{int minor;const char *name;const struct file_operations *fops;struct list_head list;struct device *pare Nt;struct device *this_device;const char *nodename;mode_t mode;};
you can see the fourth variable of the struct Miscdevice list is a linked list of all registered devices.
You can see the definition of this list at the beginning of the source code file:
Static List_head (misc_list);
It is actually an extension of the variable defined with the List_head type.
#define LIST_HEAD (name) struct List_head name = List_head_init (name)
then use the macro list_head_init to initialize the
This will use the address of the variable name to populate the two variables of the prev and next struct bodies.
#define LIST_HEAD_INIT (name) {& (name), & (name)}
now look at the function Misc_register for registering miscellaneous devices.
It initializes the miscdevice->list with a function init_list_head at the outset.
Init_list_head (&misc->list);
the effect is the same as the macro list_head_init.
static inline void Init_list_head (struct list_head *list) {list->next = List;list->prev = LIST;}
Next, after the function device_create has created the device,
We will use the following statement to add the device to the device chain list:
List_add (&misc->list, &misc_list);
The kernel file list.h provides an API interface for adding new items to the list.
Let's take a look at its implementation:
static inline void List_add (struct list_head *new, struct list_head *head) {__list_add (new, head, Head->next);}
the intrinsic function __list_add is actually called with 3 specified arguments:
New-item.
Head-new item will be inserted at the back of head
Head->next-an item that is behind the head before inserting.
The implementation of __list_add is very simple:
static inline void __list_add (struct list_head *new,struct list_head *prev,struct list_head *next) {Next->prev = New;ne W->next = Next;new->prev = Prev;prev->next = new;}
here, we have added a new item between Prev and next.
So the misc list we defined with the macro List_head_init at the beginning contains a forward pointer and a backward pointer to miscdevice->list.
Here's another question: How do you get the contents of a list? Here's a special macro:
#define List_entry (PTR, type, member) container_of (PTR, type, member)
three parameters are used:
PTR-pointer to structure List_head;
Type-struct types;
Member-the name of the variable in the structure body type list_head;
For example:
const struct Miscdevice *p = list_entry (V, struct miscdevice, list)
Then we can use P->minor or p->name to access the Miscdevice. Let's take a look at the implementation of List_entry:
#define List_entry (PTR, type, member) container_of (PTR, type, member)
As we can see, it only calls the macro container_of with the same parameters. At first glance this macro is quite strange:
#define CONTAINER_OF (PTR, type, member) ({Const typeof (((type *) 0)->member) *__mptr = (PTR); (Type *) ((char *) __mptr-offsetof (Type,member));})
First you can notice that the curly braces contain two expressions.
The compiler executes all the statements in curly braces, and then returns the value of the last expression.
For example:
#include <stdio.h>int main () {int i = 0;printf ("i =%d\n", ({++i; ++i;})); return 0;}
will eventually print out 2.
the next point is TypeOf, which is also very simple.
As you can understand from the name, it simply returns the type of the given variable. When I first saw the implementation of macro container_of, the strangest thing I felt was the expression ((type *) 0) of 0. In fact, this pointer cleverly calculates the offset from a specific variable in the struct, where 0 is exactly the 0 offset in the width of the bit.
For example:
#include <stdio.h>struct s {int field1;char field2;char field3;}; int main () {printf ("%p\n", & (struct s*) 0)->field3); return 0;}
The results show 0x5.
The next macro Offsetof calculates the offset from the start address of the struct to a given structure field.
Its implementation is similar to the above:
#define OFFSETOF (Type, MEMBER) ((size_t) & ((TYPE *) 0)->member)
Now let's summarize the macro container_of. Simply given the address, name, and type of the struct container for the List_head type field in the struct, it can return the starting address of the struct body. In the first line of the macro definition, a pointer to the struct member variable PTR is declared __mptr, and the address of the PTR is assigned to it. Now PTR and __mptr point to the same address. Technically we don't need this line, but it's easy to do type checking. The first line guarantees that the specific struct (parameter type) contains the member variable member. The second line of code calculates the offset of the member variable relative to the starting address of the struct using the macro offsetof, then subtracts the offset from the address of the struct, and finally gets the struct.
of course, List_add and list_entry are not <linux/list.h>.
The only feature provided. The implementation of the doubly linked list also provides the following APIs:
List_addlist_add_taillist_dellist_replacelist_movelist_is_lastlist_emptylist_cut_positionlist_splicelist_for_ Eachlist_for_each_entry
And so many other APIs.
Free to provide the latest Linux technology tutorials Books, for open-source technology enthusiasts to do more and better: http://www.linuxprobe.com/
Data doubly linked list of Linux kernels