Linux进程管理之进程的创建


http://blog.csdn.net/npy_lp/article/details/7292566

 开发平台:Ubuntu 11.04

    内核源码:linux-2.6.38.8.tar.bz2

    目标平台:ARM体系结构

 

    在Linux应用程序的开发中,可以通过fork、vfork和clone等系统调用来创建一个子进程,它们在Linux内核中的入口点分别为sys_fork、sys_vfork和sys_clone函数。 

[cpp]  view plain copy
  1. /* linux-2.6.38.8/arch/arm/kernel/sys_arm.c */  
  2. asmlinkage int sys_fork(struct pt_regs *regs)  
  3. {  
  4. #ifdef CONFIG_MMU  
  5.     return do_fork(SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);  
  6. #else  
  7.     /* can not support in nommu mode */  
  8.     return(-EINVAL);  
  9. #endif  
  10. }  
  11.   
  12. asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,  
  13.              int __user *parent_tidptr, int tls_val,  
  14.              int __user *child_tidptr, struct pt_regs *regs)  
  15. {  
  16.     if (!newsp)  
  17.         newsp = regs->ARM_sp;  
  18.   
  19.     return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);  
  20. }  
  21.   
  22. asmlinkage int sys_vfork(struct pt_regs *regs)  
  23. {  
  24.     return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);  
  25. }  

    在这里,asmlinkage对ARM体系结构来说是忽略的。

    只有支持MMU的系统才能使用sys_fork函数。

    这三个函数的实现都只是对do_fork函数的简单封装。

    1、do_fork函数

    (1)、函数参数 

[cpp]  view plain copy
  1. /* linux-2.6.38.8/kernel/fork.c */  
  2. long do_fork(unsigned long clone_flags,  
  3.           unsigned long stack_start,  
  4.           struct pt_regs *regs,  
  5.           unsigned long stack_size,  
  6.           int __user *parent_tidptr,  
  7.           int __user *child_tidptr)  

    clone_flags是clone标志,用于控制创建子进程的行为,可能的取值如下: 

[cpp]  view plain copy
  1. /* linux-2.6.38.8/include/linux/sched.h */  
  2. #define CSIGNAL     0x000000ff  /* signal mask to be sent at exit */  
  3. #define CLONE_VM    0x00000100  /* set if VM shared between processes */  
  4. #define CLONE_FS    0x00000200  /* set if fs info shared between processes */  
  5. #define CLONE_FILES 0x00000400  /* set if open files shared between processes */  
  6. #define CLONE_SIGHAND   0x00000800  /* set if signal handlers and blocked signals shared */  
  7. #define CLONE_PTRACE    0x00002000  /* set if we want to let tracing continue on the child too */  
  8. #define CLONE_VFORK 0x00004000  /* set if the parent wants the child to wake it up on mm_release */  
  9. #define CLONE_PARENT    0x00008000  /* set if we want to have the same parent as the cloner */  
  10. #define CLONE_THREAD    0x00010000  /* Same thread group? */  
  11. #define CLONE_NEWNS 0x00020000  /* New namespace group? */  
  12. #define CLONE_SYSVSEM   0x00040000  /* share system V SEM_UNDO semantics */  
  13. #define CLONE_SETTLS    0x00080000  /* create a new TLS for the child */  
  14. #define CLONE_PARENT_SETTID 0x00100000  /* set the TID in the parent */  
  15. #define CLONE_CHILD_CLEARTID    0x00200000  /* clear the TID in the child */  
  16. #define CLONE_DETACHED      0x00400000  /* Unused, ignored */  
  17. #define CLONE_UNTRACED      0x00800000  /* set if the tracing process can't force CLONE_PTRACE on this clone */  
  18. #define CLONE_CHILD_SETTID  0x01000000  /* set the TID in the child */  
  19. /* 0x02000000 was previously the unused CLONE_STOPPED (Start in stopped state) 
  20.    and is now available for re-use. */  
  21. #define CLONE_NEWUTS        0x04000000  /* New utsname group? */  
  22. #define CLONE_NEWIPC        0x08000000  /* New ipcs */  
  23. #define CLONE_NEWUSER       0x10000000  /* New user namespace */  
  24. #define CLONE_NEWPID        0x20000000  /* New pid namespace */  
  25. #define CLONE_NEWNET        0x40000000  /* New network namespace */  
  26. #define CLONE_IO        0x80000000  /* Clone io context */  

    stack_start等于父进程当前的栈指针。

    regs指向通用寄存器集合,它以原始的形式保存了调用参数。在ARM体系结构上,pt_regs结构体的定义如下: 

[cpp]  view plain copy
  1. /* linux-2.6.38.8/arch/arm/include/asm/ptrace.h */  
  2. struct pt_regs {  
  3.     unsigned long uregs[18];  
  4. };  
  5.   
  6. #define ARM_cpsr    uregs[16]  
  7. #define ARM_pc      uregs[15]  
  8. #define ARM_lr      uregs[14]  
  9. #define ARM_sp      uregs[13]  
  10. #define ARM_ip      uregs[12]  
  11. #define ARM_fp      uregs[11]  
  12. #define ARM_r10     uregs[10]  
  13. #define ARM_r9      uregs[9]  
  14. #define ARM_r8      uregs[8]  
  15. #define ARM_r7      uregs[7]  
  16. #define ARM_r6      uregs[6]  
  17. #define ARM_r5      uregs[5]  
  18. #define ARM_r4      uregs[4]  
  19. #define ARM_r3      uregs[3]  
  20. #define ARM_r2      uregs[2]  
  21. #define ARM_r1      uregs[1]  
  22. #define ARM_r0      uregs[0]  
  23. #define ARM_ORIG_r0 uregs[17]  

    stack_size总被设置为0。

    parent_tidptr和child_tidptr分别表示父子进程的用户态变量地址,只有在CLONE_PARENT_SETTID或CLONE_CHILD_SETTID标志被设置时才有意义。

    (2)、检查冲突的clone标志和必要的权限 

