Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1078803
  • 博文数量: 277
  • 博客积分: 8313
  • 博客等级: 中将
  • 技术积分: 2976
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-22 11:25
文章分类

全部博文(277)

文章存档

2013年(17)

2012年(66)

2011年(104)

2010年(90)

我的朋友

分类: LINUX

2012-04-18 18:44:42

Linux中关于进程创建的主要函数有fork,vfork,clone,他们的实现都是有do_fork来完成的,只是传入的参数有差别,do_fork中调用函数copy_process从父进程中复制相关内容到子进程,其中这个复制量的确定是根据传入参数flag来确定是否需要重新申请内存还是共享父进程的资源,下面对具体的代码进行分析。

  1. /*唯一使用的标志是SIGCHLD。这意味着在子进程终止后 
  2. 发送SIGCHLD信号通知父进程。 
  3. */  
  4. int sys_fork(struct pt_regs *regs)  
  5. {  
  6.     return do_fork(SIGCHLD, regs->sp, regs, 0, NULL, NULL);  
  7. }  
  8.   
  9. /* 
  10.  * This is trivial, and on the face of it looks like it 
  11.  * could equally well be done in user mode. 
  12.  * 
  13.  * Not so, for quite unobvious reasons - register pressure. 
  14.  * In user mode vfork() cannot have a stack frame, and if 
  15.  * done by calling the "clone()" system call directly, you 
  16.  * do not have enough call-clobbered registers to hold all 
  17.  * the information you need. 
  18.  */  
  19. int sys_vfork(struct pt_regs *regs)  
  20. {  
  21.     return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->sp, regs, 0,  
  22.                NULL, NULL);  
  23. }  
  1. /*可以看到,参数的设置取决于用户的设置*/  
  2. int sys_clone(struct pt_regs *regs)  
  3. {  
  4.     unsigned long clone_flags;  
  5.     unsigned long newsp;  
  6.     int __user *parent_tidptr, *child_tidptr;  
  7.   
  8.     clone_flags = regs->bx;  
  9.     newsp = regs->cx;  
  10.     parent_tidptr = (int __user *)regs->dx;  
  11.     child_tidptr = (int __user *)regs->di;  
  12.     if (!newsp)  
  13.         newsp = regs->sp;  
  14.     return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);  
  15. }  

