窥探 kernel,just for fun --- sys_fork,sys_vfork,sys_clone,kernel_thread

来源:互联网 发布:前端框架ajax取数据 编辑:程序博客网 时间:2024/04/28 05:53

分类: 嵌入式linux kernel篇 245人阅读 评论(2) 收藏 举报

本系列文章由张同浩编写,转载请注明出处:http://blog.csdn.net/muge0913/article/details/7479379

邮箱:muge0913@sina.com




用户空间进程创建接口:fork,vfork,clone函数,这里只做简单说明。

fork:使用该系统调用时,子进程复制父进程的全部资源。由于要复制父进程进程描述符给子进程(进程描述的结构很大!!),这一过程开销是很大的。linux采用了”写时复制技术”(copy on write,COW),使子进程先共享父进程的物理页,只有子进程进行写操作时,再复制对应的物理页,避免了无用的复制开销,提高了系统的性能。

实现代码(x86):arch/x86/kernel/process.c

[cpp] view plaincopyprint?
  1. int sys_fork(struct pt_regs *regs)  
  2. {  
  3.      return do_fork(SIGCHLD, regs->sp, regs,0, NULL, NULL);  
  4. }  

实现代码(arm):arch/arm/kernel/sys_arm.c

[cpp] view plaincopyprint?
  1. /* Fork a newtask - this creates a new program thread. 
  2.  * This is called indirectly via a smallwrapper 
  3.  */  
  4. asmlinkage int sys_fork(struct pt_regs *regs)  
  5. {  
  6. #ifdefCONFIG_MMU  
  7.      return do_fork(SIGCHLD, regs->ARM_sp,regs, 0, NULL, NULL);  
  8. #else  
  9.      /* can not support in nommu mode */  
  10.      return(-EINVAL);  
  11. #endif  
  12. }  

 

vfork:该系统调用创建的子进程,完全运行在父进程地址空间之上。子进程对地址空间任何数据的修改同样为父进程所见。vfork执行后父进程堵塞,知道子进程运行结束。

实现代码(x86):arch/x86/kernel/process.c

[cpp] view plaincopyprint?
  1. intsys_vfork(struct pt_regs *regs)  
  2. {  
  3.      return do_fork(CLONE_VFORK | CLONE_VM |SIGCHLD, regs->sp, regs, 0,NULL, NULL);  
  4. }  

实现代码(arm):arch/arm/kernel/sys_arm.c

[cpp] view plaincopyprint?
  1. asmlinkage intsys_vfork(struct pt_regs *regs)  
  2. {  
  3.      return do_fork(CLONE_VFORK | CLONE_VM |SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);  
  4. }  

clone:该调用是linux系统所特有的,其NPTL的实现依赖此函数。与fork,vfork相比clone对进程创建有更好的控制能力,能控制子进程和父进程共享何种资源。

实现代码(x86):arch/x86/kernel/process.c

[cpp] view plaincopyprint?
  1. long sys_clone(unsignedlong clone_flags, unsigned long newsp,  
  2.       void __user *parent_tid, void __user *child_tid, struct pt_regs *regs)  
  3. {  
  4.      if (!newsp)  
  5.          newsp = regs->sp;  
  6.      return do_fork(clone_flags, newsp, regs, 0,parent_tid, child_tid);  
  7. }  

实现代码(arm):arch/arm/kernel/sys_arm.c

[cpp] view plaincopyprint?
  1. /* Clone a task- this clones the calling program thread. 
  2.  * This is called indirectly via a smallwrapper 
  3.  */  
  4. asmlinkage intsys_clone(unsigned long clone_flags, unsigned long newsp,  
  5.                int __user *parent_tidptr, int tls_val,int__user *child_tidptr, struct pt_regs *regs)  
  6. {  
  7.      if (!newsp)  
  8.          newsp = regs->ARM_sp;  
  9.      return do_fork(clone_flags, newsp, regs, 0,parent_tidptr, child_tidptr);  
  10. }  