[cpp]  view plain copy
  1. if (clone_flags & CLONE_NEWUSER) {  
  2.     if (clone_flags & CLONE_THREAD)  
  3.         return -EINVAL;  
  4.     /* hopefully this check will go away when userns support is 
  5.      * complete 
  6.      */  
  7.     if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SETUID) ||  
  8.             !capable(CAP_SETGID))  
  9.         return -EPERM;  
  10. }  

    (3)、检查当前进程(也就是父进程)的ptrace字段(current->ptrace),如果它的值不为0,则表示有另外一个进程正在跟踪父进程 

[cpp]  view plain copy
  1. if (likely(user_mode(regs)))  
  2.     trace = tracehook_prepare_clone(clone_flags);  

    创建内核线程时不需要检查。

    (4)、调用copy_process函数 

[cpp]  view plain copy
  1. p = copy_process(clone_flags, stack_start, regs, stack_size,  
  2.          child_tidptr, NULL, trace);  

    (5)、若copy_process函数创建子进程失败,则返回PTR_ERR函数转换后的错误码 

[cpp]  view plain copy
  1. /* linux-2.6.38.8/include/linux/err.h */  
  2. static inline long __must_check PTR_ERR(const void *ptr)  
  3. {  
  4.     return (long) ptr;  
  5. }  

    在一个返回指针的函数(如copy_process)中,应该先使用ERR_PTR函数把所有的错误码都转变为指针,然后在调用函数中通过IS_ERR函数来检查它的返回值,或通过PTR_ERR函数恢复返回值对应的错误码。在实际应用中,IS_ERR或PTR_ERR应该与ERR_PTR配对使用。 

[cpp]  view plain copy
  1. static inline void * __must_check ERR_PTR(long error)  
  2. {  
  3.     return (void *) error;  
  4. }  
  5.   
  6. #define MAX_ERRNO   4095  
  7. #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)  
  8. static inline long __must_check IS_ERR(const void *ptr)  
  9. {  
  10.     return IS_ERR_VALUE((unsigned long)ptr);  
  11. }  

    (6)、若成功创建子进程,内核还须执行一些收尾操作

    1)、追踪点函数,通过宏TRACE_EVENT定义在linux-2.6.38.8/include/trace/events/sched.h文件中,在ARM体系结构上不用考虑 

[cpp]  view plain copy
  1. trace_sched_process_fork(current, p);  

    2)、调用task_pid_vnr函数获取子进程的局部PID 

[cpp]  view plain copy
  1. nr = task_pid_vnr(p);  

[cpp]  view plain copy
  1. /* linux-2.6.38.8/include/linux/sched.h */  
  2. static inline pid_t task_pid_vnr(struct task_struct *tsk)  
  3. {  
  4.     return __task_pid_nr_ns(tsk, PIDTYPE_PID, NULL);  
  5. }  
  6. /* linux-2.6.38.8/kernel/pid.c */  
  7. pid_t __task_pid_nr_ns(struct task_struct *task, enum pid_type type,  
  8.             struct pid_namespace *ns)  
  9. {  
  10.     pid_t nr = 0;  
  11.   
  12.     rcu_read_lock();  
  13.     if (!ns)  
  14.         ns = current->nsproxy->pid_ns;  
  15.     if (likely(pid_alive(task))) {  
  16.         if (type != PIDTYPE_PID)  
  17.             task = task->group_leader;  
  18.         nr = pid_nr_ns(task->pids[type].pid, ns);  
  19.     }  
  20.     rcu_read_unlock();  
  21.   
  22.     return nr;  
  23. }  

    在这里,子进程共享父进程的PID命名空间。

    然后,在CLONE_PARENT_SETTID标志被设置时,把此PID值通过parent_tidptr指针输出到用户空间 

[cpp]  view plain copy
  1. if (clone_flags & CLONE_PARENT_SETTID)  
  2.     put_user(nr, parent_tidptr);  

    3)、在CLONE_VFORK标志被设置时,初始化completion同步原语 

[cpp]  view plain copy
  1. if (clone_flags & CLONE_VFORK) {  
  2.     p->vfork_done = &vfork;  
  3.     init_completion(&vfork);  
  4. }  

    completion结构体变量vfork被用于父子进程之间的同步。task_struct结构体中的vfork_done字段就是用于此目的。

    4)、系统调用审计 

[cpp]  view plain copy
  1. audit_finish_fork(p);  

    只有在CONFIG_AUDITSYSCALL选项被配置时才有效,在S3C2410的配置中没有这个选项。

    5)、如果子进程的ptrace字段不为0,则为子进程增加挂起信号(SIGSTOP),并设置它的thread_info 结构体的flags字段为TIF_SIGPENDING。 

[cpp]  view plain copy
  1. tracehook_report_clone(regs, clone_flags, nr, p);  

    6)、清除PF_STARTING标志 

[cpp]  view plain copy
  1. p->flags &= ~PF_STARTING;  

    7)、唤醒子进程 