下面是具体的do_fork函数

  1. /* 
  2.  *  Ok, this is the main fork-routine. 
  3.  * 
  4.  * It copies the process, and if successful kick-starts 
  5.  * it and waits for it to finish using the VM if required. 
  6.  */  
  7.  /*clone_flags是一个标志集合,用来指定控制复制过程的一些属性。 
  8.  最低字节指定了在子进程终止时被发给父进程的信号号码。其余的高位 
  9.  字节保存了各种常数,下文会分别讨论。 
  10.  
  11. start_stack是用户状态下栈的起始地址。 
  12.  
  13. regs是一个指向寄存器集合的指针,其中以原始形式保存了调用参数。 
  14. 该参数使用的数据类型是特定于体系结构的struct pt_regs,其中按照 
  15. 系统调用执行时寄存器在内核栈上的存储顺序,保存了所有的寄存器 
  16. (更详细的信息,请参考附录A)。 
  17.  
  18. stack_size是用户状态下栈的大小。该参数通常是不必要的,设置为0。 
  19.  
  20. parent_tidptr和child_tidptr是指向用户空间中地址的两个指针 
  21. ,分别指向父子进程的TID。NPTL(Native Posix Threads Library) 
  22. 库的线程实现需要这两个参数。 
  23. */  
  24. long do_fork(unsigned long clone_flags,  
  25.           unsigned long stack_start,  
  26.           struct pt_regs *regs,  
  27.           unsigned long stack_size,  
  28.           int __user *parent_tidptr,  
  29.           int __user *child_tidptr)  
  30. {  
  31.     struct task_struct *p;  
  32.     int trace = 0;  
  33.     long nr;  
  34.   
  35.     /* 
  36.      * Do some preliminary argument and permissions checking before we 
  37.      * actually start allocating stuff 
  38.      */  
  39.      /*下面是标志组合的正确性检查*/  
  40.     if (clone_flags & CLONE_NEWUSER) {  
  41.         if (clone_flags & CLONE_THREAD)  
  42.             return -EINVAL;  
  43.         /* hopefully this check will go away when userns support is 
  44.          * complete 
  45.          *//*权限*/  
  46.         if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SETUID) ||  
  47.                 !capable(CAP_SETGID))  
  48.             return -EPERM;  
  49.     }  
  50.   
  51.     /* 
  52.      * We hope to recycle these flags after 2.6.26 
  53.      */  
  54.      /*如果设置了CLONE_STOPPED标志,进程内核打印 
  55.      输出创建标志信息*/  
  56.     if (unlikely(clone_flags & CLONE_STOPPED)) {  
  57.         static int __read_mostly count = 100;  
  58.         /*函数printk_ratelimit为打印控制,具体会在后面做分析*/  
  59.         if (count > 0 && printk_ratelimit()) {  
  60.             char comm[TASK_COMM_LEN];  
  61.   
  62.             count--;  
  63.             printk(KERN_INFO "fork(): process `%s' used deprecated "  
  64.                     "clone flags 0x%lx\n",  
  65.                 get_task_comm(comm, current),  
  66.                 clone_flags & CLONE_STOPPED);  
  67.         }  
  68.     }  
  69.   
  70.     /* 
  71.      * When called from kernel_thread, don't do user tracing stuff. 
  72.      */  
  73.      /*whether a register set came from user mode.*/  
  74.     if (likely(user_mode(regs)))  
  75.         trace = tracehook_prepare_clone(clone_flags);/*Its return value will be passed to tracehook_finish_clone().*/  
  76.     /*复制一个进程,并对相应标志位进行设定,参见后面的详细介绍 
  77.     */  
  78.     p = copy_process(clone_flags, stack_start, regs, stack_size,  
  79.              child_tidptr, NULL, trace);  
  80.     /* 
  81.      * Do this prior waking up the new thread - the thread pointer 
  82.      * might get invalid after that point, if the thread exits quickly. 
  83.      */  
  84.     if (!IS_ERR(p)) {/*判断p的有效性*/  
  85.         struct completion vfork;  
  86.         /*???*/  
  87.         trace_sched_process_fork(current, p);  
  88.   
  89.         nr = task_pid_vnr(p);/*返回进程p的命名空间中的pid*/  
  90.         /*Write a simple value into user space*/  
  91.         if (clone_flags & CLONE_PARENT_SETTID)/* if set the TID in the parent */  
  92.             put_user(nr, parent_tidptr);/*将nr放到parent_tidptr地址中 
  93.                                     此地址为用户空间中的*/  
  94.         /*如果设置了这个标志,将初始化进程中的 
  95.         completion结构*/  
  96.         if (clone_flags & CLONE_VFORK) {  
  97.             p->vfork_done = &vfork;  
  98.             init_completion(&vfork);  
  99.         }  
  100.          /*audit context内容的复制,需要该变量空间已经存在*/  
  101.         audit_finish_fork(p);  
  102.          /*如果进子进程被跟踪则发送SIGSTOP信号. 
  103.         由于子进程现在还没有运行,信号不能被处理 
  104.         所以设置TIF_SIGPENDING标志*/  
  105.         tracehook_report_clone(regs, clone_flags, nr, p);  
  106.   
  107.         /* 
  108.          * We set PF_STARTING at creation in case tracing wants to 
  109.          * use this to distinguish a fully live task from one that 
  110.          * hasn't gotten to tracehook_report_clone() yet.  Now we 
  111.          * clear it and set the child going. 
  112.          */  
  113.         p->flags &= ~PF_STARTING;  
  114.         /*如果子进程初始化成STOP状态 
  115.         则发送SIGSTOP信号.由于子进程现在还没有运行,信号不能被处理 
  116.         所以设置TIF_SIGPENDING标志*/  
  117.         if (unlikely(clone_flags & CLONE_STOPPED)) {  
  118.             /* 
  119.              * We'll start up with an immediate SIGSTOP. 
  120.              */  
  121.             sigaddset(&p->pending.signal, SIGSTOP);  
  122.             set_tsk_thread_flag(p, TIF_SIGPENDING);  
  123.             __set_task_state(p, TASK_STOPPED);  
  124.         } else {/*进入运行队列,调用调度类的唤醒函数,后面详细介绍*/  
  125.             wake_up_new_task(p, clone_flags);  
  126.         }  
  127.   
  128.         tracehook_report_clone_complete(trace, regs,  
  129.                         clone_flags, nr, p);  
  130.         /*如果定义了CLONE_VFORK标志.则将当前进程投入睡眠*/  
  131.         if (clone_flags & CLONE_VFORK) {  
  132.             freezer_do_not_count();  
  133.             wait_for_completion(&vfork);  
  134.             freezer_count();  
  135.             tracehook_report_vfork_done(p, nr);  
  136.         }  
  137.     } else {  
  138.         nr = PTR_ERR(p);  
  139.     }  
  140.     return nr;  
  141. }  
