歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Unix知識 >> Unix基礎知識 >> kernel學習之sys_fork,sys_vfork,sys_clone和kernel_thread

kernel學習之sys_fork,sys_vfork,sys_clone和kernel_thread

日期:2017/3/3 14:55:08   编辑:Unix基礎知識

用戶空間進程創建接口:fork,vfork,clone函數,這裡只做簡單說明。

fork:使用該系統調用時,子進程復制父進程的全部資源。由於要復制父進程進程描述符給子進程(進程描述的結構很大!!),這一過程開銷是很大的。linux采用了”寫時復制技術”(copy on write,COW),使子進程先共享父進程的物理頁,只有子進程進行寫操作時,再復制對應的物理頁,避免了無用的復制開銷,提高了系統的性能。

實現代碼(x86):arch/x86/kernel/process.c

int sys_fork(struct pt_regs *regs)
{
     return do_fork(SIGCHLD, regs->sp, regs,0, NULL, NULL);
}

實現代碼(arm):arch/arm/kernel/sys_arm.c

/* Fork a newtask - this creates a new program thread.
 * This is called indirectly via a smallwrapper
 */
asmlinkage int sys_fork(struct pt_regs *regs)
{
#ifdefCONFIG_MMU
     return do_fork(SIGCHLD, regs->ARM_sp,regs, 0, NULL, NULL);
#else
     /* can not support in nommu mode */
     return(-EINVAL);
#endif
}

vfork:該系統調用創建的子進程,完全運行在父進程地址空間之上。子進程對地址空間任何數據的修改同樣為父進程所見。vfork執行後父進程堵塞,知道子進程運行結束。

實現代碼(x86):arch/x86/kernel/process.c

intsys_vfork(struct pt_regs *regs)
{
     return do_fork(CLONE_VFORK | CLONE_VM |SIGCHLD, regs->sp, regs, 0,NULL, NULL);
}

實現代碼(arm):arch/arm/kernel/sys_arm.c

asmlinkage intsys_vfork(struct pt_regs *regs)
{
     return do_fork(CLONE_VFORK | CLONE_VM |SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
}

clone:該調用是linux系統所特有的,其NPTL的實現依賴此函數。與fork,vfork相比clone對進程創建有更好的控制能力,能控制子進程和父進程共享何種資源。

實現代碼(x86):arch/x86/kernel/process.c

long sys_clone(unsignedlong clone_flags, unsigned long newsp,
      void __user *parent_tid, void __user *child_tid, struct pt_regs *regs)
{
     if (!newsp)
         newsp = regs->sp;
     return do_fork(clone_flags, newsp, regs, 0,parent_tid, child_tid);
}

實現代碼(arm):arch/arm/kernel/sys_arm.c

/* Clone a task- this clones the calling program thread.
 * This is called indirectly via a smallwrapper
 */
asmlinkage intsys_clone(unsigned long clone_flags, unsigned long newsp,
               int __user *parent_tidptr, int tls_val,int__user *child_tidptr, struct pt_regs *regs)
{
     if (!newsp)
         newsp = regs->ARM_sp;
     return do_fork(clone_flags, newsp, regs, 0,parent_tidptr, child_tidptr);
}

查看本欄目更多精彩內容:http://www.bianceng.cn/OS/unix/

上面進程的創建最終依賴於:do_fork,只是向其傳遞了不同的參數。

longdo_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)

參數clone_flags非常重要,fork把其設置為SIGCHLD,vfork把其設置為CLONE_VFORK|CLONE_VM|SIGCHLD,clone由用戶調用時傳遞。總的來說,do_fork由clone_flags決定。其值可以自由組合決定。include/linux/sched.h中宏定義:

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

上面的宏定義都占用了獨立的bit,所以能或|組合使用。其低八位沒有使用,是為了能和信號量組合使用。

內核線程創建接口:

內核線程是一種特殊的進程,它只能運行在內核態,不能訪問用戶空間的內容。內核線程除了各自的棧和硬件上下文外,共享所用資源。內核利用內核線程來完成一些後台工作如kswapd,ksoftirqd。內核線程有kernel_thread創建。

在linux2.6.xxx/arch/x86/include/asm/processor.h
/*
 * create a kernel thread without removing itfrom tasklists
 */
extern intkernel_thread(int (*fn)(void *), void *arg, unsigned long flags);

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

/*
 * Create a new kernel thread
 */
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

/*
 * Create a kernel thread
 */
intkernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
     struct pt_regs regs;//保存進程的硬件上下文
 
     memset(®s, 0, sizeof(regs));
     regs.si = (unsigned long) fn;
     regs.di = (unsigned long) arg;
 
#ifdefCONFIG_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, ®s, 0, NULL, NULL);
}

分析:

從這段代碼可知,內核線程的創建最終還是調用了do_fork。

arm架構的kernel_thread實現:

/*
 * Create a kernel thread.
 */
pid_tkernel_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 = (unsignedlong)kernel_thread_exit;
     regs.ARM_r7 = SVC_MODE | PSR_ENDSTATE |PSR_ISETSTATE;
     regs.ARM_pc = (unsignedlong)kernel_thread_helper;
     regs.ARM_cpsr = regs.ARM_r7 | PSR_I_BIT;
 
     return do_fork(flags|CLONE_VM|CLONE_UNTRACED,0, ®s, 0, NULL, NULL);
}
 
/*
 * Shuffle the argument into the correctregister before calling the
 * thread function.  r4 is the thread argument, r5 is the pointerto
 * the thread function, and r6 points to theexit function.
 */
extern voidkernel_thread_helper(void);
asm( ".pushsection .text\n"
"    .align\n"
"    .type    kernel_thread_helper,#function\n"
"kernel_thread_helper:\n"
#ifdefCONFIG_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"
"    .size    kernel_thread_helper,. - kernel_thread_helper\n"
"    .popsection");

出處:http://blog.csdn.net/muge0913/article/details/7479379

Copyright © Linux教程網 All Rights Reserved