[cpp]  view plain copy
  1. wake_up_new_task(p, clone_flags);  

    在CONFIG_SMP选项未配置时,它的实现只是简单地调用activate_task函数把子进程加载到运行队列。

    8)、当tracehook_prepare_clone的返回值trace不为0时(即父进程被跟踪),则把子进程的PID存入当前进程的ptrace_message字段,并调用ptrace_notify函数。 

[cpp]  view plain copy
  1. tracehook_report_clone_complete(trace, regs,  
  2.                 clone_flags, nr, p);  

    9)、在CLONE_VFORK标志被设置时,父进程通过调用wait_for_completion函数使其被阻塞,直到子进程退出(或执行新的程序)并接受到信号为止(内核通过complete函数)。 

[cpp]  view plain copy
  1. if (clone_flags & CLONE_VFORK) {  
  2.     freezer_do_not_count();  
  3.     wait_for_completion(&vfork);  
  4.     freezer_count();  
  5.     tracehook_report_vfork_done(p, nr);  
  6. }  

    其中,freezer_do_not_count和freezer_count函数分别用于设置或清除PF_FREEZER_SKIP标志。

    tracehook_report_vfork_done函数用于向跟踪程序报告子进程已退出或已执行新的程序。

    2、copy_process函数 

[cpp]  view plain copy
  1. static struct task_struct *copy_process(unsigned long clone_flags,  
  2.                     unsigned long stack_start,  
  3.                     struct pt_regs *regs,  
  4.                     unsigned long stack_size,  
  5.                     int __user *child_tidptr,  
  6.                     struct pid *pid,  
  7.                     int trace)  

    1)、检查冲突的clone标志 

[cpp]  view plain copy
  1. if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))  
  2.     return ERR_PTR(-EINVAL);  
  3.   
  4. if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))  
  5.     return ERR_PTR(-EINVAL);  
  6.   
  7. if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))  
  8.     return ERR_PTR(-EINVAL);  
  9.   
  10. if ((clone_flags & CLONE_PARENT) &&  
  11.             current->signal->flags & SIGNAL_UNKILLABLE)  
  12.     return ERR_PTR(-EINVAL);  

    2)、附加的安全检查 

[cpp]  view plain copy
  1. retval = security_task_create(clone_flags);  
  2. if (retval)  
  3.     goto fork_out;  

[cpp]  view plain copy
  1. /* linux-2.6.38.8/security/security.c */  
  2. int security_task_create(unsigned long clone_flags)  
  3. {  
  4.     return security_ops->task_create(clone_flags);  
  5. }  
  6. /* linux-2.6.38.8/security/capability.c */  
  7. static int cap_task_create(unsigned long clone_flags)  
  8. {  
  9.     return 0;  
  10. }  

    函数指针task_create指向cap_task_create,返回0表示允许操作。更复杂的安全模型是美国国家安全局开发的SELinux,这时的函数指针task_create指向selinux_task_create函数。

    3)、调用dup_task_struct函数为子进程获取进程描述符(即task_struct结构体) 

[cpp]  view plain copy
  1. retval = -ENOMEM;  
  2. p = dup_task_struct(current);  
  3. if (!p)  
  4.     goto fork_out;  

[cpp]  view plain copy
  1. /* linux-2.6.38.8/kernel/fork.c */  
  2. static struct task_struct *dup_task_struct(struct task_struct *orig)  

    a、对ARM体系结构来说,prepare_to_copy函数为空 

[cpp]  view plain copy
  1. prepare_to_copy(orig);  

    b、执行alloc_task_struct宏,为新进程分配进程描述符(即task_struct结构体) 

[cpp]  view plain copy
  1. tsk = alloc_task_struct();  
  2. if (!tsk)  
  3.     return NULL;  

[cpp]  view plain copy
  1. # define alloc_task_struct()    kmem_cache_alloc(task_struct_cachep, GFP_KERNEL)  

    c、调用alloc_thread_info函数分配一块2THREAD_SIZE_ORDER个页的空闲的内存区,用来存放新进程的thread_info结构体和内核栈。 

[cpp]  view plain copy
  1. ti = alloc_thread_info(tsk);  
  2. if (!ti) {  
  3.     free_task_struct(tsk);  
  4.     return NULL;  
  5. }  

[cpp]  view plain copy
  1. #define THREAD_SIZE_ORDER   1  
  2. static inline struct thread_info *alloc_thread_info(struct task_struct *tsk)  
  3. {  
  4. #ifdef CONFIG_DEBUG_STACK_USAGE  
  5.     gfp_t mask = GFP_KERNEL | __GFP_ZERO;  
  6. #else  
  7.     gfp_t mask = GFP_KERNEL;  
  8. #endif  
  9.     return (struct thread_info *)__get_free_pages(mask, THREAD_SIZE_ORDER);  
  10. }  

    进程描述符和thread_info结构体以及内核栈的关系请参考Linux进程管理之task_struct结构体(上)

    d、复制父进程task_struct结构体中的内容,但指向子进程自身的内核栈 

[cpp]  view plain copy
  1. err = arch_dup_task_struct(tsk, orig);  
  2. f (err)  
  3. goto out;  
  4.   
  5. sk->stack = ti;  

[cpp]  view plain copy
  1. int __attribute__((weak)) arch_dup_task_struct(struct task_struct *dst,  
  2.                            struct task_struct *src)  
  3. {  
  4.     *dst = *src;  
  5.     return 0;  
  6. }  

    e、初始化子进程的dirties字段 

