分享

关于kernel_thread的补充说明

 Liucw2012 2012-03-23
kernel_thread会产生一个task_struct实例出来,所以应该算是内核进程了,x86上的实现代码是:

<arch/x86/kernel/process.c>
int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct pt_regs regs;

memset(&regs, 0, sizeof(regs));

regs.si = (unsigned long) fn;
regs.di = (unsigned long) arg;

#ifdef CONFIG_X86_32
regs.ds = __USER_DS;
regs.es = __USER_DS;
regs.fs = __KERNEL_PERCPU;
regs.gs = __KERNEL_STACK_CANARY;
#else
regs.ss = __KERNEL_DS;
#endif

regs.orig_ax = -1;
regs.ip = (unsigned long) kernel_thread_helper;
regs.cs = __KERNEL_CS | get_kernel_rpl();
regs.flags = X86_EFLAGS_IF | 0x2;

/* Ok, create the new process.. */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
}

期间的关键节点有:
1. do_fork在调用的copy_process函数里生成一个新的task_struct实例p,代表一个新进程
2. 通过alloc_pages为新进程生成一个独立的内核栈(栈大小8KB):alloc_thread_info_node,之后将父进程内核栈的内容copy到新进程内核栈中。
3. p加入运行队列等待调度
4. copy_thread:
struct pt_regs *childregs;
*childregs = *regs;
p->thread.sp = (unsigned long) childregs;
p->thread.sp0 = (unsigned long) (childregs+1);
p->thread.ip = (unsigned long) ret_from_fork;
因此,新进程拥有自己的堆栈且会根据regs中的内容进行修改。

5. p被调度运行,因为p->thread.ip = (unsigned long) ret_from_fork,所以从ret_from_fork开始执行(在新进程上下文中)
6. ret_from_fork最后调用syscall_exit,后者最后以INTERRUPT_RETURN(也就是ia32中iret指令或者是x86_64中的iretq指令)返回,因此将从当前堆栈中找到返回地址,该地址在上面的第4步中被赋值为regs.ip = (unsigned long) kernel_thread_helper,所以kernel_thread_helper将在新进程环境中被调用。

简单的总结就是:kernel_thread产生一个新进程p,p->thread.ip=ret_from_fork,所以当新进程被调度执行时将从ret_from_fork函数开始执行(原因在进程切换用的switch_to代码中),ret_from_fork执行的最后执行iret,因为堆栈中的ip被指向了regs.ip = (unsigned long) kernel_thread_helper,所以将执行kernel_thread_helper函数。

接下来的过程可参考解密Linux kernel中的内核线程 ,本帖实际上是该贴的一个补充。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多