Chinaunix首页 | 论坛 | 博客
  • 博客访问: 134309
  • 博文数量: 39
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 237
  • 用 户 组: 普通用户
  • 注册时间: 2013-09-23 10:59
文章分类

全部博文(39)

文章存档

2016年(2)

2015年(25)

2014年(10)

2013年(2)

我的朋友

分类: LINUX

2015-08-23 16:12:31

 Linux下用于创建进程的API有三个fork,vfork和clone,这三个函数分别是通过系统调用sys_fork,sys_vfork以及sys_clone实现的
注:这里目前讨论的都是基于x86架构的
fork
  1. int sys_fork(struct pt_regs *regs)
  2. {
  3.     return do_fork(SIGCHLD, regs->sp, regs, 0, NULL, NULL);
  4. }
vfork
  1. int sys_vfork(struct pt_regs *regs)
  2. {
  3.     return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->sp, regs, 0,
  4.          NULL, NULL);
  5. }
clone
  1. int sys_vfork(struct pt_regs *regs)
  2. {
  3.     return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->sp, regs, 0,
  4.          NULL, NULL);
  5. }
由以上代码可知,最终这三个系统调用,都是通过do_fork来实现的,只是传入了不同的参数。

那么接下来先看看do_fork的实现
  1. long do_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)
  7. {
  8.     struct task_struct *p;
  9.     int trace = 0;
  10.     long nr;

  11.     /*
  12.      * Do some preliminary argument and permissions checking before we
  13.      * actually start allocating stuff
  14.      */
  15.     if (clone_flags & CLONE_NEWUSER) {
  16.         if (clone_flags & CLONE_THREAD)
  17.             return -EINVAL;
  18.         /* hopefully this check will go away when userns support is
  19.          * complete
  20.          */
  21.         if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SETUID) ||
  22.                 !capable(CAP_SETGID))
  23.             return -EPERM;
  24.     }

  25.     /*
  26.      * When called from kernel_thread, don't do user tracing stuff.
  27.      */
  28.     if (likely(user_mode(regs)))
  29.         trace = tracehook_prepare_clone(clone_flags);
  30. 1)
  31.     p = copy_process(clone_flags, stack_start, regs, stack_size,
  32.              child_tidptr, NULL, trace);
  33.     /*
  34.      * Do this prior waking up the new thread - the thread pointer
  35.      * might get invalid after that point, if the thread exits quickly.
  36.      */
  37.     if (!IS_ERR(p)) {
  38.         struct completion vfork;

  39.         trace_sched_process_fork(current, p);
  40. 2)
  41.         nr = task_pid_vnr(p);

  42.         if (clone_flags & CLONE_PARENT_SETTID)
  43.             put_user(nr, parent_tidptr);

  44.         if (clone_flags & CLONE_VFORK) {
  45.             p->vfork_done = &vfork;
  46.             init_completion(&vfork);
  47.         }

  48.         audit_finish_fork(p);
  49.         tracehook_report_clone(regs, clone_flags, nr, p);

  50.         /*
  51.          * We set PF_STARTING at creation in case tracing wants to
  52.          * use this to distinguish a fully live task from one that
  53.          * hasn't gotten to tracehook_report_clone() yet. Now we
  54.          * clear it and set the child going.
  55.          */
  56.         p->flags &= ~PF_STARTING;

  57.         wake_up_new_task(p, clone_flags);

  58.         tracehook_report_clone_complete(trace, regs,
  59.                         clone_flags, nr, p);

  60.         if (clone_flags & CLONE_VFORK) {
  61.             freezer_do_not_count();
  62.             wait_for_completion(&vfork);
  63.             freezer_count();
  64.             tracehook_report_vfork_done(p, nr);
  65.         }
  66.     } else {
  67.         nr = PTR_ERR(p);
  68.     }
  69.     return nr;
  70. }
代码不是很长,但是涉及到很多工作,这里简单讲解一下主体流程,细节方面的东西后面再解释:
1、copy_process:这个步骤主要是执行生成新进程的主要工作,并根据指定的标志重用父进程的一些数据;
  1. p = copy_process(clone_flags, stack_start, regs, stack_size,
  2.              child_tidptr, NULL, trace);
2、确定PID:注意这里不是生成新的PID,而是确定局部PID;
  1. nr = task_pid_vnr(p);
3、init_completion:初始化vfork的完成处理程序(即是vfork调用时设置的CLONE_VFORK标志)和ptrace标志;
  1. if (clone_flags & CLONE_VFORK) {
  2.             p->vfork_done = &vfork;
  3.             init_completion(&vfork);
  4.         }

4、wake_up_new_task:这个是保证子进程在父进程之前调用,如果子进程立即调用exec,这样可以极大的减少复制内存页的工作量;
  1. wake_up_new_task(p, clone_flags);
5、wait_for_completion:跟上面的init_completion对应,让父进程在该task_struct的vfork_done成员上进入睡眠状态,直到子进程退出或者执行exec调用时,内核才会调用complete(vfork_done),这样就会唤醒父进程(也包含其他因该变量睡眠的进程)。
  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.         }

这中间还有很多的细节没有讲解,这里主要梳理了一下大致的流程。

接下来会对上面的流程一一进行讲解。

阅读(1503) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~