快捷搜索:

您的位置:澳门新葡4473网站 > 澳门新葡4473网站 > 澳门新葡4473网站Linux内核学习笔记(2)-- 父进程

澳门新葡4473网站Linux内核学习笔记(2)-- 父进程

发布时间:2019-10-16 00:23编辑:澳门新葡4473网站浏览(197)

      Linux系统中,进度之间有贰个远近有名的接轨关系,全体进度都是 PID 为1的 init 进度的后生。内核在系统运行的末尾阶段运行 init 进度。该进度读取系统的领头化脚本(initscript)并实施别的的有关程序,最终成就系统运营的一切经过。

    Linux 内核list_head 学习(一)

      系统中每种进度必有二个父进度,相应的,每种进程也得以由零个依旧七个子进度。具备同八个父进度的兼具进程被称为兄弟。过程之间的涉及贮存在进程描述符 task_struct 中。每个 task_struct 都包括多个对准其父进程 task_struct 的指针 parent,还应该有三个被称为 children 的子进度链表。

     

    在Linux内核中,提供了一个用来创设双向循环链表的布局 list_head。即便linux内核是用C语言写的,可是list_head的引进,使得内核数据结构也能够具有面向对象的特征,通过动用操作list_head 的通用接口很轻易完结代码的选定,有一点类似于C++的存在延续机制(希望有机缘写篇小说商量一下C语言的面向对象机制)。下面便是kernel中的list_head结构定义:

    一、父进度的拜见方法

    struct list_head {

      对于方今进度,可以行使下边代码访问其父进程,获得其进程描述符:

      struct list_head *next, *prev;

    struct task_struct *my_parent = current -> parent;
    

    };

       其中,current 是三个宏,在 linux/asm-generic/current.h中有定义:

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

    /* 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 */
    

    亟需专一的少数是,头结点head是不行使的,那一点需求在乎。

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

    使用list_head组织的链表的构造如下图所示:

    /*
     * 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堆栈指针与栈底对齐    
    }    
    

       

       能够看出,current 实际上是指向当前试行进度的 task_struct 指针的。

    澳门新葡4473网站 1

     

    list_head那个协会看起来怪怪的,它竟从未数据域!所以看见这么些布局的人第一影响就是大家怎么访谈数据?

    二、子进度的拜见方法

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

      可以采取以下方法访谈子进度:

    struct file_node{

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

      char c;

      可以见见,这里运用的是链表相关的操作来访问子进程。大家知晓, task_struct 是寄放在一个双向循环链表 task_list(职务队列)中的,而一个task_struct 蕴含了贰个有血有肉进程的富有音信,由此,大家只须求找到子进度的 task_struct 即能够访谈子进度了,上边代码就是如此做的。那么,具体是何许找到子进度的进程描述符 task_struct的呢?上边前遇到下边包车型客车代码举行详细分析:

      struct list_head node;

      list_head: 在 linux/types.h 中定义

    };

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

    此时list_head就当作它的父结构中的二个分子了,当大家知道list_head的地点(指针)时,大家得以经过list.c提供的宏 list_entry 来取得它的父结构的地方。上面我们来探问list_entry的实现:

      显然,list_head 其实就是一个双向链表,而且日常的话,都以双向循环链表。

    #define list_entry(ptr,type,member)

     

      container_of(ptr,type,member)

      list_for_each: 在linux/list.h 中定义

       

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

    #define offsetof(TYPE,MEMBER) ((size_t)&((TYPE *)0)->MEMBER)

      那是贰个宏定义。在这之中,pos 是指向 list_head 的指针,而 head 是双链表 list_head 中的指针,定义了从何地带头遍历那几个链表。那一个宏的功用正是对一个双向循环链表实行遍历。

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

     

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

      list_entry: 在 linux/list.h 中定义,也是一个宏定义

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

    /**
     * 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_entry 实际上便是 container_of。

    此处提到到四个宏,如故有一些复杂的,大家一个二个来看:

     

    #define offsetof(TYPE,MEMBER) ( (size_t)& ((TYPE *)0)-> MEMBER )

      container_of : 在 linux/kernel.h 中定义

    大家知晓 0 地址内容是不可能访问的,但 0地址的地方大家照旧得以访问的,这里用到叁个取址运算符

    /**
     * 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))); })
    

    (TYPE *)0 它意味着将 0地址强制转换为TYPE类型,((TYPE *)0)-> MEMBE奥迪Q5 也正是从0址址找到TYPE 的积极分子MEMBEGL450 。

      container_of 实现了依靠叁个构造中的三个成员变量的指针来获取指向任何结构的指针的魔法。当中,offsetof 也是一个宏,它的机能是得到成员变量基于其所在结构的地点的偏移量,定义如下:

    咱俩构成地方的布局来看

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

    struct file_node{

      深入分析一下 offsetof 宏:

      char c;

    1)、((TYPE *) 0) : 将 0 调换来 TYPE 类型的指针。那申明了四个针对性 0 的指针,且那个指针是 TYPE 类型的;

      struct list_head node;

    2)、((TYPE *) 0) -> MEMBE昂Cora: 访谈结构中的成员MEMBELX570,一个对准 0 的 TYPE 类型的布局;分明,MEMBEXC60 的地点正是偏移地址;

    };

    3)、&((TYPE *) 0) -> MEMBE本田CR-V:取多少成员MEMBECRUISER的地址(不是按位与,不要看错了);

    将实参代入 offset( struct file_node, node );最后将变为那样:

    4)、((size_t) &((TYPE *) 0) -> MEMBE途锐): 强制类型转变来 size_t 类型。 

    ( (size_t) & ((struct file_node*)0)-> node );那样看的仍旧不很领会,大家再变变:

     

    struct file_node *p = NULL;

    & p->node;

    这么应有比较清楚了,即求 p 的分子 node的地点,只不过p 为0地址,从0地址起始算成员node的地方,相当于成员 node 在构造体 struct file_node中的偏移量。offset宏便是算MEMBE奥德赛在TYPE中的偏移量的。

    咱俩再看第贰个宏

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

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

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

    其一宏是由四个语句组成,最后container_of再次来到的结果就是首个表明式的值。这里__mptr为中等变量,那就是list_head指针类型,它被开头化为ptr的值,而ptr就是方今所求的结构体中list_head节点的地方。为啥要用中间变量,那是思虑到安全性因素,假如传进来三个ptr++,全部ptr++放在叁个表明式中会有副效能,像 (p++)+(p++)之类。

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

    container_of的值是三个地点相减,

    刚说了__mptr是结构体中list_head节点的地方,offset宏求的是list_head节点MEMBE大切诺基在结构体TYPE中的偏移量,那么__mptr减去它所在结构体中的偏移量,正是结构体的地点。

    所以list_entry(ptr,type,member)宏的功用正是,由结构体成员地址求结构体地址。当中ptr 是所求结构体中list_head成员指针,type是所求结构体类型,member是布局体list_head成员名。通过下图来总括一下:

       

    澳门新葡4473网站 2

       

    此起彼落列举部分双链表的常用操作:

    双向链表的遍历——list_for_each

    //注:这里prefetch 是gcc的多少个优化增选,也得以不用

    #define list_for_each(pos, head)

             for (pos = (head)->next; prefetch(pos->next), pos != (head);

                     pos = pos->next)

       

    改变双向链表的头结点——LIST_HEAD()

    LIST_HEAD() -- 生成多少个名称为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;

    }

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

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

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

    {

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

    }

    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;

    }

    从list中除去结点——list_del()

    static inline void list_del(struct list_head *entry)

    {

      __list_del(entry->prev, entry->next);

      entry->next = LIST_POISON1;

      entry->prev = LIST_POISON2;

    }

    static inline void __list_del(struct list_head * prev, struct list_head * next)

    {

      next->prev = prev;

      prev->next = next;

    }

       

    推断链表是还是不是为空(假若双向链表head为空则重临真,不然为假)——list_empty()

    static inline int list_empty(const struct list_head *head)

    {

      return head->next == head;

    }

       

    make it simple, make it happen

     

    本文由澳门新葡4473网站发布于澳门新葡4473网站,转载请注明出处:澳门新葡4473网站Linux内核学习笔记(2)-- 父进程

    关键词: