日期:2014-05-16  浏览次数:20640 次

linux 2.6线程创建源码分析
上章讲到线程,现在对线程创建的代码流程分析下。来一步一步揭开她神秘的面纱

linux内核创建线程函数 kernel_thread(),最终会调用do_fork().
前面谈到线程也是用task_struct结构表示它拥有的信息,只是是共享进程的资源。
根据clone_flags标志,来调用clone()创建"线程",表示共享内存、共享文件系统访问计数、共享文件描述符表,以及共享信号处理方式。

kernel_thread定义在/arch/kernel/process.c
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
       struct pt_regs regs;   

       memset(&regs, 0, sizeof(regs));         //把该结构的变量全部清0

       regs.ebx = (unsigned long) fn;          /* ebx指向函数地址 */
       regs.edx = (unsigned long) arg;         /* edx指向参数地址 */

       regs.xds = __USER_DS;
       regs.xes = __USER_DS;
       regs.xfs = __KERNEL_PERCPU;
       regs.orig_eax = -1;
       regs.eip = (unsigned long) kernel_thread_helper;       /*eip指向回调函数*/
       regs.xcs = __KERNEL_CS | get_kernel_rpl();
       regs.eflags = X86_EFLAGS_IF | X86_EFLAGS_SF | X86_EFLAGS_PF | 0x2;


       /* 利用do_fork来产生一个新的线程,共享父进程地址空间,并且不允许调试子进程 */
       return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
}


其中__USER_DS,__KERNEL_PERCPU,__KERNEL_CS都是一些宏定义。在/linux/include/asm-i386/segment.h


extern void kernel_thread_helper(void);  /* 定义成全局变量 */
__asm__(".section .text\n"
    ".align 4\n"
    "kernel_thread_helper:\n\t"
    "movl %edx,%eax\n\t"
    "pushl %edx\n\t"   /* edx指向参数,压入堆栈 */
    "call *%ebx\n\t"   /* ebx指向函数地址,执行函数 */
    "pushl %eax\n\t"
    "call do_exit\n"   /* 结束线程 */
    ".previous");
   
在kernel_thread中调用了do_fork,让我们揭开do_fork()的面纱.
long do_fork(unsigned long clone_flags,
          unsigned long stack_start,
          struct pt_regs *regs,
          unsigned long stack_size,
          int __user *parent_tidptr,
          int __user *child_tidptr)
{
    ...
    ...
    p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);
    ...
    ...
}

   
接着分析do_fork(),copy_proces()是它的核心函数。重点分析一下:
static struct task_struct *copy_process(unsigned long clone_flags,
                       unsigned long stack_start,
                       struct pt_regs *regs,
                       unsigned long stack_size,
                       int __user *parent_tidptr,