dup_task_struct&copy_thread &kernel_thread函数分析(v4.4-rc6)

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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值