Reputation: 1494
When reading Linux kernel source code, I find one thing makes me confused.
In task_struct
, it says like this
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
...
};
The complete code is here, can you tell me why this must be the first element in task_struct
?
Upvotes: 4
Views: 2383
Reputation: 1494
After searching many books, websites and debugging the Linux kernel, I understand the design of thread_info
and I get to know why it must be the first element in task_struct
.
Firstly, we need to understand Linux kernel stack.
Then, we come to the core : we need to get to kernel stack by task_struct
some time, and we need to get to task_struct
by kernel stack some time. So we must have some way to do it.
task_struct
to kernel stackIt is easy to use stack
like below
static inline void *task_stack_page(const struct task_struct *task)
{
return task->stack;
}
What's more, we can get to pt_regs
by task_pt_regs(task)
task_struct
Here is the use of thread_info
.
Let me put two version here
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 */
};
The first one is the old version, it includes task_struct
, from which we can go straightly to task_struct
. But it costs too much to strore it, so the new version delete all.
In the old version, we have this function to get it.
static inline struct thread_info *current_thread_info(void)
{
return (struct thread_info *)(current_top_of_stack() - THREAD_SIZE);
}
But how can we get to task_struct
by the new version?
It is because we use a new current_thread_info
#include <asm/current.h>
#define current_thread_info() ((struct thread_info *)current)
#endif
The current is defined below
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
That is to say, we have task_struct
from per cpu.
Here is the last part of the source code we show
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)
By this way, we can get task_struct
easily.
Upvotes: 4
Reputation: 136208
The comment says:
/*
* For reasons of header soup (see current_thread_info()), this
* must be the first element of task_struct.
*/
struct thread_info thread_info;
And then <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
In other words, <linux/thread_info.h>
cannot include <linux/sched.h>
, where task_struct
is defined, so that it cannot access its members. However, it can cast a task_struct*
to the pointer to its first member (C and C++ aliasing rules explicitly allow that), which is struct thread_info*
.
Upvotes: 5