Linux内核 do_fork 函数源代码浅析

来源:互联网 发布:photoshop cs6破解mac 编辑:程序博客网 时间:2024/05/21 11:13

      在 Linux 内核中,供用户创建进程的系统调用fork()函数的响应函数是 sys_fork()、sys_clone()、sys_vfork()。这三个函数都是通过调用内核函数 do_fork() 来实现的。根据调用时所使用的 clone_flags 参数不同,do_fork() 函数完成的工作也各异。

      这部分内容简单,我不打算就此而展开分析。下面我们重点来讲解以下 do_fork() 函数的工作原理。

      我们知道 do_fork() 函数生成一个新的进程,大致分为三个步骤。

      1)建立进程控制结构并赋初值,使其成为进程映像。这个过程完成以下内容。
        在内存中分配一个 task_struct 数据结构,以代表即将产生的新进程。把父进程 PCB 的内容复制到新进程的 PCB 中。为新进程分配一个唯一的进程标识号 PID 和 user_struct 结构。然后检查用户具有执行一个新进程所必须具有的资源。重新设置 task_struct 结构中那些与父进程值不同的数据成员。设置进程管理信息,根据所提供的 clone_flags 参数值,决定是否对父进程 task_struct 中的指针 fs 、files 指针等所选择的部分进行拷贝,如果 clone_flags 参数指明的是
共享而不是拷贝,则将其计数器 count 的值加 1 ,否则就拷贝新进程所需要的相关信息内容 PCB 。这个地方是区分 sys_fork() 还是 sys_clone() 。

     2) 必须为新进程的执行设置跟踪进程执行情况的相关内核数据结构。包括 任务数组、自由时间列表 tarray_freelist 以及 pidhash[] 数组。这部分完成如下内容:

       (1) 把新进程加入到进程链表中 
       (2) 把新进程加入到 pidhash 散列表中,并增加任务计数值。
       (3) 通过拷贝父进程的上、下文来初始化硬件的上下文(TSS段、LDT以及 GDT)。


     3) 启动调度程序,使子进程获得运行的机会。
         这部分完成以下动作:

        (1) 设置新的就绪队列状态 TASK_RUNING , 并将新进程挂到就绪队列中,并重新启动调度程序使其运行。
        (2) 向父进程返回子进程的 PID,设置子进程从 do_fork() 返回 0 值。
       

        (3) 下面就具体的 do_fork() 函数程序代码进行分析(该代码位于 kernel/fork.c 文件中)

/* *  Ok, this is the main fork-routine. * * It copies the process, and if successful kick-starts * it and waits for it to finish using the VM if required. */long do_fork(unsigned long clone_flags,      unsigned long stack_start,      struct pt_regs *regs,      unsigned long stack_size,      int __user *parent_tidptr,      int __user *child_tidptr){struct task_struct *p;int trace = 0;long nr;/* * Do some preliminary argument and permissions checking before we * actually start allocating stuff */if (clone_flags & CLONE_NEWUSER) {if (clone_flags & CLONE_THREAD)return -EINVAL;/* hopefully this check will go away when userns support is * complete */if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SETUID) ||!capable(CAP_SETGID))return -EPERM;}/* * When called from kernel_thread, don't do user tracing stuff. */if (likely(user_mode(regs)))trace = tracehook_prepare_clone(clone_flags);p = copy_process(clone_flags, stack_start, regs, stack_size, child_tidptr, NULL, trace);/* * 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;trace_sched_process_fork(current, p);nr = task_pid_vnr(p);if (clone_flags & CLONE_PARENT_SETTID)put_user(nr, parent_tidptr);if (clone_flags & CLONE_VFORK) {p->vfork_done = &vfork;init_completion(&vfork);}audit_finish_fork(p);tracehook_report_clone(regs, clone_flags, nr, p);/* * We set PF_STARTING at creation in case tracing wants to * use this to distinguish a fully live task from one that * hasn't gotten to tracehook_report_clone() yet.  Now we * clear it and set the child going. */p->flags &= ~PF_STARTING;wake_up_new_task(p);tracehook_report_clone_complete(trace, regs,clone_flags, nr, p);if (clone_flags & CLONE_VFORK) {freezer_do_not_count();wait_for_completion(&vfork);freezer_count();tracehook_report_vfork_done(p, nr);}} else {nr = PTR_ERR(p);}return nr;}


 

 

 

 

原创粉丝点击