浅析armlinux-sp的孵化流程,1号内核线程init的创建
文章来源:http://gliethttp.cublog.cn
接续《浅析armlinux-sp的孵化流程,1号内核线程init的创建-之1》 ----------------------------------------------------------------------- rest_init()->kernel_thread()->arch_kernel_thread()->sys_clone()->do_fork()->copy_thread() int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, unsigned long unused, struct task_struct * p, struct pt_regs * regs) { struct pt_regs *childregs; struct context_save_struct * save; atomic_set(&p->thread.refcount, 1); childregs = ((struct pt_regs *)((unsigned long)p + 8192 - 8)) - 1;
//(char*)&p[8192]-sizeof(pt_regs),即:(char*)&p[8192]-18*4[gliethttp] *childregs = *regs; childregs->ARM_r0 = 0; childregs->ARM_sp = esp; save = ((struct context_save_struct *)(childregs)) - 1; *save = INIT_CSS; //新线程的默认cpsr,r4,r5,r6,r7,r8,r9,sl,fp,pc设置 save->pc |= (unsigned long)ret_from_fork; //返回函数 p->thread.save = save; //供__switch_to进程切换使用 return 0; } ----------------------------------------------------------------------- 系统调用swi返回 arch/arm/kernel/entry-common.S ... .align 5 /* * This is the fast syscall return path. We do as little as * possible here, and this includes saving r0 back into the SVC * stack. */ ret_fast_syscall: disable_irq r1 @ ensure IRQs are disabled ldr r1, [tsk, #TSK_NEED_RESCHED] ldr r2, [tsk, #TSK_SIGPENDING] teq r1, #0 @ need_resched || sigpending teqeq r2, #0 bne slow fast_restore_user_regs ... ----------------------- arch/arm/kernel/entry-header.S /* * Must be called with IRQs already disabled. */ .macro fast_restore_user_regs ldr r1, [sp, #S_OFF + S_PSR] @ get calling cpsr ldr lr, [sp, #S_OFF + S_PC]! @ get pc msr spsr, r1 @ save in spsr_svc ldmdb sp, {r1 - lr}^ //恢复各寄存器 mov r0, r0 add sp, sp, #S_FRAME_SIZE - S_PC movs pc, lr //返回系统调用处,继续执行 .endm 能力所限,就到这里了,后边的继续研究.另外用户栈的创建,粗略的过一下,免得忘了: ----------------------------------------------------------------------- ######## 摘: 将参数栈加入到进程的虚拟地址空间。 进程的用户栈的栈底在0xC0000000(3G)处,用户栈从此处开始向下增长。进程的参数栈在它的参数数据结构linux_binprm的page表中,而且参数所占用的物理页已经分配。因为目前要放入用户栈中的只有在参数栈中的进程运行所需要的参数,而且用户栈的大小也无法预先确定,所以,此处先根据参数栈的大小建立用户栈。如果进程在以后的运行中需要更大的栈空间(PUSH操作时,栈指针esp越界),处理器会产生page fault异常,系统中的page fault异常处理程序do_page_fault会向下扩展用户栈空间。 根据参数栈的大小建立一个内存区域数据结构vm_area_struct,将已分配的参数页逐个加入到进程的页目录和页表中。 ◆申请一块内存用来建立vm_area_struct数据结构,该内存区域的地址范围是[3G - 参数栈大小,3G],其标志为VM_STACK_FLAGS(向下增长,可读、写、执行、共享等),该区域没有规定操作,没有对应的文件; ◆因为当前页目录中用户页表部分全为空,所以根据用户参数栈各页的虚拟地址首先分配相应的页表,而后将各参数栈页对应的页表项设为脏页,填入相应的页表中; ◆设置当前进程用户栈的开始位置为当前的栈顶: mm->start_stack。 ######## load_elf_binary()->setup_arg_pages(bprm) int setup_arg_pages(struct linux_binprm *bprm) { unsigned long stack_base; struct vm_area_struct *mpnt; int i; /*STACK_TOP = 0xC00000000 = 3G #ifdef __KERNEL__ #define STACK_TOP ((current->personality == PER_LINUX_32BIT) ? \ TASK_SIZE : TASK_SIZE_26) #endif */ stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE; bprm->p += stack_base; if (bprm->loader) bprm->loader += stack_base; bprm->exec += stack_base; mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); if (!mpnt) return -ENOMEM; down_write(¤t->mm->mmap_sem); { mpnt->vm_mm = current->mm; mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; mpnt->vm_end = STACK_TOP; //0xC0000000 = 3G mpnt->vm_page_prot = PAGE_COPY; mpnt->vm_flags = VM_STACK_FLAGS; //设置该VMA为VM_STACK_FLAGS标志 mpnt->vm_ops = NULL; mpnt->vm_pgoff = 0; mpnt->vm_file = NULL; mpnt->vm_private_data = (void *) 0; insert_vm_struct(current->mm, mpnt); current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; } for (i = 0 ; i < MAX_ARG_PAGES ; i++) { struct page *page = bprm->page[i]; if (page) { bprm->page[i] = NULL; put_dirty_page(current,page,stack_base); } stack_base += PAGE_SIZE; } up_write(¤t->mm->mmap_sem); return 0; } ----------------------------------------------------------------------- load_elf_binary()->setup_arg_pages(bprm)->start_thread(regs, elf_entry, bprm->p) #define start_thread(regs,pc,sp) \ ({ \ unsigned long *stack = (unsigned long *)sp; \ set_fs(USER_DS); \ memzero(regs->uregs, sizeof(regs->uregs)); \ if (current->personality & ADDR_LIMIT_32BIT) \ regs->ARM_cpsr = USR_MODE; \ else \ regs->ARM_cpsr = USR26_MODE; \ regs->ARM_pc = pc; /* pc */ \ regs->ARM_sp = sp; /* sp */ \//系统调用返回时,将regs中数据设置生效 regs->ARM_r2 = stack[2]; /* r2 (envp) */ \ regs->ARM_r1 = stack[1]; /* r1 (argv) */ \ regs->ARM_r0 = stack[0]; /* r0 (argc) */ \ })
完.
|