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三者之间的关系网,知道其中任何一个,都可以快速的访问到另外两个,提高了数据存取的效率。
 
 
posted @ 2017-10-21 18:38  泡面狂  阅读(8051)  评论(0编辑  收藏  举报