Chinaunix首页 | 论坛 | 博客
  • 博客访问: 75112
  • 博文数量: 15
  • 博客积分: 1410
  • 博客等级: 上尉
  • 技术积分: 265
  • 用 户 组: 普通用户
  • 注册时间: 2008-09-21 18:15
文章分类

全部博文(15)

文章存档

2009年(8)

2008年(7)

我的朋友

分类: LINUX

2008-10-05 12:27:33

/*
注意:在linux中是不分线程跟进程的,多线程程序的实现是依靠一组轻量级进程,
线程组就由一组轻量级线程组成的。
每个进程都有一个不同的PID。但是POSIX标准规定了一个多线程应用程序中的所有线程必须有
相同的pid,所以linux中引入了tgid字段,用来表示一组线程(一组轻量级进程)的id号,对于
领头线程(第一个轻量级进程)其tgid与pid相同,而其他的线程不同,系统调用getpid是
返回的tgid,而不是pid。
无论fork,clone,vfork,最终都是调用do_fork进行创建进程的
 */
long do_fork(unsigned long clone_flags,  /*clone标志*/
       unsigned long stack_start,   /*用户栈地址,如果为0则与父进程公用*/  
       struct pt_regs *regs,    /*从用户态切换到内核态时被保存到内核栈的寄存器的值*/
       unsigned long stack_size,   /*未使用,总是0*/
       int __user *parent_tidptr,   /*父进程的用户态变量地址,该父进程具有与新的轻量级进程相同的pid。只有在CLONE_PARENT_SETTID标志被设置时才有意义*/
       int __user *child_tidptr)   /*新轻量级进程的用户态变量地址,该进程具有与这一类轻量级进程相同的pid。只有在CLONE_CHILD_SETTID标志被设置时才有意义*/
{
 
 struct task_struct *p;
 int trace = 0;
 //分配一个新的pid
 struct pid *pid = alloc_pid();
 long nr;
 if (!pid)
  return -EAGAIN;
 nr = pid->nr;
 //是否被跟踪
 if (unlikely(current->ptrace)) {
  trace = fork_traceflag (clone_flags);
  if (trace)
   clone_flags |= CLONE_PTRACE;
 }
 //复制父进程的资源,这是创建过程的关键步骤
 p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, nr);
 /*
  * Do this prior waking up the new thread - the thread pointer
  * might get invalid after that point, if the thread exits quickly.
  */
 if (!IS_ERR(p)) {
  struct completion vfork;
  //如果带有CLONE_VFORK标志.赋值并初始化vfork_done
  if (clone_flags & CLONE_VFORK) {
   p->vfork_done = &vfork;
   init_completion(&vfork);
  }
  //如果进子进程被跟踪,或者子进程初始化成STOP状态
          //则发送SIGSTOP信号.由于子进程现在还没有运行,信号不能被处理
          //所以设置TIF_SIGPENDING标志
  if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) {
   /*
    * We'll start up with an immediate SIGSTOP.
    */
   sigaddset(&p->pending.signal, SIGSTOP);
   set_tsk_thread_flag(p, TIF_SIGPENDING);
  }
  //如果子进程末定义CLONE_STOPPED标志,将其置为RUNNING.等待下一次调度
          //否则将子进程状态更改为TASK_STOPPED
  if (!(clone_flags & CLONE_STOPPED))
   wake_up_new_task(p, clone_flags);
  else
   //如果CLONE_STOPPED被设置,则吧子进程置为TASK_STOPPED
   p->state = TASK_STOPPED;
  if (unlikely (trace)) {
   current->ptrace_message = nr;
   ptrace_notify ((trace << 8) | SIGTRAP);
  }
  //如果设置了CLONE_VFORK,则将父进程插入等待对列,并挂起父进程直到子进程释放自己的内存地址空间。
  //也就是说直到子进程结束或者执行新的程序
  if (clone_flags & CLONE_VFORK) {
   wait_for_completion(&vfork);
   if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE)) {
    current->ptrace_message = nr;
    ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
   }
  }
 } else {
  free_pid(pid);
  nr = PTR_ERR(p);
 }
 return nr;
}
copy_process是fork的核心函数,他创建进程描述符以及子进程执行所需要的所有其他数据结构,他的参数与do_fork相同,增加了子进程的pid。
static struct task_struct *copy_process(unsigned long clone_flags,
     unsigned long stack_start,
     struct pt_regs *regs,
     unsigned long stack_size,
     int __user *parent_tidptr,
     int __user *child_tidptr,
     int pid /*子进程的pid*/   )
{
 int retval;
 struct task_struct *p = NULL;
 
 //对clone_flags进行检查
 //不能同时定义CLONE_NEWNS,CLONE_FS
 if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
  return ERR_PTR(-EINVAL);
 //如果定义CLONE_THREAD,则必须要定义CLONE_SIGHAND
 if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
  return ERR_PTR(-EINVAL);
 //如果定义CLONE_SIGHAND,则必须要定义CLONE_VM
 if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
  return ERR_PTR(-EINVAL);
 //进行安全检查
 retval = security_task_create(clone_flags);
 if (retval)
  goto fork_out;
 retval = -ENOMEM;
 //为子进程分配一个描述符
 p = dup_task_struct(current);
 if (!p)
  goto fork_out;
 rt_mutex_init_task(p);
#ifdef CONFIG_TRACE_IRQFLAGS
 DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled);
 DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
