Linux kernel data structure: Linux doubly linked list

Source: Internet
Author: User
Tags prev

The Linux kernel provides a set of bidirectional linked list implementations that you can find in include/linux/list.h. We start with a two-way list of data structures in the Linux kernel, as this is the most widely used data structure in the Linux kernel, which you can view here.


First, let's take a look at the main structural bodies:


struct List_head {

struct List_head *next, *prev;

};


You can see that it differs significantly from the common structure implementations, such as the bidirectional list implementations used in glib.


struct GList {

Gpointer data;

GList *next;

GList *prev;

};


In general, the list structure includes a pointer to the data, but the list of the Linux kernel does not contain this implementation. So the first question: How does a linked list store data? The Linux kernel implements a linked list called intrusive (intrusive list), which does not contain data in the linked list structure, but only provides pointers for maintaining the forward and back access structures. This implementation makes the linked list data structure very common because it does not need to focus on the specific data types maintained by the linked list.


Like what:


struct NMI_DESC {

spinlock_t lock;

struct List_head head;

};


Let's look at some specific examples of kernels using List_head. As mentioned earlier, many of the modules in the Linux kernel use List_head. Here we take the kernel miscellaneous character device driver (Miscellaneous character drivers) partial implementation as an example. Driven API in DRIVERS/CHAR/MISC.C, which implements simple hardware peripherals as well as virtual device drivers, this driver shares the main device number (Major numbers):


#define MISC_MAJOR 10


Each device has its own secondary device number, which can be seen in the following examples:



Now let's look at how the device driver uses the list of lists to maintain the device, first, let's look at the struct definition of miscdevice:


struct Miscdevice

{

int minor;

const char *name;

const struct File_operations *fops;

struct List_head list;

struct device *parent;

struct device *this_device;

const char *nodename;

mode_t mode;

};


You can see the fourth member list of Miscdevice, which is the structure used to maintain the registered device list. In the header of the source code, we can see the following definitions:


Static List_head (misc_list);


This definition of macro expansion, you can see is used to define the List_head type variable:


#define LIST_HEAD (name)

struct List_head name = List_head_init (name)


LIST_HEAD_INIT This macro is used to initialize a two-way pointer to a defined variable:


#define LIST_HEAD_INIT (name) {& (name), & (name)}


Now I can see how the function Misc_register is registered with the device. The first is to initialize the Miscdevice->list member variables with Init_list_head:


Init_list_head (&misc->list);


This action is consistent with the LIST_HEAD_INIT macro:


static inline void Init_list_head (struct list_head *list)

{

List->next = list;

List->prev = list;

}


Next, the device is created with the function device_create, and the device is added to the MISC device list:


List_add (&misc->list, &misc_list);


The kernel's list.h file provides an API to add nodes to the list, and here is the implementation of the add operation:


static inline void List_add (struct list_head *new, struct list_head *head)

{

__list_add (New, head, Head->next);

}


The function implementation is simple, which is to call the internal __list_add when the entry parameter is converted to three parameters:


New–new entry;

Head–list head after which would be inserted new item;

Head->next–next Item after list head.

The implementation of the _list_add function is much simpler:


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;

}


This sets the prev and next pointers for the newly added node, which associates the miscdevice->list structure with a two-way pointer to the misc linked list previously defined using List_head_init.


Another problem here is how to get the data in the list, List_head provides a special macro to get the data pointer.


#define List_entry (PTR, type, member)

Container_of (PTR, type, member)


Here are three parameters


Ptr:list_head Structure Pointers

Type: The type of struct that corresponds to the data

Member: member variable name corresponding to List_head member in data

Examples are as follows:


const struct Miscdevice *p = list_entry (V, struct miscdevice, list)


Next we have enough access to the various members of Miscdevice, such as P->minor, P->name, and so on, and we look at the implementation of List_entry:


#1216. Www.qixoo.qixoo.com/define list_entry (PTR, type, member)

Container_of (PTR, type, member)


Its implementation is very simple, that is, using the incoming parameter call Container_of macro, the implementation of the macro is as follows:


#define CONTAINER_OF (PTR, type, member) ({

Const typeof (((type *) 0)->member) *__mptr = (PTR);

(Type *) ((char *) __mptr-offsetof (Type,member));})


Note that the macro uses curly brace expressions, and for curly brace expressions, the compiler expands all expressions and returns with the result of the last expression.


As an example:


#include <stdio.h>


int main () {

int i = 0;

printf ("i =%dn", ({++i; ++i;}));

return 0;

}


The output result is 2.


Another key is the TypeOf keyword, which is very simple, as is its name, and the result of this keyword return is the type of the variable. When I first saw this macro, the most surprising thing to me was the expression ((type*) 0) in the 0 value, in fact, the use of 0 value as the address of this is the member variable to get the relative offset address within the struct clever implementation, we look at an example:


#include <stdio.h>


struct S {

int field1;

Char Field2;

Char field3;

};


int main () {

printf ("%pn", & (struct s*) 0)->field3);

return 0;

}


The output result is 0x5.


There is also a macro that is specifically used to get the offset of a member variable in a struct, and its implementation is very similar to the previously mentioned macro:


#define OFFSETOF (Type, MEMBER) ((size_t) & ((TYPE *) 0)->member)


The CONTAINER_OF macro is reviewed here, and the CONTAINER_OF macro returns the memory address of the struct's corresponding data through the List_head members in the struct. The first line of the macro defines a pointer to the List_head member __mptr, and assigns the PTR address to __mptr. From a technical implementation point of view, this line of definition is not really required, but this is very meaningful for type checking. This line of code ensures that the member corresponding member exists in the struct (type). The second line uses the Offsetoff macro to calculate the memory address of the struct containing the member, which is so simple.


Of course List_add and list_entry are not all functions in <linux/list.h>, for the bidirectional linked list List_head, the kernel also provides the following interfaces:


List_add

List_add_tail

List_del

List_replace

List_move

List_is_last

List_empty

List_cut_position

List_splice


Unfinished, it is necessary to say that the kernel code is not just these interfaces.

Linux kernel data structure: Linux doubly linked list

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.