[cpp]  view plain copy
  1. err = prop_local_init_single(&tsk->dirties);  
  2. if (err)  
  3.     goto out;  

    f、复制父进程的thread_info结构体信息,但指向子进程描述符 

[cpp]  view plain copy
  1. setup_thread_stack(tsk, orig);  

[cpp]  view plain copy
  1. /* linux-2.6.38.8/include/linux/sched.h */  
  2. #define task_thread_info(task)  ((struct thread_info *)(task)->stack)  
  3.   
  4. static inline void setup_thread_stack(struct task_struct *p, struct task_struct *org)  
  5. {  
  6.     *task_thread_info(p) = *task_thread_info(org);  
  7.     task_thread_info(p)->task = p;  
  8. }  

    g、清除子进程thread_info结构体的TIF_USER_RETURN_NOTIFY和TIF_NEED_RESCHED标志 

[cpp]  view plain copy
  1. clear_user_return_notifier(tsk);  
  2. clear_tsk_need_resched(tsk);  

    TIF_NEED_RESCHED标志表示该进程想要调度器选择另一个进程来替换它,清除该标志则表示不想要这么做。

    h、由于内核栈和thread_info结构体共享2个页面的内存,为防止内核栈中的数据破坏thread_info结构体,因此在它们之间设置一个魔数,用来表示内核栈栈底。 

[cpp]  view plain copy
  1. stackend = end_of_stack(tsk);  
  2. *stackend = STACK_END_MAGIC;    /* for overflow detection */  

[cpp]  view plain copy
  1. /* linux-2.6.38.8/include/linux/magic.h */  
  2. #define STACK_END_MAGIC     0x57AC6E9D  

    i、初始化子进程描述符的stack_canary字段 

[cpp]  view plain copy
  1. #ifdef CONFIG_CC_STACKPROTECTOR  
  2.     tsk->stack_canary = get_random_int();  
  3. #endif  

    j、设置子进程描述符的usage字段为2,表示进程描述符正在被使用而且其相应的进程处于活动状态。 

[cpp]  view plain copy
  1. atomic_set(&tsk->usage,2);  

    k、设置子进程描述符的fs_excl、btrace_seq和splice_pipe三个字段为0或NULL。 

[cpp]  view plain copy
  1. atomic_set(&tsk->fs_excl, 0);  
  2. ifdef CONFIG_BLK_DEV_IO_TRACE  
  3. tsk->btrace_seq = 0;  
  4. endif  
  5. tsk->splice_pipe = NULL;  

    l、进程内核栈计数 

[cpp]  view plain copy
  1. account_kernel_stack(ti, 1);  

    4)、初始化子进程描述符中与ftrace功能相关的字段 

[cpp]  view plain copy
  1. ftrace_graph_init_task(p);  

    5)、初始化子进程描述符的pi_lock、pi_waiters和pi_blocked_on三个字段 

[cpp]  view plain copy
  1. rt_mutex_init_task(p);  

    6)、检查子进程描述符的hardirqs_enabled和softirqs_enabled两个字段是否不为0 

[cpp]  view plain copy
  1. #ifdef CONFIG_PROVE_LOCKING  
  2.     DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled);  
  3.     DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);  
  4. #endif  

    7)、检查用户所拥有的进程数是否超过了限制,如果是,则放弃创建进程,除非当前用户是root用户或者分配了特别的权限 

[cpp]  view plain copy
  1. retval = -EAGAIN;  
  2. if (atomic_read(&p->real_cred->user->processes) >=  
  3.         task_rlimit(p, RLIMIT_NPROC)) {  
  4.     if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&  
  5.         p->real_cred->user != INIT_USER)  
  6.         goto bad_fork_free;  
  7. }  

    8)、如果设置了CLONE_THREAD标志,则copy_creds函数会使用父进程的real_cred和cred,但更新了其中的一些计数 

[cpp]  view plain copy
  1. retval = copy_creds(p, clone_flags);  
  2. if (retval < 0)  
  3.     goto bad_fork_free;  

    9)、检查系统中的进程数量(存放在nr_threads变量中)是否超过max_threads变量的值(max_threads变量的值取决于系统内存的大小,总的原则是所有进程的thread_info结构体和内核栈所占用的空间不能超过物理内存的八分之一)。 

[cpp]  view plain copy
  1. retval = -EAGAIN;  
  2. if (nr_threads >= max_threads)  
  3.     goto bad_fork_cleanup_count;  

    10)、如果实现新进程的执行域和可执行格式的内核函数都包含在内核模块中,则递增它们的使用计数器。 

[cpp]  view plain copy
  1. if (!try_module_get(task_thread_info(p)->exec_domain->module))  
  2.     goto bad_fork_cleanup_count;  

    11)、子进程描述符中众多零碎的字段的初始化 