#endif
 retval = -EAGAIN;
 if (atomic_read(&p->user->processes) >=
   p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
   //如果用户的进程总数超过了限制
  if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
    p->user != &root_user)
   goto bad_fork_free;
 }
 //用户拥有的进程数量加1
 atomic_inc(&p->user->__count);
 atomic_inc(&p->user->processes);
 get_group_info(p->group_info);
 /*
  * If multiple threads are within copy_process(), then this check
  * triggers too late. This doesn't hurt, the check is only there
  * to stop root fork bombs.
  */
 //检查当前进程数量是否超过最大值
 if (nr_threads >= max_threads)
  goto bad_fork_cleanup_count;
 //加载进程的相关执行模块
 if (!try_module_get(task_thread_info(p)->exec_domain->module))
  goto bad_fork_cleanup_count;
 if (p->binfmt && !try_module_get(p->binfmt->module))
  goto bad_fork_cleanup_put_domain;
 p->did_exec = 0;
 //一些关键字段的初始化
 delayacct_tsk_init(p); /* Must remain after dup_task_struct() */
 //更新子进程p->flags字段
 copy_flags(clone_flags, p);
 //子进程的pid
 p->pid = pid;
 retval = -EFAULT;
 //CLONE_PARENT_SETTID如果设置,则拷贝pid到arent_tidptr
 if (clone_flags & CLONE_PARENT_SETTID)
  if (put_user(p->pid, parent_tidptr))
   goto bad_fork_cleanup_delays_binfmt;
 //初始化子进程描述符的队列、自旋锁、信号、定时器、时间统计表
 INIT_LIST_HEAD(&p->children);
 INIT_LIST_HEAD(&p->sibling);
 p->vfork_done = NULL;
 spin_lock_init(&p->alloc_lock);
 //父进程的TIF_SIGPENDING被复制进了子进程,这个标志表示有末处理的信号
     //这个标志子进程是不需要的
 clear_tsk_thread_flag(p, TIF_SIGPENDING);
 init_sigpending(&p->pending);
 p->utime = cputime_zero;
 p->stime = cputime_zero;
  p->sched_time = 0;
#ifdef CONFIG_TASK_XACCT
 p->rchar = 0;  /* I/O counter: bytes read */
 p->wchar = 0;  /* I/O counter: bytes written */
 p->syscr = 0;  /* I/O counter: read syscalls */
 p->syscw = 0;  /* I/O counter: write syscalls */
#endif
 task_io_accounting_init(p);
 acct_clear_integrals(p);
  p->it_virt_expires = cputime_zero;
 p->it_prof_expires = cputime_zero;
  p->it_sched_expires = 0;
  INIT_LIST_HEAD(&p->cpu_timers[0]);
  INIT_LIST_HEAD(&p->cpu_timers[1]);
  INIT_LIST_HEAD(&p->cpu_timers[2]);
 p->lock_depth = -1;  /* -1 = no lock */
 do_posix_clock_monotonic_gettime(&p->start_time);
 p->security = NULL;
 p->io_context = NULL;
 p->io_wait = NULL;
 p->audit_context = NULL;
 cpuset_fork(p);
#ifdef CONFIG_NUMA
  p->mempolicy = mpol_copy(p->mempolicy);
  if (IS_ERR(p->mempolicy)) {
   retval = PTR_ERR(p->mempolicy);
   p->mempolicy = NULL;
   goto bad_fork_cleanup_cpuset;
  }
 mpol_fix_fork_child_flag(p);
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
 p->irq_events = 0;
#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
 p->hardirqs_enabled = 1;
#else
 p->hardirqs_enabled = 0;
