免费注册 查看新帖 |

Chinaunix

广告
  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 937 | 回复: 0
打印 上一主题 下一主题

内核源码分析--进程的创建 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-10-05 12:27 |只看该作者 |倒序浏览
/*
注意:在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 ptrace & PT_TRACE_VFORK_DONE)) {
    current->ptrace_message = nr;
    ptrace_notify ((PTRACE_EVENT_VFORK_DONE
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(&current->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(&current->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(&current->signal->cpu_timers[0]) ||
      !list_empty(&current->signal->cpu_timers[1]) ||
      !list_empty(&current->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(&current->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,而父进程返回子进程号了。




本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/79926/showart_1272739.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP