【linux进程管理-01】task_struct、thread_info、stack之间的关系(x86)

内核版本:2.6.34

1. task_struct

task_struct 结构体即所说的进程描述符,包含了内核管理一个进程所需的全部信息。
位置:include/linux/sched.h

struct task_struct {
	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
	void *stack;
	atomic_t usage;
	unsigned int flags;	/* per process flags, defined below */
	unsigned int ptrace;

	int lock_depth;		/* BKL lock depth */
	......
}

其中,*stack 是指向内核栈的指针。

2. stack

stack 即所说的进程内核栈,是一段大小为2个page(32位:PAGE_SIZE=4KB;64位:PAGE_SIZE=8KB)的内存空间。
位置:include/linux/sched.h

union thread_union {
	struct thread_info thread_info;
	unsigned long stack[THREAD_SIZE/sizeof(long)];
};

从联合体thread_union的定义可以看出,thread_info和内核栈是共享内存空间的。换句话说,内核栈的开头就是一个thread_info结构。

3. thread_info

thread_info 即内核栈开头(注意这里“开头”指的是最低地址)的一段数据结构。
位置:arch/x86/include/asm/thread_info.h

struct thread_info {
	struct task_struct	*task;		/* main task structure */
	struct exec_domain	*exec_domain;	/* execution domain */
	__u32			flags;		/* low level flags */
	__u32			status;		/* thread synchronous flags */
	__u32			cpu;		/* current CPU */
	int			preempt_count;	/* 0 => preemptable,
	......
};

其中,*task 又指向了进程描述符stack_struct


三者的关系见下图,可以大概猜测一下进程在创建的时候的一个流程:

  • 首先给进程初始化一个task_struct(后面对进程的管理都仰仗这位老兄了);

  • 分配一个大小为2*PAGE_SIZE的内存空间,并把首地址赋给task_struct->stack;

  • 将stack头部的一段空间当作thread_info结构来使用,并把thread_info->task指向task_struct;

    具体创建流程可以参考:浅谈Linux内核创建新进程的全过程

stack内核栈最高的8字节为预留,紧跟着的就是pt_regs,当进程从用户态陷入内核态时候,用户态的上下文信息保存在pt_regs数据结构。
在这里插入图片描述

4. 通过 task_struct 找内核栈

从上图可知,可以通过task_struct->stack找到内核栈,内核中已经定义好了接口。
位置:include/linux/sched.h

#define task_stack_page(task)	((task)->stack)

5. 通过 task_struct 找 thread_info

先找到内核栈,然后强转成thread_info就可以了。
位置:include/linux/sched.h

#define task_thread_info(task)	((struct thread_info *)(task)->stack)

6. 通过 task_struct 找 pt_regs

以X86_32为例,先找到内核栈,然后指针位移到最高地址,减掉8个字节的预留字节,即指向了pt_regs的末尾地址,强转成pt_regs结构,然后自减1,即指向了pt_regs的首地址。
而X86_64,使用到了task_struct中的thread_struct成员,后面再补充。
位置:arch/x86/include/asm/processor.h

// X86_32
#define task_pt_regs(task)                                             \
({                                                                     \
       struct pt_regs *__regs__;                                       \
       __regs__ = (struct pt_regs *)(KSTK_TOP(task_stack_page(task))-8); \
       __regs__ - 1;                                                   \
})

// X86_64
#define task_pt_regs(tsk)	((struct pt_regs *)(tsk)->thread.sp0 - 1)

7. 通过内核栈找 task_struct

内核是通过current宏来获取当前进程的task_struct的。可以看出current先通过current_thread_info()获取到进程的thread_info,然后通过其task指针找到task_struct。
那么,current_thread_info()又是怎么找到当前进程的thread_info呢?这就与硬件体系结构相关了,后面再补充。
位置:include/asm-generic/current.h

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

参考

进程管理(五)–linux进程内核栈
linux 进程及调度基础知识
《Linux内核设计与实现(第三版)》

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值