#endif
 p->hardirq_enable_ip = 0;
 p->hardirq_enable_event = 0;
 p->hardirq_disable_ip = _THIS_IP_;
 p->hardirq_disable_event = 0;
 p->softirqs_enabled = 1;
 p->softirq_enable_ip = _THIS_IP_;
 p->softirq_enable_event = 0;
 p->softirq_disable_ip = 0;
 p->softirq_disable_event = 0;
 p->hardirq_context = 0;
 p->softirq_context = 0;
#endif
#ifdef CONFIG_LOCKDEP
 p->lockdep_depth = 0; /* no locks held yet */
 p->curr_chain_key = 0;
 p->lockdep_recursion = 0;
#endif
#ifdef CONFIG_DEBUG_MUTEXES
 p->blocked_on = NULL; /* not blocked yet */
#endif
 //tgid = pid
 p->tgid = p->pid;
 if (clone_flags & CLONE_THREAD)
  //子进程插入父进程所在的线程组
  p->tgid = current->tgid;
 if ((retval = security_task_alloc(p)))
  goto bad_fork_cleanup_policy;
 if ((retval = audit_alloc(p)))
  goto bad_fork_cleanup_security;
 /* copy all the process information */
 //创建新的数据结构,并将父进程相应数据结构的值复制到新的数据结构中
 if ((retval = copy_semundo(clone_flags, p)))
  goto bad_fork_cleanup_audit;
 if ((retval = copy_files(clone_flags, p)))
  goto bad_fork_cleanup_semundo;
 if ((retval = copy_fs(clone_flags, p)))
  goto bad_fork_cleanup_files;
 if ((retval = copy_sighand(clone_flags, p)))
  goto bad_fork_cleanup_fs;
 if ((retval = copy_signal(clone_flags, p)))
  goto bad_fork_cleanup_sighand;
 if ((retval = copy_mm(clone_flags, p)))
  goto bad_fork_cleanup_signal;
 if ((retval = copy_keys(clone_flags, p)))
  goto bad_fork_cleanup_mm;
 if ((retval = copy_namespaces(clone_flags, p)))
  goto bad_fork_cleanup_keys;
 //用regs初始化子进程的内核栈,但是regs->eax的值设置成0,这就是为什么子进程返回0的原因
 retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
 if (retval)
  goto bad_fork_cleanup_namespaces;
 p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
 /*
  * Clear TID on mm_release()?
  */
 p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL;
 p->robust_list = NULL;
#ifdef CONFIG_COMPAT
 p->compat_robust_list = NULL;
#endif
 INIT_LIST_HEAD(&p->pi_state_list);
 p->pi_state_cache = NULL;
 /*
  * sigaltstack should be cleared when sharing the same VM
  */
 if ((clone_flags & (CLONE_VM|CLONE_VFORK)) == CLONE_VM)
  p->sas_ss_sp = p->sas_ss_size = 0;
 /*
  * Syscall tracing should be turned off in the child regardless
  * of CLONE_PTRACE.
  */
  //清除TIF_SYSCALL_TRACE的值,以使ret_from_ret不会吧系统调用结束的消息通知给调试进程,因为对子进程的跟踪是有tsk->ptrace中的PTRACE_SYSCALL标志来控制的
 clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
#ifdef TIF_SYSCALL_EMU
 clear_tsk_thread_flag(p, TIF_SYSCALL_EMU);