上面进程的创建最终依赖于:do_fork,只是向其传递了不同的参数。

[cpp] view plaincopyprint?
  1. longdo_fork(unsigned long clone_flags,  
  2.           unsigned long stack_start,  
  3.           struct pt_regs *regs,  
  4.           unsigned long stack_size,  
  5.           int __user *parent_tidptr,  
  6.           int __user *child_tidptr)  

参数clone_flags非常重要,fork把其设置为SIGCHLD,vfork把其设置为CLONE_VFORK|CLONE_VM|SIGCHLD,clone由用户调用时传递。总的来说,do_fork由clone_flags决定。其值可以自由组合决定。include/linux/sched.h中宏定义:

[cpp] view plaincopyprint?
  1. /* 
  2.  *cloning flags: 
  3.  */  
  4. #define CSIGNAL             0x000000ff    /*signal mask to be sent at exit */  
  5. #define CLONE_VM            0x00000100    /* set if VM shared between processes */  
  6. #define CLONE_FS            0x00000200    /* set if fs info shared between processes*/  
  7. #define CLONE_FILES         0x00000400    /* set if open files shared betweenprocesses */  
  8. #define CLONE_SIGHAND       0x00000800    /* set if signal handlers and blockedsignals shared */  
  9. #define CLONE_PTRACE        0x00002000    /* set if we want to let tracing continue onthe child too */  
  10. #define CLONE_VFORK         0x00004000    /* set if the parent wants the child to wakeit up on mm_release */  
  11. #define CLONE_PARENT        0x00008000    /* set if we want to have the same parent asthe cloner */  
  12. #define CLONE_THREAD        0x00010000    /* Same thread group? */  
  13. #define CLONE_NEWNS         0x00020000    /* New namespace group? */  
  14. #define CLONE_SYSVSEM       0x00040000    /* share system V SEM_UNDO semantics */  
  15. #define CLONE_SETTLS        0x00080000    /* create a new TLS for the child */  
  16. #define CLONE_PARENT_SETTID 0x00100000    /*set the TID in the parent */  
  17. #define CLONE_CHILD_CLEARTID     0x00200000    /*clear the TID in the child */  
  18. #define CLONE_DETACHED      0x00400000    /* Unused,ignored */  
  19. #define CLONE_UNTRACED      0x00800000    /* set ifthe tracing process can't force CLONE_PTRACE on this clone */  
  20. #define CLONE_CHILD_SETTID  0x01000000    /*set the TID in the child */  
  21. #define CLONE_STOPPED       0x02000000    /* Start instopped state */  
  22. #define CLONE_NEWUTS        0x04000000    /* Newutsname group? */  
  23. #define CLONE_NEWIPC        0x08000000    /* Newipcs */  
  24. #defineCLONE_NEWUSER       0x10000000    /* New user namespace */  
  25. #define CLONE_NEWPID        0x20000000    /* New pidnamespace */  
  26. #define CLONE_NEWNET        0x40000000    /* Newnetwork namespace */  
  27. #define CLONE_IO            0x80000000    /* Clone io context */  

上面的宏定义都占用了独立的bit,所以能或|组合使用。其低八位没有使用,是为了能和信号量组合使用。



内核线程创建接口:

内核线程是一种特殊的进程,它只能运行在内核态,不能访问用户空间的内容。内核线程除了各自的栈和硬件上下文外,共享所用资源。内核利用内核线程来完成一些后台工作如kswapd,ksoftirqd。内核线程有kernel_thread创建。

[cpp] view plaincopyprint?
  1. 在linux2.6.xxx/arch/x86/include/asm/processor.h  
  2. /* 
  3.  * create a kernel thread without removing itfrom tasklists 
  4.  */  
  5. extern intkernel_thread(int (*fn)(void *), void *arg, unsigned long flags);  

