为什么 thread_info 应该是 task_struct 中的第一个元素?
Why thread_info should be the first element in task_struct?
在阅读Linux内核源代码时,我发现一件事让我很困惑。
在task_struct
中是这样写的
struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
/*
* For reasons of header soup (see current_thread_info()), this
* must be the first element of task_struct.
*/
struct thread_info thread_info;
#endif
...
};
完整的代码是here,你能告诉我为什么这个必须是task_struct
中的第一个元素吗?
评论说:
/*
* For reasons of header soup (see current_thread_info()), this
* must be the first element of task_struct.
*/
struct thread_info thread_info;
然后<linux/thread_info.h>
:
/*
* For CONFIG_THREAD_INFO_IN_TASK kernels we need <asm/current.h> for the
* definition of current, but for !CONFIG_THREAD_INFO_IN_TASK kernels,
* including <asm/current.h> can cause a circular dependency on some platforms.
*/
#include <asm/current.h>
#define current_thread_info() ((struct thread_info *)current)
#endif
换句话说,<linux/thread_info.h>
不能包含<linux/sched.h>
,其中定义了task_struct
,这样它就不能访问它的成员。但是,它可以将 task_struct*
转换为指向其第一个成员 (C and C++ aliasing rules explicitly allow that) 的指针,即 struct thread_info*
.
找了很多书,网站,调试了Linux内核,明白了thread_info
的设计,也明白了为什么一定要放在task_struct
的第一个元素。
首先,我们需要了解Linux kernel stack。
然后,我们来到核心:我们需要在task_struct
之前到达内核堆栈,并且我们需要在一段时间之前通过内核堆栈到达task_struct
。所以我们必须有一些方法来做到这一点。
1。 task_struct
到内核堆栈
使用起来很简单stack
如下
static inline void *task_stack_page(const struct task_struct *task)
{
return task->stack;
}
此外,我们可以通过 task_pt_regs(task)
到达 pt_regs
2.kernel 堆叠到 task_struct
这里是thread_info
的用法。
让我在这里放两个版本
struct thread_info {
struct task_struct *task; /* main task structure */
__u32 flags; /* low level flags */
__u32 status; /* thread synchronous flags */
__u32 cpu; /* current CPU */
mm_segment_t addr_limit;
unsigned int sig_on_uaccess_error:1;
unsigned int uaccess_err:1; /* uaccess failed */
};
struct thread_info {
unsigned long flags; /* low level flags */
unsigned long status; /* thread synchronous flags */
};
第一个是旧版本,它包括task_struct
,从中我们可以直接进入task_struct
。但是存储成本太高,所以新版本全部删除了。
在旧版本中,我们有这个函数来获取它。
static inline struct thread_info *current_thread_info(void)
{
return (struct thread_info *)(current_top_of_stack() - THREAD_SIZE);
}
可是新版本怎么才能达到task_struct
呢?
因为我们使用了新的current_thread_info
#include <asm/current.h>
#define current_thread_info() ((struct thread_info *)current)
#endif
电流定义如下
struct task_struct;
DECLARE_PER_CPU(struct task_struct *, current_task);
static __always_inline struct task_struct *get_current(void)
{
return this_cpu_read_stable(current_task);
}
#define current get_current
也就是说,我们有 task_struct
来自 cpu.
这是我们展示的源代码的最后一部分
DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task;
......
__visible __notrace_funcgraph struct task_struct *
__switch_to(struct task_struct *prev_p, struct task_struct *next_p)
{
......
this_cpu_write(current_task, next_p);
......
return prev_p;
}
...
#define this_cpu_read_stable(var) percpu_stable_op("mov", var)
通过这种方式,我们可以很容易地得到task_struct
。
在阅读Linux内核源代码时,我发现一件事让我很困惑。
在task_struct
中是这样写的
struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
/*
* For reasons of header soup (see current_thread_info()), this
* must be the first element of task_struct.
*/
struct thread_info thread_info;
#endif
...
};
完整的代码是here,你能告诉我为什么这个必须是task_struct
中的第一个元素吗?
评论说:
/*
* For reasons of header soup (see current_thread_info()), this
* must be the first element of task_struct.
*/
struct thread_info thread_info;
然后<linux/thread_info.h>
:
/*
* For CONFIG_THREAD_INFO_IN_TASK kernels we need <asm/current.h> for the
* definition of current, but for !CONFIG_THREAD_INFO_IN_TASK kernels,
* including <asm/current.h> can cause a circular dependency on some platforms.
*/
#include <asm/current.h>
#define current_thread_info() ((struct thread_info *)current)
#endif
换句话说,<linux/thread_info.h>
不能包含<linux/sched.h>
,其中定义了task_struct
,这样它就不能访问它的成员。但是,它可以将 task_struct*
转换为指向其第一个成员 (C and C++ aliasing rules explicitly allow that) 的指针,即 struct thread_info*
.
找了很多书,网站,调试了Linux内核,明白了thread_info
的设计,也明白了为什么一定要放在task_struct
的第一个元素。
首先,我们需要了解Linux kernel stack。
然后,我们来到核心:我们需要在task_struct
之前到达内核堆栈,并且我们需要在一段时间之前通过内核堆栈到达task_struct
。所以我们必须有一些方法来做到这一点。
1。 task_struct
到内核堆栈
使用起来很简单stack
如下
static inline void *task_stack_page(const struct task_struct *task)
{
return task->stack;
}
此外,我们可以通过 task_pt_regs(task)
pt_regs
2.kernel 堆叠到 task_struct
这里是thread_info
的用法。
让我在这里放两个版本
struct thread_info {
struct task_struct *task; /* main task structure */
__u32 flags; /* low level flags */
__u32 status; /* thread synchronous flags */
__u32 cpu; /* current CPU */
mm_segment_t addr_limit;
unsigned int sig_on_uaccess_error:1;
unsigned int uaccess_err:1; /* uaccess failed */
};
struct thread_info {
unsigned long flags; /* low level flags */
unsigned long status; /* thread synchronous flags */
};
第一个是旧版本,它包括task_struct
,从中我们可以直接进入task_struct
。但是存储成本太高,所以新版本全部删除了。
在旧版本中,我们有这个函数来获取它。
static inline struct thread_info *current_thread_info(void)
{
return (struct thread_info *)(current_top_of_stack() - THREAD_SIZE);
}
可是新版本怎么才能达到task_struct
呢?
因为我们使用了新的current_thread_info
#include <asm/current.h>
#define current_thread_info() ((struct thread_info *)current)
#endif
电流定义如下
struct task_struct;
DECLARE_PER_CPU(struct task_struct *, current_task);
static __always_inline struct task_struct *get_current(void)
{
return this_cpu_read_stable(current_task);
}
#define current get_current
也就是说,我们有 task_struct
来自 cpu.
这是我们展示的源代码的最后一部分
DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task;
......
__visible __notrace_funcgraph struct task_struct *
__switch_to(struct task_struct *prev_p, struct task_struct *next_p)
{
......
this_cpu_write(current_task, next_p);
......
return prev_p;
}
...
#define this_cpu_read_stable(var) percpu_stable_op("mov", var)
通过这种方式,我们可以很容易地得到task_struct
。