[cpp]  view plain copy
  1.     p->did_exec = 0;    //用于记录调用execve()的次数  
  2.     delayacct_tsk_init(p);    //delays字段:分配struct task_delay_info大小的内存空间,并初始化其中的自旋锁  
  3.     copy_flags(clone_flags, p); //flags字段:清除PF_SUPERPRIV和PF_WQ_WORKER标志,并设置PF_FORKNOEXEC和PF_STARTING标志  
  4.                     //还要清除thread_info结构体的TIF_FREEZE标志  
  5.     INIT_LIST_HEAD(&p->children);   
  6.     INIT_LIST_HEAD(&p->sibling);  
  7.     rcu_copy_process(p);  
  8. /* linux-2.6.38.8/include/linux/sched.h */  
  9. static inline void rcu_copy_process(struct task_struct *p)  
  10. {  
  11.     p->rcu_read_lock_nesting = 0;  
  12.     p->rcu_read_unlock_special = 0;  
  13. #ifdef CONFIG_TREE_PREEMPT_RCU  
  14.     p->rcu_blocked_node = NULL;  
  15. #endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */  
  16. #ifdef CONFIG_RCU_BOOST  
  17.     p->rcu_boost_mutex = NULL;  
  18. #endif /* #ifdef CONFIG_RCU_BOOST */  
  19.     INIT_LIST_HEAD(&p->rcu_node_entry);  
  20. }  
  21.   
  22.     p->vfork_done = NULL; //在do_fork函数中会用到  
  23.     spin_lock_init(&p->alloc_lock); //初始化自旋锁  
  24.   
  25.     init_sigpending(&p->pending);  //清除信号集  
  26. /* linux-2.6.38.8/include/linux/signal.h */  
  27. static inline void init_sigpending(struct sigpending *sig)  
  28. {  
  29.     sigemptyset(&sig->signal);  
  30.     INIT_LIST_HEAD(&sig->list);  
  31. }  
  32.   
  33. //初始化进程的各种时间为0  
  34. //#define cputime_zero  (0UL)  /* linux-2.6.38.8/include/asm-generic/cputime.h */  
  35.     p->utime = cputime_zero;  
  36.     p->stime = cputime_zero;  
  37.     p->gtime = cputime_zero;  
  38.     p->utimescaled = cputime_zero;  
  39.     p->stimescaled = cputime_zero;  
  40. #ifndef CONFIG_VIRT_CPU_ACCOUNTING  
  41.     p->prev_utime = cputime_zero;  
  42.     p->prev_stime = cputime_zero;  
  43. #endif  
  44. #if defined(SPLIT_RSS_COUNTING)  
  45.     memset(&p->rss_stat, 0, sizeof(p->rss_stat));     //初始化struct task_rss_stat变量为0  
  46. #endif  
  47.   
  48.     p->default_timer_slack_ns = current->timer_slack_ns;  //初始化为父进程的timer_slack_ns值  
  49.   
  50.     task_io_accounting_init(&p->ioac);  //初始化struct task_io_accounting变量为0  
  51.     acct_clear_integrals(p);  
  52. /* linux-2.6.38.8/kernel/tsacct.c */  
  53. void acct_clear_integrals(struct task_struct *tsk)  
  54. {  
  55.     tsk->acct_timexpd = 0;  
  56.     tsk->acct_rss_mem1 = 0;  
  57.     tsk->acct_vm_mem1 = 0;  
  58. }  
  59.   
  60.     posix_cpu_timers_init(p);  
  61. /* linux-2.6.38.8/kernel/fork.c */  
  62. static void posix_cpu_timers_init(struct task_struct *tsk)  
  63. {  
  64.     tsk->cputime_expires.prof_exp = cputime_zero;  
  65.     tsk->cputime_expires.virt_exp = cputime_zero;  
  66.     tsk->cputime_expires.sched_exp = 0;  
  67.     INIT_LIST_HEAD(&tsk->cpu_timers[0]);  
  68.     INIT_LIST_HEAD(&tsk->cpu_timers[1]);  
  69.     INIT_LIST_HEAD(&tsk->cpu_timers[2]);  
  70. }  
  71.   
  72.     p->lock_depth = -1;   //大内核锁,-1表示不上锁  
  73.     do_posix_clock_monotonic_gettime(&p->start_time);  
  74.     p->real_start_time = p->start_time;  
  75.     monotonic_to_bootbased(&p->real_start_time); //真实启动时间还须要加上总的睡眠时间  
  76.     p->io_context = NULL;  
  77.     p->audit_context = NULL;  
  78.     cgroup_fork(p); //共享父进程的cgroups,并更新其中的引用计数  
  79. /* linux-2.6.38.8/kernel/cgroup.c */  
  80. void cgroup_fork(struct task_struct *child)  
  81. {  
  82.     task_lock(current);  
  83.     child->cgroups = current->cgroups;  
  84.     get_css_set(child->cgroups);  
  85.     task_unlock(current);  
  86.     INIT_LIST_HEAD(&child->cg_list);  
  87. }  
  88.   
  89. #ifdef CONFIG_NUMA  
  90.     p->mempolicy = mpol_dup(p->mempolicy); //为子进程分配新的struct mempolicy变量,但拷贝父进程mempolicy变量中的内容  
  91.     if (IS_ERR(p->mempolicy)) {  
  92.         retval = PTR_ERR(p->mempolicy);  
  93.         p->mempolicy = NULL;  
  94.         goto bad_fork_cleanup_cgroup;  
  95.     }  
  96.     mpol_fix_fork_child_flag(p); //flags字段:如果新的struct mempolicy变量分配成功,则增加PF_MEMPOLICY标志  
  97. #endif  
  98.   
  99. #ifdef CONFIG_TRACE_IRQFLAGS  
  100.     p->irq_events = 0;  
  101. #ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW  
  102.     p->hardirqs_enabled = 1;  
  103. #else  
  104.     p->hardirqs_enabled = 0;  
  105. #endif  
  106.     p->hardirq_enable_ip = 0;  
  107.     p->hardirq_enable_event = 0;  
  108.     p->hardirq_disable_ip = _THIS_IP_;  
  109.     p->hardirq_disable_event = 0;  
  110.     p->softirqs_enabled = 1;  
  111.     p->softirq_enable_ip = _THIS_IP_;  
  112.     p->softirq_enable_event = 0;  
  113.     p->softirq_disable_ip = 0;  
  114.     p->softirq_disable_event = 0;  
  115.     p->hardirq_context = 0;  
  116.     p->softirq_context = 0;  
  117. #endif  
  118. #ifdef CONFIG_LOCKDEP  
  119.     p->lockdep_depth = 0; /* no locks held yet */  
  120.     p->curr_chain_key = 0;  
  121.     p->lockdep_recursion = 0;  
  122. #endif  
  123.   
  124. #ifdef CONFIG_DEBUG_MUTEXES  
  125.     p->blocked_on = NULL; /* not blocked yet */  
  126. #endif  
  127. #ifdef CONFIG_CGROUP_MEM_RES_CTLR  
  128.     p->memcg_batch.do_batch = 0;  
  129.     p->memcg_batch.memcg = NULL;  
  130. #endif  

    12)、调用sched_fork函数初始化子进程描述符与调度有关的字段 

