static struct task_struct *dup_task_struct(struct task_struct *orig)
{
struct task_struct *tsk;
struct thread_info *ti;
int node = tsk_fork_get_node(orig); //获取orig的task的node
int err;
tsk = alloc_task_struct_node(node);//从orig同样的numa节点分配task结构体
if (!tsk)
return NULL;
ti = alloc_thread_info_node(tsk, node);//分配两页的page作为thread_info 结构
if (!ti)
goto free_tsk;
err = arch_dup_task_struct(tsk, orig); //完全复制orig的task结构体
if (err)
goto free_ti;
tsk->stack = ti; //复制orig的thread_info与task关联起来
#ifdef CONFIG_SECCOMP
/*
* We must handle setting up seccomp filters once we're under
* the sighand lock in case orig has changed between now and
* then. Until then, filter must be NULL to avoid messing up
* the usage counts on the error path calling free_task.
*/
tsk->seccomp.filter = NULL;
#endif
setup_thread_stack(tsk, orig);//复制orig的栈地址且讲thread_info 结构体与task关联起来
clear_user_return_notifier(tsk);
clear_tsk_need_resched(tsk);
set_task_stack_end_magic(tsk);
#ifdef CONFIG_CC_STACKPROTECTOR
tsk->stack_canary = get_random_int();
#endif
/*
* One for us, one for whoever does the "release_task()" (usually
* parent)
*/
atomic_set(&tsk->usage, 2);
#ifdef CONFIG_BLK_DEV_IO_TRACE
tsk->btrace_seq = 0;
#endif
tsk->splice_pipe = NULL;
tsk->task_frag.page = NULL;
account_kernel_stack(ti, 1);
return tsk;
free_ti:
free_thread_info(ti);
free_tsk:
free_task_struct(tsk);
return NULL;
}
整个函数如下图:
int copy_thread(unsigned long clone_flags, unsigned long stack_start,
unsigned long stk_sz, struct task_struct *p)
{
/*读取p的栈地址 #define task_stack_page(task) ((task)->stack)
#define task_pt_regs(p) \
((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1)
THREAD_START_SP 是栈相对于thread_info的偏移地址*/
struct pt_regs *childregs = task_pt_regs(p); //获取用户态的栈顶地址
memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context)); //清空栈
if (likely(!(p->flags & PF_KTHREAD))) { //非内核线程
*childregs = *current_pt_regs(); //获取当前调用copy_thread线程的栈
childregs->regs[0] = 0;//强制将此函数的返回地址设置为0 ,解释了fork类进程创建系统调用的子进程能够返回0
/*
* Read the current TLS pointer from tpidr_el0 as it may be
* out-of-sync with the saved value.
*/
asm("mrs %0, tpidr_el0" : "=r" (*task_user_tls(p)));//保存当前的tpidr_el0寄存器到p的tp_value中
if (stack_start) { //如果指定了栈的起始地址
if (is_compat_thread(task_thread_info(p)))//判断用户进程是不是32位
childregs->compat_sp = stack_start; //初始化p的栈起始地址
/* 16-byte aligned stack mandatory on AArch64 */
else if (stack_start & 15)//arm64栈起始地址必须是16byte对齐的
return -EINVAL;
else
childregs->sp = stack_start;//64位程序调用则直接设置sp为指定的栈起始地址
}
/*
* If a TLS pointer was passed to clone (4th argument), use it
* for the new thread.
*/
if (clone_flags & CLONE_SETTLS)//如果指定了线程本地存储的标志
p->thread.tp_value = childregs->regs[3];//则用clone的第四个参数初始化
} else { //内核线程
memset(childregs, 0, sizeof(struct pt_regs));
childregs->pstate = PSR_MODE_EL1h;
p->thread.cpu_context.x19 = stack_start;//设置内核栈的起始地址,起始是kernel_thread指定的fn
p->thread.cpu_context.x20 = stk_sz;//设置内核栈的大小,实际上是kernel_thread的设置fn的arg参数
}
p->thread.cpu_context.pc = (unsigned long)ret_from_fork; //设置下一条指令位fork返回值
p->thread.cpu_context.sp = (unsigned long)childregs;//将初始化好的栈保存在cpu的上下文的sp中
ptrace_hw_copy_thread(p);
return 0;
}
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
return _do_fork(flags|CLONE_VM|CLONE_UNTRACED, (unsigned long)fn,
(unsigned long)arg, NULL, NULL, 0);//这里的stack_start和stack_size被设置为fn和arg
}
./arch/arm64/kernel/entry.S
ENTRY(ret_from_fork)
bl schedule_tail
cbz x19, 1f // not a kernel thread,x19等于0 则跳转到1标签执行,当是内核线程x19被初始化为fn
mov x0, x20
blr x19 //执行内核线程指定的函数
1: get_thread_info tsk
b ret_to_user
ENDPROC(ret_from_fork)