Linux 内核list_head 学习

  这是八个宏定义。当中,pos 是指向 list_head 的指针,而 head 是双链表 list_head 中的指针,定义了从哪个地方起始遍历这么些链表。那个宏的职能便是对多个双向循环链表举办遍历。

};

图片 1

  list_head: 在 linux/types.h 中定义

  struct list_head *next, *prev;

650#define container_of(ptr, type, member) ({ \

  list_entry 实际上正是 container_of。

struct list_head name = LIST_HEAD_INIT(name)

335 container_of(ptr, type, member)

/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_GENERIC_CURRENT_H
#define __ASM_GENERIC_CURRENT_H

#include <linux/thread_info.h>

#define get_current() (current_thread_info()->task)
#define current get_current()

#endif /* __ASM_GENERIC_CURRENT_H */

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

19struct list_head {

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

make it simple, make it happen

334#define list_entry(ptr, type, member) \

  能够旁观,这里运用的是链表相关的操作来访谈子进度。大家领略, task_struct 是寄存在八个双向循环链表 task_list(职务队列)中的,而一个task_struct 包括了八个切实进度的有所新闻,因而,大家只供给找到子进度的 task_struct 即能够访问子进度了,上面代码正是这么做的。那么,具体是如何找到子进度的长河描述符 task_struct的呢?下直面上边的代码举行详细深入分析:

(char*)__mptr 之所以要强制类型转变为char是因为地址是以字节为单位的,而char的长短正是一个字节。

透过那样豆蔻梢头种实现方式确立的链表,节点都以通过list_head类型的变量相连接的,那么大家怎么由list_head类型得指针拿到中间有个别节点类型的指针呢?大家来看这么二个操作:list_entry(p,t,m卡塔尔,个中t是链表的节点类型,m是节点内list_head类型的变量名,p是指向该变量的指针,该操功效于从list_head指针拿到指向链表节点的指针。

1)、((TYPE *卡塔尔 0卡塔尔(英语:State of Qatar) : 将 0 调换到 TYPE 类型的指针。那证明了一个指向性 0 的指针,且那几个指针是 TYPE 类型的;

大家构成地点的构造来看

24#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) /*总计出变量在布局中的偏移量(以字节为单位卡塔尔国*/  

 

 

linux内核通过定义list_head以致对于list_head上的黄金时代组操作完结对两样门类的循环链表的同类操作,这种做法幸免了对于分歧数据类型的循环链表定义再次的操作函数,使代码获得了足够的接纳,是大器晚成种特别管用的编制程序方法。

struct task_struct *task;
struct list_head *list;

list_for_each(list,&current->children){
    task = list_entry(list,struct task_struct,sibling);      
}

将new所代表的构造体插入head所管理的双向链表的头节点head之后: (即插入表头)

21};

  container_of : 在 linux/kernel.h 中定义

图片 2

图1

#define offsetof(TYPE,MEMBER)  ((size_t) &((TYPE *)0) -> MEMBER)        // 获得成员变量member基于其所在结构的地址的偏移量,该宏在 linux/stddef.h 中有定义

  const typeof( ((type*)0)->member ) *__mptr=(ptr);\

任何时候大家来看任性风流洒脱种数据布局的循环链表(如图1卡塔尔(قطر‎,链表的各种节点中步入了一个list_head类型的变量,节点的其它变量大肆。(注意:每个指针所指向的任务不是节点数据的初始地点,而是list_head类型变量的早先地址。卡塔尔(英语:State of Qatar)

  系统中每种进度必有叁个父进程,相应的,每一种进程也足以由零个要么四个子进度。具有同二个父进程的有着进度被叫做兄弟。进程之间的关系存放在进程描述符 task_struct 中。每个 task_struct 都带有七个针对性其父进度 task_struct 的指针 parent,还应该有叁个被喻为 children 的子进程链表。

   

小说中引用的代码来源于LX讴歌ZDX,所深入分析的木本版本是v2.6.31。

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:    the pointer to the member.
 * @type:    the type of the container struct this is embedded in.
 * @member:    the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({                \
    void *__mptr = (void *)(ptr);                    \
    BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) &&    \
             !__same_type(*(ptr), void),            \
             "pointer type mismatch in container_of()");    \
    ((type *)(__mptr - offsetof(type, member))); })

{

20 struct list_head *next, *prev;

 

  struct list_head node;

list_head的定义:

struct task_struct *my_parent = current -> parent;

  return head->next == head;

651 const typeof( ((type *)0)->member ) *__mptr = (ptr); \ /*_mptr与ptr类型值都同样,是ptr的叁个拷贝*/

 

双向链表的插入操作 -- list_add()

652 (type *)( (char *)__mptr - offsetof(type,member) );}) /*地址减去偏移量(以字节为单位卡塔尔(قطر‎就可以*/

  能够接纳以下方法访问子进度:

其实list_head不是拿来单独用的,它平常被嵌到别的组织中,如:

linux内核通过定义list_head以致对于list_head上的生机勃勃组操作完结对两样类型的循环链表...

  深入分析一下 offsetof 宏:

 

图片 3

4)、((size_t) &((TYPE *卡塔尔(قطر‎ 0卡塔尔(英语:State of Qatar) -> MEMBE昂科威卡塔尔(英语:State of Qatar): 强制类型调换来 size_t 类型。 

(TYPE *卡塔尔(英语:State of Qatar)0 它意味着将 0地址强制调换为TYPE类型,((TYPE *卡塔尔(英语:State of Qatar)0)-> MEMBECR-V 约等于从0址址找到TYPE 的分子MEMBE奥迪Q5 。

/*
 * how to get the thread information struct from C
 */
static inline struct thread_info *current_thread_info(void) __attribute_const__;

static inline struct thread_info *current_thread_info(void)
{
    return (struct thread_info *)
        (current_stack_pointer & ~(THREAD_SIZE - 1));        // 让SP堆栈指针与栈底对齐    
}    

   

 

像这种类型应该相比清楚了,即求 p 的积极分子 node之处,只可是p 为0地址,从0地址最初算成员node之处,也便是成员 node 在布局体 struct file_node中的偏移量。offset宏正是算MEMBEMurano在TYPE中的偏移量的。

二、子进度的访谈方法

   

意气风发、父进度的拜见方法

供给注意的少数是,头结点head是不应用的,那点须求在乎。

  list_for_each: 在linux/list.h 中定义

大浪涛沙列举部分双链表的常用操作:

/**
 * 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_head within the struct.
 */
#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)

  list->next = list;

  Linux系统中,进程之间有三个显眼的继续关系,全体进度都以 PID 为1的 init 进程的儿孙。内核在系统运营的尾声阶段运营 init 进度。该进程读取系统的初步化脚本(initscript)并试行别的的连带程序,最后产生系统运行的不论什么事进程。

{

  对于当前经过,能够采纳上边代码访谈其父进度,得到其经过描述符:

  container_of(ptr,type,member)

  container_of 实现了基于贰个协会中的贰个分子变量的指针来收获指向任何布局的指针的作用。个中,offsetof 也是多少个宏,它的职能是收获成员变量基于其所在构造的地点的偏移量,定义如下:

  __list_add(new, head, head->next);

  而 current_thread_info() 函数在 arch/arm/include/asm/thread_info.h 中有定义:

这里提到到五个宏,仍有一点点复杂的,我们叁个叁个来看:

   能够见到,current 实际上是指向当前实践进程的 task_struct 指针的。

#define container_of(ptr,type,member) ( {\

  显然,list_head 其实正是一个双向链表,况兼日常的话,都以双向循环链表。

#define list_entry(ptr,type,member)\

#define list_for_each(pos, head) \
    for (pos = (head)->next; pos != (head); pos = pos->next)

struct file_node{

2)、((TYPE *卡塔尔 0卡塔尔 -> MEMBECRUISER: 访谈构造中的成员MEMBEEnclave,三个针对 0 的 TYPE 类型的布局;分明,MEMBE安德拉 的地址就是偏移地址;