[cpp]  view plain copy
  1. sched_fork(p, clone_flags);  

[cpp]  view plain copy
  1. /* linux-2.6.38.8/kernel/sched.c */  
  2. static void __sched_fork(struct task_struct *p)  
  3. {  
  4.     p->se.exec_start     = 0;  
  5.     p->se.sum_exec_runtime       = 0;  
  6.     p->se.prev_sum_exec_runtime  = 0;  
  7.     p->se.nr_migrations      = 0;  
  8.   
  9. #ifdef CONFIG_SCHEDSTATS  
  10.     memset(&p->se.statistics, 0, sizeof(p->se.statistics));  
  11. #endif  
  12.   
  13.     INIT_LIST_HEAD(&p->rt.run_list);  
  14.     p->se.on_rq = 0;  
  15.     INIT_LIST_HEAD(&p->se.group_node);  
  16.   
  17. #ifdef CONFIG_PREEMPT_NOTIFIERS  
  18.     INIT_HLIST_HEAD(&p->preempt_notifiers);  
  19. #endif  
  20. }  
  21.   
  22. void sched_fork(struct task_struct *p, int clone_flags)  
  23. {  
  24.     int cpu = get_cpu();  
  25.   
  26.     __sched_fork(p);  
  27.     /* 
  28.      * We mark the process as running here. This guarantees that 
  29.      * nobody will actually run it, and a signal or other external 
  30.      * event cannot wake it up and insert it on the runqueue either. 
  31.      */  
  32.     p->state = TASK_RUNNING;  
  33.   
  34.     /* 
  35.      * Revert to default priority/policy on fork if requested. 
  36.      */  
  37.     if (unlikely(p->sched_reset_on_fork)) {  
  38.         if (p->policy == SCHED_FIFO || p->policy == SCHED_RR) {  
  39.             p->policy = SCHED_NORMAL;  
  40.             p->normal_prio = p->static_prio;  
  41.         }  
  42.   
  43.         if (PRIO_TO_NICE(p->static_prio) < 0) {  
  44.             p->static_prio = NICE_TO_PRIO(0);  
  45.             p->normal_prio = p->static_prio;  
  46.             set_load_weight(p);  
  47.         }  
  48.   
  49.         /* 
  50.          * We don't need the reset flag anymore after the fork. It has 
  51.          * fulfilled its duty: 
  52.          */  
  53.         p->sched_reset_on_fork = 0;  
  54.     }  
  55.   
  56.     /* 
  57.      * Make sure we do not leak PI boosting priority to the child. 
  58.      */  
  59.     p->prio = current->normal_prio;  
  60.   
  61.     if (!rt_prio(p->prio))  
  62.         p->sched_class = &fair_sched_class;  
  63.   
  64.     if (p->sched_class->task_fork)  
  65.         p->sched_class->task_fork(p);  
  66.   
  67.     /* 
  68.      * The child is not yet in the pid-hash so no cgroup attach races, 
  69.      * and the cgroup is pinned to this child due to cgroup_fork() 
  70.      * is ran before sched_fork(). 
  71.      * 
  72.      * Silence PROVE_RCU. 
  73.      */  
  74.     rcu_read_lock();  
  75.     set_task_cpu(p, cpu);  
  76.     rcu_read_unlock();  
  77.   
  78. #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT)  
  79.     if (likely(sched_info_on()))  
  80.         memset(&p->sched_info, 0, sizeof(p->sched_info));  
  81. #endif  
  82. #if defined(CONFIG_SMP) && defined(__ARCH_WANT_UNLOCKED_CTXSW)  
  83.     p->oncpu = 0;  
  84. #endif  
  85. #ifdef CONFIG_PREEMPT  
  86.     /* Want to start with kernel preemption disabled. */  
  87.     task_thread_info(p)->preempt_count = 1;  
  88. #endif  
  89. #ifdef CONFIG_SMP  
  90.     plist_node_init(&p->pushable_tasks, MAX_PRIO);  
  91. #endif  
  92.   
  93.     put_cpu();  
  94. }  

    该函数把子进程的状态设置为TASK_RUNNING,并把thread_info结构体的preempt_count字段设置为1,从而禁止内核抢占。

    13)、初始化Performance Event功能相关的字段 

[cpp]  view plain copy
  1. retval = perf_event_init_task(p);  
  2. if (retval)  
  3.     goto bad_fork_cleanup_policy;  

