一联合体thread_union:表示一个进程的线程描述符和内核栈大小定义
unionthread_union {
struct thread_info thread_info;
unsigned long stack[THREAD_SIZE/sizeof(long)];根据联合体的定义和作用,一个thread_info的大小也就是栈的大小
};
线程描述符:----也可以理解成线程的”逻辑栈信息“,注意不是与cpu栈信息,这里仅仅是软件层面上线程的“栈信息”
struct thread_info {
unsigned long flags; /* low level flags */
int preempt_count; /* 0 => preemptable, <0 => bug */
mm_segment_t addr_limit; /* address limit */
struct task_struct *task; /* main task structure */
struct exec_domain *exec_domain; /* execution domain */
__u32 cpu; /* cpu */
__u32 cpu_domain; /* cpu domain */
struct cpu_context_save cpu_context; /* cpu context */-----cpu的寄存器层面栈的信息
__u32 syscall; /* syscall number */
__u8 used_cp[16]; /* thread used copro */
unsigned long tp_value;
struct crunch_state crunchstate;
union fp_state fpstate __attribute__((aligned(8)));
union vfp_state vfpstate;
#ifdef CONFIG_ARM_THUMBEE
unsigned long thumbee_state; /* ThumbEE Handler Base register */
#endif
struct restart_block restart_block;
int cpu_excp;
void *regs_on_excp;
};
小结:thread_union与thread_info是一样的作用,只是thread_union做了一个stack大小的定义。
二线程栈信息的建立流程
概述:首先系统启动的时候,init线程首先分配了一个thread_union;以后其他的线程创建的时候,都是copy该init线程的thread_union结构,复制出一个线程副本出来。
下面分析:
1)init_task相关栈和task信息的建立
这里关注栈信息(线程描述信息)
1.1)初始化设置栈信息:参考”内核栈的初始化设置过程“
什么意思呢?就是建立第一个init线程的对应cpu体系的栈信息结构的起始位置SP指针,与后面系统建立其他线程的栈无关,其他线程自己会分配自己的空间,初始化两个数据结构:一个是thread_info,另一个就是task_struct;
:thread_info的定义,
0号线程是通过联合体thread_union定义对象:”init_thread_union“:
init_thread_union定义和初始化在哪里呢?如下:
unionthread_unioninit_thread_union__init_task_data=
{ INIT_THREAD_INFO(init_task) };
__init_task_data只是一个宏,来自:
Init_task.h (zaw809aa_lava\kernel\include\linux):#define __init_task_data __attribute__((__section__(".data..init_task"))):
#define__init_task_data__attribute__((__section__(".data.init_task")))
那么,上面的内容就展开成了:
union thread_unioninit_thread_union__attribute__((__section__(".data.init_task"))) ={INIT_THREAD_INFO(init_task)};并且把数据放进指定的数据段.data.init_task
那么如何完成init_thread_union赋值呢?
Step1 )先执行宏:
#define INIT_THREAD_INFO(tsk) \
{ \
.task = &tsk, \
.exec_domain = &default_exec_domain, \
.flags = 0, \
.preempt_count = INIT_PREEMPT_COUNT, \
.addr_limit = KERNEL_DS, \
.cpu_domain = domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
domain_val(DOMAIN_IO, DOMAIN_CLIENT), \
.restart_block = { \
.fn = do_no_restart_syscall, \
}, \
}
所以unionthread_unioninit_thread_union__attribute__((__section__(".data.init_task"))) ={INIT_THREAD_INFO(init_task)};展开就是:
unionthread_unioninit_thread_union__attribute__((__section__(".data.init_task"))) ={
.task = &tsk, \
.exec_domain = &default_exec_domain, \
.flags = 0, \
.preempt_count = INIT_PREEMPT_COUNT, \
.addr_limit = KERNEL_DS, \
.cpu_domain = domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
domain_val(DOMAIN_IO, DOMAIN_CLIENT), \
.restart_block = { \
.fn = do_no_restart_syscall, \
}, \
};
可以看到在初始化thread_info的成员(task,exec_domain,flags,preempt_count ,addr_limit等);
Step2)然后执行struct task_structinit_task=INIT_TASK(init_task);
Step3)最后执行完INIT_TASK(tsk)这个宏以后,init_thread_union就被初始化成以上内容了,至此,0号进程的task_struct和thread_info就初始化完毕了:
/*
* Initial task structure.
*
* All other task structs will be allocated on slabs in fork.c
*/
structtask_structinit_task= INIT_TASK(init_task);每一个线程对应一个task_struct任务调度的基本单位的初始化。
#defineINIT_TASK(tsk) \
{ \
.state = 0, \
.stack = &init_thread_info, \init线程的描述符(也就是栈信息)指向栈空间
.usage = ATOMIC_INIT(2), \
.flags = PF_KTHREAD, \
….
}
===============================================================
2)其他线程的创建的时候,copy当前线程(init线程是一切线程的鼻祖)的task_struct信息和栈内存空间:
copy_process()---->p = dup_task_struct(current);
static struct task_struct *dup_task_struct(struct task_struct *orig)
{
struct task_struct *tsk;
struct thread_info *ti;//创建thread_info也就是栈
unsigned long *stackend;
int node = tsk_fork_get_node(orig);
int err;
prepare_to_copy(orig);
tsk = alloc_task_struct_node(node);//在当前线程的node上分配和复制该task信息
if (!tsk){
printk("[%d:%s] fork fail at alloc_tsk_node, please check kmem_cache_alloc_node()\n", current->pid, current->comm);
return NULL;
}
ti = alloc_thread_info_node(tsk, node);在当前线程的node上分配和复制该task的stack信息(栈)
if (!ti) {
printk("[%d:%s] fork fail at alloc_t_info_node, please check alloc_pages_node()\n", current->pid, current->comm);
free_task_struct(tsk);
return NULL;
}
err = arch_dup_task_struct(tsk, orig);
if (err){
printk("[%d:%s] fork fail at arch_dup_task_struct, err:%d \n", current->pid, current->comm, err);
goto out;
}
tsk->stack = ti;
setup_thread_stack(tsk, orig);//把当前task的stack成员,数据类型转化成栈信息结构:thread_info
clear_user_return_notifier(tsk);
clear_tsk_need_resched(tsk);
stackend = end_of_stack(tsk);
*stackend = STACK_END_MAGIC; /* for overflow detection */
…...
}
#definetask_thread_info(task) ((structthread_info*)(task)->stack)//把(task)->stack数据类型格式化成栈信息结构thread_info,
#define task_stack_page(task) ((task)->stack)
static inline void setup_thread_stack(struct task_struct *p, struct task_struct *org)
{
*task_thread_info(p) = *task_thread_info(org);
task_thread_info(p)->task = p;
}