“Linux内核分析”实验报告(六)分析Linux内核创建一个新进程的过程


窦猛汉 sa15226344 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000


1.进程简介:

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

进程控制块(PCB)是系统为了管理进程设置的一个专门的数据结构。系统用它来记录进程的外部特征,描述进程的运动变化过程。同时,系统可以利用PCB来控制和管理进程,所以说,PCB(进程控制块)是系统感知进程存在的唯一标志。

2.进程创建

我们可以通过fork,vfork,clone来创建进程,他们需要调用对应的系统调用函数。

SYSCALL_DEFINE0(fork)
{
     return do_fork(SIGCHLD, 0, 0, NULL, NULL);
}

SYSCALL_DEFINE0(vfork)
{
     return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0,0, NULL, NULL);
}

SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, 
                int __user *, parent_tidptr, int __user *, child_tidptr, int, tls_val)
{  
    return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr);
}

 我们可以看到每一个创建进程的函数,都要调用do_fork

long do_fork(unsigned long clone_flags,unsigned long stack_start, unsigned long stack_size, 
                        int __user *parent_tidptr,int __user *child_tidptr)
{
    struct task_struct *p;
    int trace = 0;
    long nr;
    
    if (!(clone_flags & CLONE_UNTRACED)) 
    {
        if (clone_flags & CLONE_VFORK)
            trace = PTRACE_EVENT_VFORK;
        else if ((clone_flags & CSIGNAL) != SIGCHLD)
            trace = PTRACE_EVENT_CLONE;
        else
            trace = PTRACE_EVENT_FORK;
            	
        if (likely(!ptrace_event_enabled(current, trace)))
             trace = 0;	
      }
       //复制进程进程资源,返回task_struct 的指针
      p = copy_process(clone_flags, stack_start, stack_size,child_tidptr, NULL, trace);
      if (!IS_ERR(p)) 
      {
          struct completion vfork;
          struct pid *pid;
          trace_sched_process_fork(current, p);
          
          //获得 pid
          pid = get_task_pid(p, PIDTYPE_PID);
          nr = pid_vnr(pid);
          if (clone_flags & CLONE_PARENT_SETTID)
              put_user(nr, parent_tidptr);
              
           //如果是vfork 則执行
          if (clone_flags & CLONE_VFORK) 
          {
              p->vfork_done = &vfork;
              init_completion(&vfork);
              get_task_struct(p);
          }
         
         //把进程添加到调度器队列,等待调度器调度
         wake_up_new_task(p);
         
         if (unlikely(trace))
             ptrace_event_pid(trace, pid);
         if (clone_flags & CLONE_VFORK) 
         {
             if (!wait_for_vfork_done(p, &vfork))
                 ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);	
         }
         put_pid(pid);
     }
      else 
      {
          nr = PTR_ERR(p);
      }
      
      return nr;
 }

我们可以进一步查看copy_process源码

static struct task_struct *copy_process(unsigned long clone_flags,unsigned long stack_start,unsigned long stack_size,
				                                       int __user *child_tidptr,struct pid *pid,int trace)
{
      int retval;
      struct task_struct *p;
   
     /*。。。。。。。。。。。。。。。。省略一些代码*/
      /*分配一个新的 task_struct,复制current的task_struct*/
       p = dup_task_struct(current);
       /*。。。。。。。。。。。。。。。。。。。。。。。*/
       /*完成对新进程调度程序数据结构的初始化,并把新进程的状态设置为TASK_RUNNING*/
       retval = sched_fork(clone_flags, p);
       if (retval)
           goto bad_fork_cleanup_policy;
       /*。。。。。。。。。。。。。。。。省略的代码是对*p的初始化*/
       /* 设置子进程的内核栈*/
       retval = copy_thread(clone_flags, stack_start, stack_size, p);
       if (retval)
            goto bad_fork_cleanup_io;
       
       if (pid != &init_struct_pid)
        {
            retval = -ENOMEM;
            /*为新进程分配新的 pid 号*/
            pid = alloc_pid(p->nsproxy->pid_ns_for_children);
            if (!pid)
                goto bad_fork_cleanup_io;
       	}
       	/*。。。。。。。。。。。。。。。。继续对*p初始化*/
        /*设置新进程的pid*/
         p->pid = pid_nr(pid);
       /*。。。。。。。。。。。。。。。。。。。。。。。。*/
         if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) 
         {
    
             p->real_parent = current->real_parent;
             p->parent_exec_id = current->parent_exec_id;
         }
         else 
         {
                 /*如果是创建线程,当前进程是子进程的parent*/
             p->real_parent = current;
             p->parent_exec_id = current->self_exec_id;
          }
      
        /*。。。。。。。。。。。。。。。。。。。。*/
        return ERR_PTR(retval);
    }