[cpp]  view plain copy
  1. /* linux-2.6.38.8/kernel/perf_event.c */  
  2. int perf_event_init_task(struct task_struct *child)  
  3. {  
  4.     int ctxn, ret;  
  5.   
  6.     memset(child->perf_event_ctxp, 0, sizeof(child->perf_event_ctxp));  
  7.     mutex_init(&child->perf_event_mutex);  
  8.     INIT_LIST_HEAD(&child->perf_event_list);  
  9.   
  10.     for_each_task_context_nr(ctxn) {  
  11.         ret = perf_event_init_context(child, ctxn);  
  12.         if (ret)  
  13.             return ret;  
  14.     }  
  15.   
  16.     return 0;  
  17. }  

    14)、分配一个struct audit_context变量,并把它赋给子进程描述符的audit_context字段 

[cpp]  view plain copy
  1. if ((retval = audit_alloc(p)))  
  2.     goto bad_fork_cleanup_policy;  

    15)、各种数据的拷贝 

[cpp]  view plain copy
  1. if ((retval = copy_semundo(clone_flags, p)))  
  2.     goto bad_fork_cleanup_audit;  
  3. if ((retval = copy_files(clone_flags, p)))  
  4.     goto bad_fork_cleanup_semundo;  
  5. if ((retval = copy_fs(clone_flags, p)))  
  6.     goto bad_fork_cleanup_files;  
  7. if ((retval = copy_sighand(clone_flags, p)))  
  8.     goto bad_fork_cleanup_fs;  
  9. if ((retval = copy_signal(clone_flags, p)))  
  10.     goto bad_fork_cleanup_sighand;  
  11. if ((retval = copy_mm(clone_flags, p)))  
  12.     goto bad_fork_cleanup_signal;  
  13. if ((retval = copy_namespaces(clone_flags, p)))  
  14.     goto bad_fork_cleanup_mm;  
  15. if ((retval = copy_io(clone_flags, p)))  
  16.     goto bad_fork_cleanup_namespaces;  

    a、如果CLONE_SYSVSEM标志被设置,copy_semundo则使用父进程的System V信号量。

    b、如果CLONE_FILES标志被设置,copy_files则使用父进程的文件描述符;否则创建新的struct files_struct变量。

    c、如果CLONE_FS标志被设置,copy_fs则使用父进程的struct fs_struct变量。

    d、如果CLONE_SIGHAND标志被设置,copy_sighand则使用父进程的信号处理程序。

    e、如果CLONE_THREAD标志被设置,copy_signal则使用父进程的struct signal_struct变量。

    f、如果CLONE_VM标志被设置,copy_mm则会让父子进程共享同一地址空间,并把相关的min_flt、maj_flt、nvcsw、nivcsw和last_switch_count字段都初始化为0。

    g、如果与命名空间相关的标志都未被设置,copy_namespaces则会让子进程共享父进程相应的命名空间。

    h、如果CLONE_IO标志被设置,copy_io则使用父进程的struct io_context变量。

    16)、初始化thread_info结构体 

[cpp]  view plain copy
  1. retval = copy_thread(clone_flags, stack_start, stack_size, p, regs);  
  2. if (retval)  
  3.     goto bad_fork_cleanup_io;  

    17)、如果传入的pid参数不为init_struct_pid,则会申请一个新的struct pid实例,并且在CLONE_NEWPID标志被设置时,还需要在proc文件系统中输出新的PID命名空间。 

[cpp]  view plain copy
  1. if (pid != &init_struct_pid) {  
  2.     retval = -ENOMEM;  
  3.     pid = alloc_pid(p->nsproxy->pid_ns);  
  4.     if (!pid)  
  5.         goto bad_fork_cleanup_io;  
  6.   
  7.     if (clone_flags & CLONE_NEWPID) {  
  8.         retval = pid_ns_prepare_proc(p->nsproxy->pid_ns);  
  9.         if (retval < 0)  
  10.             goto bad_fork_free_pid;  
  11.     }  
  12. }  

    18)、获取全局PID值,并在CLONE_THREAD标志被设置时,把子进程的tgid的值初始化为父进程的tgid值。 

[cpp]  view plain copy
  1. p->pid = pid_nr(pid);  
  2. p->tgid = p->pid;  
  3. if (clone_flags & CLONE_THREAD)  
  4.     p->tgid = current->tgid;  

    19)、当父子进程的nsproxy不相等时,初始化子进程描述符的cgroups和cg_list两个字段 

[cpp]  view plain copy
  1. if (current->nsproxy != p->nsproxy) {  
  2.     retval = ns_cgroup_clone(p, pid);  
  3.     if (retval)  
  4.         goto bad_fork_free_pid;  
  5. }  

    20)、另一些零碎字段的初始化 