#endif
 /* Our parent execution domain becomes current domain
    These must match for thread signalling to apply */
 p->parent_exec_id = p->self_exec_id;
 /* ok, now we should be set up.. */
 //用clone)flags参数低位的信号数字编码初始化tsk->exit_signal,如果CLONE_THREAD标志被职位,
 //就把tsk->exit_signal字段初始化为-1.只有当线程组的最后一个成员死亡是,才会产生一个信号通知线程组领头线程的父进程。
 p->exit_signal = (clone_flags & CLONE_THREAD) ? -1 : (clone_flags & CSIGNAL);
 p->pdeath_signal = 0;
 p->exit_state = 0;
 /*
  * Ok, make it visible to the rest of the system.
  * We dont wake it up yet.
  */
 p->group_leader = p;
 INIT_LIST_HEAD(&p->thread_group);
 INIT_LIST_HEAD(&p->ptrace_children);
 INIT_LIST_HEAD(&p->ptrace_list);
 /* Perform scheduler related setup. Assign this task to a CPU. */
 //初始化对新进程调度程序数据结构
 sched_fork(p, clone_flags);
 /* Need tasklist lock for parent etc handling! */
 write_lock_irq(&tasklist_lock);
 /* for sys_ioprio_set(IOPRIO_WHO_PGRP) */
 p->ioprio = current->ioprio;
 /*
  * The task hasn't been attached yet, so its cpus_allowed mask will
  * not be changed, nor will its assigned CPU.
  *
  * The cpus_allowed mask of the parent may have changed after it was
  * copied first time - so re-copy it here, then check the child's CPU
  * to ensure it is on a valid CPU (and if not, just force it back to
  * parent's CPU). This avoids alot of nasty races.
  */
 p->cpus_allowed = current->cpus_allowed;
 if (unlikely(!cpu_isset(task_cpu(p), p->cpus_allowed) ||
   !cpu_online(task_cpu(p))))
  set_task_cpu(p, smp_processor_id());
 /* CLONE_PARENT re-uses the old parent */
 if (clone_flags & (CLONE_PARENT|CLONE_THREAD))
  p->real_parent = current->real_parent;
 else
  p->real_parent = current;
 p->parent = p->real_parent;
 spin_lock(¤t->sighand->siglock);
 /*
  * Process group and session signals need to be delivered to just the
  * parent before the fork or both the parent and the child after the
  * fork. Restart if a signal comes in before we add the new process to
  * it's process group.
  * A fatal signal pending means that current will exit, so the new
  * thread can't slip out of an OOM kill (or normal SIGKILL).
   */
  recalc_sigpending();
 if (signal_pending(current)) {
  spin_unlock(¤t->sighand->siglock);
  write_unlock_irq(&tasklist_lock);
  retval = -ERESTARTNOINTR;
  goto bad_fork_cleanup_namespaces;
 }
 if (clone_flags & CLONE_THREAD) {
  p->group_leader = current->group_leader;
  list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group);
  if (!cputime_eq(current->signal->it_virt_expires,
    cputime_zero) ||
      !cputime_eq(current->signal->it_prof_expires,
    cputime_zero) ||
      current->signal->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY ||
      !list_empty(¤t->signal->cpu_timers[0]) ||
      !list_empty(¤t->signal->cpu_timers[1]) ||
      !list_empty(¤t->signal->cpu_timers[2])) {
   /*
    * Have child wake up on its first tick to check
    * for process CPU timers.
    */
   p->it_prof_expires = jiffies_to_cputime(1);
  }
 }
 if (likely(p->pid)) {//将pid插入hash表
  add_parent(p);
  if (unlikely(p->ptrace & PT_PTRACED))
   __ptrace_link(p, current->parent);
  
  if (thread_group_leader(p)) {
   //如果此进程是领头线程
   p->signal->tty = current->signal->tty;
   p->signal->pgrp = process_group(current);
   set_signal_session(p->signal, process_session(current));
   attach_pid(p, PIDTYPE_PGID, process_group(p));
   attach_pid(p, PIDTYPE_SID, process_session(p));
   list_add_tail_rcu(&p->tasks, &init_task.tasks);
   __get_cpu_var(process_counts)++;
  }
  attach_pid(p, PIDTYPE_PID, p->pid);
  nr_threads++;
 }
 //总进程数加1
 total_forks++;
 spin_unlock(¤t->sighand->siglock);
 write_unlock_irq(&tasklist_lock);
 proc_fork_connector(p);
 return p;
}
 
 
函数dup_task_struct分配进程描述符
static struct task_struct *dup_task_struct(struct task_struct *orig)
{
 struct task_struct *tsk;
 struct thread_info *ti;
 prepare_to_copy(orig);
 //分配一个进程描述符
 tsk = alloc_task_struct();
 if (!tsk)
  return NULL;
 //分配一块空闲内存区,用来存放新进程的thread_info结构和内核栈,大小一般为8k
 ti = alloc_thread_info(tsk);
 if (!ti) {
  free_task_struct(tsk);
  return NULL;
 }
 //复制task信息
 *tsk = *orig;
 //使task->stack指向thread_info
 tsk->thread_info = ti;
 //复制父进程的thread_info信息
 setup_thread_stack(tsk, orig);
#ifdef CONFIG_CC_STACKPROTECTOR
 tsk->stack_canary = get_random_int();
#endif
 /* One for us, one for whoever does the "release_task()" (usually parent) */
 //新进程描述符的是哦那个计数器为2,用来表示进程描述符正在被使用而且其相应的进程处于活动状态
 atomic_set(&tsk->usage,2);
 atomic_set(&tsk->fs_excl, 0);
#ifdef CONFIG_BLK_DEV_IO_TRACE
 tsk->btrace_seq = 0;
#endif
 tsk->splice_pipe = NULL;
 return tsk;
}
 
至此,新进程的创建基本完成了,由于regs结构的eax的值不同,父子进程而返回值不同,这就是为什么子进程返回0,而父进程返回子进程号了。
 
 
阅读(1594) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~