对于设置了CLONE_VFORK标志的,调用下面函数

  1. /** 
  2.  * init_completion: - Initialize a dynamically allocated completion 
  3.  * @x:  completion structure that is to be initialized 
  4.  * 
  5.  * This inline function will initialize a dynamically created completion 
  6.  * structure. 
  7.  */  
  8. static inline void init_completion(struct completion *x)  
  9. {   /*done标志为0。表示子进程还没有将父进程唤醒*/  
  10.     x->done = 0;  
  11.     /*初始化一个等待队列*/  
  12.     init_waitqueue_head(&x->wait);  
  13. }  
和下面的代码呼应

  1. /*如果定义了CLONE_VFORK标志.则将当前进程投入睡眠*/  
  2.         if (clone_flags & CLONE_VFORK) {  
  3.             freezer_do_not_count();  
  4.             wait_for_completion(&vfork);  
  5.             freezer_count();  
  6.             tracehook_report_vfork_done(p, nr);  
  7.         }  
唤醒函数,将进程入运行队列

  1. /* 
  2.  * wake_up_new_task - wake up a newly created task for the first time. 
  3.  * 
  4.  * This function will do some initial scheduler statistics housekeeping 
  5.  * that must be done for every newly created context, then puts the task 
  6.  * on the runqueue and wakes it. 
  7.  */  
  8. void wake_up_new_task(struct task_struct *p, unsigned long clone_flags)  
  9. {  
  10.     unsigned long flags;  
  11.     struct rq *rq;  
  12.   
  13.     rq = task_rq_lock(p, &flags);/*获得指定task的rq*/  
  14.     BUG_ON(p->state != TASK_RUNNING);  
  15.     update_rq_clock(rq);/*更新rq的时钟计时*/  
  16.   
  17.     if (!p->sched_class->task_new || !current->se.on_rq) {  
  18.         activate_task(rq, p, 0);/*进入运行队列*/  
  19.     } else {  
  20.         /* 
  21.          * Let the scheduling class do new task startup 
  22.          * management (if any): 
  23.          */  
  24.         p->sched_class->task_new(rq, p);  
  25.         inc_nr_running(rq);  
  26.     }  
  27.     trace_sched_wakeup_new(rq, p, 1);  
  28.     /*用以決定一個Task是否可以中斷目前正在 
  29.     運作的Task,取得執行權.*/  
  30.     check_preempt_curr(rq, p, WF_FORK);  
  31. #ifdef CONFIG_SMP  
  32.     if (p->sched_class->task_wake_up)  
  33.         p->sched_class->task_wake_up(rq, p);  
  34. #endif  
  35.     task_rq_unlock(rq, &flags);  
  36. }  

对于copy_process函数比较繁琐,也是do_fork主要执行函数,完成进程资源的复制,根据相关的标志位,有的资源需要和父进程共享,具体下一篇我们会看到。
阅读(676) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~