[cpp]  view plain copy
  1. /* 用户空间线程库使用clone系统调用来生成新线程 */  
  2.     p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL; //保存clone系统调用的用户空间指针  
  3.     /* 
  4.      * Clear TID on mm_release()? 
  5.      */  
  6.     p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL; //把用户空间指针保存在另一个字段  
  7. #ifdef CONFIG_FUTEX  
  8.     p->robust_list = NULL;  
  9. #ifdef CONFIG_COMPAT  
  10.     p->compat_robust_list = NULL;  
  11. #endif  
  12.     INIT_LIST_HEAD(&p->pi_state_list);  
  13.     p->pi_state_cache = NULL;  
  14. #endif  
  15.   
  16.     /* 
  17.      * sigaltstack should be cleared when sharing the same VM 
  18.      */  
  19.     if ((clone_flags & (CLONE_VM|CLONE_VFORK)) == CLONE_VM)  
  20.         p->sas_ss_sp = p->sas_ss_size = 0;  
  21.   
  22.     /* 
  23.      * Syscall tracing and stepping should be turned off in the 
  24.      * child regardless of CLONE_PTRACE. 
  25.      */  
  26.     user_disable_single_step(p); //ptrace字段:清除PT_SINGLESTEP标志  
  27.                      //thread字段:清除断点(breakpoint)  
  28.     clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE); //清除thread_info结构体的TIF_SYSCALL_TRACE标志  
  29. #ifdef TIF_SYSCALL_EMU  
  30.     clear_tsk_thread_flag(p, TIF_SYSCALL_EMU); //清除thread_info结构体的TIF_SYSCALL_EMU标志  
  31. #endif  
  32.     clear_all_latency_tracing(p); //初始化子进程描述符的latency_record和latency_record_count两个字段为0  
  33.   
  34.     /* ok, now we should be set up.. */  
  35.     p->exit_signal = (clone_flags & CLONE_THREAD) ? -1 : (clone_flags & CSIGNAL);   
  36.                         //CLONE_THREAD标志被设置时,初始化为-1,否则初始化为clone_flags的最后8位  
  37.     p->pdeath_signal = 0;  
  38.     p->exit_state = 0;  
  39.   
  40.     p->group_leader = p; //子进程是所在线程组的领头进程  
  41.     INIT_LIST_HEAD(&p->thread_group);  
  42.   
  43.   
  44.     cgroup_fork_callbacks(p); //cgroups字段:共享父进程的struct css_set结构体  
  45. /* linux-2.6.38.8/kernel/cgroup.c */  
  46. void cgroup_fork(struct task_struct *child)  
  47. {  
  48.     task_lock(current);  
  49.     child->cgroups = current->cgroups;  
  50.     get_css_set(child->cgroups);  
  51.     task_unlock(current);  
  52.     INIT_LIST_HEAD(&child->cg_list);  
  53. }  
  54.   
  55.   
  56. //对线程来说(当CLONE_THREAD标志被设置时),子进程的父进程是当前进程的父进程  
  57.     if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) {  
  58.         p->real_parent = current->real_parent;  
  59.         p->parent_exec_id = current->parent_exec_id;  
  60.     } else {  
  61.         p->real_parent = current;  
  62.         p->parent_exec_id = current->self_exec_id;  
  63.     }  
  64.   
  65. //普通进程的线程组组长是进程本身,但对线程来说(即CLONE_THREAD标志被设置时),其组长是当前进程的组长。  
  66.     if (clone_flags & CLONE_THREAD) {  
  67.         current->signal->nr_threads++;  
  68.         atomic_inc(&current->signal->live);  
  69.         atomic_inc(&current->signal->sigcnt);  
  70.         p->group_leader = current->group_leader;  
  71.         list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group);  
  72.     }  
  73.   
  74.   
  75.     if (likely(p->pid)) {  
  76.         tracehook_finish_clone(p, clone_flags, trace);  
  77.   
  78.         if (thread_group_leader(p)) { //判断该进程是不是线程组的组长,若是,则须进行以下的操作  
  79.                           //#define thread_group_leader(p)  (p == p->group_leader)  
  80.             if (clone_flags & CLONE_NEWPID)  
  81.                 p->nsproxy->pid_ns->child_reaper = p; //在创建新的PID命名空间时,会让其中的child_reaper字段指向自身  
  82.   
  83.             p->signal->leader_pid = pid;  
  84.             p->signal->tty = tty_kref_get(current->signal->tty);  
  85.             attach_pid(p, PIDTYPE_PGID, task_pgrp(current)); //获取PIDTYPE_PGID类型的struct pid,并插入到相应的链表中  
  86.             attach_pid(p, PIDTYPE_SID, task_session(current)); //获取PIDTYPE_SID类型的struct pid,并插入到相应的链表中  
  87.             list_add_tail(&p->sibling, &p->real_parent->children); //插入到兄弟进程链表中,它们都是同一父进程的子进程  
  88.             list_add_tail_rcu(&p->tasks, &init_task.tasks);  //插入到进程链表中,可通过此链表遍历系统中所有的进程  
  89.             __this_cpu_inc(process_counts);  
  90.         }  
  91.         attach_pid(p, PIDTYPE_PID, pid);  //把PIDTYPE_PID类型的struct pid插入到相应的链表中  
  92.         nr_threads++; //递增进程数量的统计  
  93.     }  
  94.   
  95.   
  96.     total_forks++; //递增total_forks变量,用以记录被创建的进程的数量  
  97.   
  98.     proc_fork_connector(p); //初始化struct proc_event结构的fork字段  
  99.     cgroup_post_fork(p); //构建cg_list链表  
  100. /* linux-2.6.38.8/kernel/cgroup.c */  
  101. void cgroup_post_fork(struct task_struct *child)  
  102. {  
  103.     if (use_task_css_set_links) {  
  104.         write_lock(&css_set_lock);  
  105.         task_lock(child);  
  106.         if (list_empty(&child->cg_list))  
  107.             list_add(&child->cg_list, &child->cgroups->tasks);  
  108.         task_unlock(child);  
  109.         write_unlock(&css_set_lock);  
  110.     }  
  111. }  
  112.   
  113.     perf_event_fork(p); //初始化struct perf_task_event结构的task字段为p  

 

你可能感兴趣的:(Linux进程管理之进程的创建)