可以看出在copy_process中 主要调用 dup_tark_struct,sched_fork,copy_thread,alloc_pid,pid_nr等

以下是copy_thread代码

int copy_thread(unsigned long clone_flags, unsigned long sp,unsigned long arg, struct task_struct *p)
{
    struct pt_regs *childregs = task_pt_regs(p);
    struct task_struct *tsk;
    int err;
    p->thread.sp = (unsigned long) childregs;
    p->thread.sp0 = (unsigned long) (childregs+1);
    memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
    if (unlikely(p->flags & PF_KTHREAD)) 
    {
        memset(childregs, 0, sizeof(struct pt_regs));
        p->thread.ip = (unsigned long) ret_from_kernel_thread;
        task_user_gs(p) = __KERNEL_STACK_CANARY;
        childregs->ds = __USER_DS;
        childregs->es = __USER_DS;
        childregs->fs = __KERNEL_PERCPU;
        childregs->bx = sp;
        childregs->bp = arg;
        childregs->orig_ax = -1;
        childregs->cs = __KERNEL_CS | get_kernel_rpl();
        childregs->flags = X86_EFLAGS_IF | X86_EFLAGS_FIXED;
        p->thread.io_bitmap_ptr = NULL;
        return 0;
    }
    /*将当前进程的寄存器信息复制给子进程*/
   *childregs = *current_pt_regs();
   /*设置eax 及设置返回值*/
   childregs->ax = 0;
   if (sp)
       childregs->sp = sp;
   /*设置子进程从ret_from_fork开始执行及设置eip*/
   p->thread.ip = (unsigned long) ret_from_fork;
   task_user_gs(p) = get_user_gs(current_pt_regs());
   p->thread.io_bitmap_ptr = NULL;
   tsk = current;
   err = -ENOMEM;
   if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP)))
   {
   	p->thread.io_bitmap_ptr = kmemdup(tsk->thread.io_bitmap_ptr,IO_BITMAP_BYTES, GFP_KERNEL);
   	if (!p->thread.io_bitmap_ptr)
   	 {
   	     p->thread.io_bitmap_max = 0;
   	     return -ENOMEM;
   	 }
   	set_tsk_thread_flag(p, TIF_IO_BITMAP);
   }
   err = 0;
   if (clone_flags & CLONE_SETTLS)
       err = do_set_thread_area(p, -1,(struct user_desc __user *)childregs->si, 0);
   if (err && p->thread.io_bitmap_ptr)
    {
    	kfree(p->thread.io_bitmap_ptr);
    	p->thread.io_bitmap_max = 0;193
    }
    return err;
}

3.实验图片

190707_BF29_2649303.png

190708_2woi_2649303.png

190708_6vte_2649303.png


4.实验总结

在执行fork函数后,调用copy_process,用以创建进程描述符以及子进程所需要的数据结构,在copy_process中调用dup_task_struct,用以分配一个新的 task_struct,以及复制父进程的task_struct,在dup_task_struct,后 调用copy_thread,用于初始化子进程的内核栈,在copy_thread中会设置子进程的eip。在执行完 copy_thread后回到do_fork函数执行wake_up_new_task把进程添加到调度器队列,等待调度器调度,这样调度器在调用子进程时会自动执行ret_from_fork,真正执行子进程

转载于:https://my.oschina.net/doumenghan/blog/652028

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值