pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) 通过这个函数可以创建内核线程,运行一个指定函数fn。
但是这个fn是怎么运行的了?
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct pt_regs regs;
memset(®s, 0, sizeof(regs));
regs.ARM_r4 = (unsigned long)arg;
regs.ARM_r5 = (unsigned long)fn; //做标记
regs.ARM_r6 = (unsigned long)kernel_thread_exit;
regs.ARM_r7 = SVC_MODE | PSR_ENDSTATE | PSR_ISETSTATE;
regs.ARM_pc = (unsigned long)kernel_thread_helper; //这个很重要会将fn正确运行
regs.ARM_cpsr = regs.ARM_r7 | PSR_I_BIT;
return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
}
接着do_fork->copy_process(clone_flags, stack_start, regs, stack_size, child_tidptr, NULL, trace);->copy_thread(clone_flags, stack_start, stack_size, p, regs);
int
copy_thread(unsigned long clone_flags, unsigned long stack_start,
unsigned long stk_sz, struct task_struct *p, struct pt_regs *regs)
{
struct thread_info *thread = task_thread_info(p);
struct pt_regs *childregs = task_pt_regs(p); //获得线程的内核栈
*childregs = *regs;//将之前kernel_thread的内容复制过来,如fn,kernel_thread_helper,这一步很重要,因为建立了子线程的堆栈环境,以后这个线程被schedule后,会将这里面保存的寄存器恢复,会运行kernel_thread_helper 。regs.ARM_pc = (unsigned long)kernel_thread_helper;
childregs->ARM_r0 = 0;
childregs->ARM_sp = stack_start;
memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save));
thread->cpu_context.sp = (unsigned long)childregs;
thread->cpu_context.pc = (unsigned long)ret_from_fork;
clear_ptrace_hw_breakpoint(p);
if (clone_flags & CLONE_SETTLS)
thread->tp_value = regs->ARM_r3;
thread_notify(THREAD_NOTIFY_COPY, thread);
return 0;
}
而kernel_thread_helper定义为找到,但注释已经说的很清楚了,如下:
/*
* Shuffle the argument into the correct register before calling the
* thread function. r4 is the thread argument, r5 is the pointer to
* the thread function, and r6 points to the exit function.
*/
extern void kernel_thread_helper(void);
asm( ".pushsection .text\n"
" .align\n"
" .type kernel_thread_helper, #function\n"
"kernel_thread_helper:\n"
#ifdef CONFIG_TRACE_IRQFLAGS
" bl trace_hardirqs_on\n"
#endif
" msr cpsr_c, r7\n"
" mov r0, r4\n"
" mov lr, r6\n"
" mov pc, r5\n" //将fn赋值给pc,终于原形毕露了
" .size kernel_thread_helper, . - kernel_thread_helper\n"
" .popsection");
先弄到这,当然还有很多进程切换的细节未提到。