linux2.6.xxx/arch/arm/include/asm/processor.h

[cpp] view plaincopyprint?
  1. /* 
  2.  * Create a new kernel thread 
  3.  */  
  4. extern intkernel_thread(int (*fn)(void *), void *arg, unsigned long flags);  

参数说明:

fn:新创建的内核线程要执行的函数。

arg:fn的参数。

flags:和do_fork中的clone_flags作用相似。

 

 

kernel_thread函数分析:

在linux2.6.xxx/arch/x86/kernel/process.c

[cpp] view plaincopyprint?
  1. /* 
  2.  * Create a kernel thread 
  3.  */  
  4. intkernel_thread(int (*fn)(void *), void *arg, unsigned long flags)  
  5. {  
  6.      struct pt_regs regs;//保存进程的硬件上下文  
  7.    
  8.      memset(®s, 0, sizeof(regs));  
  9.      regs.si = (unsigned long) fn;  
  10.      regs.di = (unsigned long) arg;  
  11.    
  12. #ifdefCONFIG_X86_32  
  13.      regs.ds = __USER_DS;  
  14.      regs.es = __USER_DS;  
  15.      regs.fs = __KERNEL_PERCPU;  
  16.      regs.gs = __KERNEL_STACK_CANARY;  
  17. #else  
  18.      regs.ss = __KERNEL_DS;  
  19. #endif  
  20.    
  21.      regs.orig_ax = -1;  
  22.      regs.ip = (unsigned long)kernel_thread_helper;  
  23.      regs.cs = __KERNEL_CS | get_kernel_rpl();  
  24.      regs.flags = X86_EFLAGS_IF | 0x2;  
  25.    
  26.      /* Ok, create the new process.. */  
  27.      return do_fork(flags | CLONE_VM |CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);  
  28. }  

分析:

从这段代码可知,内核线程的创建最终还是调用了do_fork。

 

arm架构的kernel_thread实现:

[cpp] view plaincopyprint?
  1. /* 
  2.  * Create a kernel thread. 
  3.  */  
  4. pid_tkernel_thread(int (*fn)(void *), void *arg, unsigned long flags)  
  5. {  
  6.      struct pt_regs regs;  
  7.    
  8.      memset(®s, 0, sizeof(regs));  
  9.    
  10.      regs.ARM_r4 = (unsigned long)arg;  
  11.      regs.ARM_r5 = (unsigned long)fn;  
  12.      regs.ARM_r6 = (unsignedlong)kernel_thread_exit;  
  13.      regs.ARM_r7 = SVC_MODE | PSR_ENDSTATE |PSR_ISETSTATE;  
  14.      regs.ARM_pc = (unsignedlong)kernel_thread_helper;  
  15.      regs.ARM_cpsr = regs.ARM_r7 | PSR_I_BIT;  
  16.    
  17.      return do_fork(flags|CLONE_VM|CLONE_UNTRACED,0, ®s, 0, NULL, NULL);  
  18. }  
  19.    
  20. /* 
  21.  * Shuffle the argument into the correctregister before calling the 
  22.  * thread function.  r4 is the thread argument, r5 is the pointerto 
  23.  * the thread function, and r6 points to theexit function. 
  24.  */  
  25. extern voidkernel_thread_helper(void);  
  26. asm( ".pushsection .text\n"  
  27. "    .align\n"  
  28. "    .type    kernel_thread_helper,#function\n"  
  29. "kernel_thread_helper:\n"  
  30. #ifdefCONFIG_TRACE_IRQFLAGS  
  31. "    bl   trace_hardirqs_on\n"  
  32. #endif  
  33. "    msr  cpsr_c,r7\n"  
  34. "    mov  r0,r4\n"  
  35. "    mov  lr,r6\n"  
  36. "    mov  pc,r5\n"  
  37. "    .size    kernel_thread_helper,. - kernel_thread_helper\n"  
  38. "    .popsection");