zoukankan      html  css  js  c++  java
  • linux内核中task_struct与thread_info及stack三者的关系

    在linux内核中进程以及线程(多线程也是通过一组轻量级进程实现的)都是通过task_struct结构体来描述的,我们称它为进程描述符。而thread_info则是一个与进程描述符相关的小数据结构,它同进程的内核态栈stack存放在一个单独为进程分配的内存区域。由于这个内存区域同时保存了thread_info和stack,所以使用了联合体来定义,相关数据结构如下(基于4.4.87版本内核):
     thread_union联合体定义:
    union thread_union {
        struct thread_info thread_info;
        unsigned long stack[THREAD_SIZE/sizeof(long)];
    };

    thread_info结构体定义:

    struct thread_info {
        unsigned long        flags;        /* low level flags */
        mm_segment_t        addr_limit;    /* address limit */
        struct task_struct    *task;        /* main task structure */
        int            preempt_count;    /* 0 => preemptable, <0 => bug */
        int            cpu;        /* cpu */
    };
    task_struct的结构比较复杂,只列出部分成员变量,完整的可以在下面这个网站直接查看对应版本的内核代码
    struct task_struct {
        volatile long state;
        void *stack; 
     //...
    #ifdef CONFIG_SMP
        int on_cpu;
        int wake_cpu;
    #endif
        int on_rq;
      //...
    #ifdef CONFIG_SCHED_INFO
        struct sched_info sched_info;
    #endif
     //... 
        pid_t pid;
        pid_t tgid;
     //...
    }; 
    用一副图来表示:
     
    这样设计的好处就是,得到stack,thread_info或task_struct任意一个数据结构的地址,就可以很快得到另外两个数据的地址。
    我们可以通过crash工具在ubuntu系统上做个实验,来窥视一下某个进程的进程描述符
    如果通过crash分析内核数据结构,可参考:
    这里以进程systemd进程为例,其pid=1
    crash> task 1
    PID: 1      TASK: ffff88007c898000  CPU: 1   COMMAND: "systemd"
    struct task_struct {
      state = 1,
      stack = 0xffff88007c894000,
      usage = {
        counter = 2
      },
    。。。
    可以看到systemd进程的task_struct结构体指针task=0xffff88007c898000
    通过task->stack这个结构体成员即可定位到进程的内核栈地址 stack=0xffff88007c894000
    另外从之前的图可以看到,thread_info和stack处于同一地址空间,且thread_info在这段地址空间的最低地址处,而且这个地址空间是以THREAD_SIZE对齐的,所以只要将stack地址的最低N位变为0,即可得到thread_info的地址(2^N=THREAD_SIZE)
    例如当THREAD_SZIE=8K时,systemd的thread_info地址就等于0xffff88007c894000&(~(0x1FFF)) = 0xffff88007c894000
    crash> * thread_info 0xffff88007c894000
    struct thread_info {
      task = 0xffff88007c898000,
      flags = 0,
      status = 0,
      cpu = 0,
      addr_limit = {
        seg = 140737488351232
      },
      sig_on_uaccess_error = 0,
      uaccess_err = 0
    }
     
      而通过thread_info->task这个成员变量,又能访问到进程的task_struct结构体,这样就形成了task_struct, thread_info,stack三者之间的关系网,知道其中任何一个,都可以快速的访问到另外两个,提高了数据存取的效率。
     
     
  • 相关阅读:
    josn类库引用
    WPF圆角按钮
    C#实现某一属性值变化时触发事件 Form1_changeEvent是对应的事件
    C#winform生成安装包
    特性
    反射可以动态调用对象(一般是类)的名称,属性,方法等。具体见下。重要
    原子操作 和Inerlocked 常用于多线程同步
    spingboot 配置多个数据源报错
    Address already in use: JVM_Bind 端口被占用的几个解决办法
    数据库问题(一)
  • 原文地址:https://www.cnblogs.com/yanghaizhou/p/7705520.html
Copyright © 2011-2022 走看看