Linux内核分析:实验六

分析linux内核创建一个新进程的过程

原创作品转载请注明出处《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

1.  进程的创建过程

在linux系统中,使i用 fork,vfork 和 clone 都可以创建新的进程,在表面看起来它们是不一样的,但实际上它们都调用do_fork函数来实现这个功能;但仔细分析来它们还是有一点点小区别的:(from:http://blog.csdn.net/abclixu123/article/details/8288292)

 

vfork和fork创建子进程的区别:
    fork: 子进程拷贝父进程的数据段
    vfork: 子进程与父进程共享数据段
<span style="font-family:sans-serif;">   </span> fork: 父、子进程的执行次序不确定
    vfork: 子进程先运行,父进程后运行

fork与exec函数族的区别就在于:fork创建一个新的进程,产生一个新的PID,exec启动一个新程序,替换原有的进程,因此进程的PID不会改变。

 

下面我们就详细看看这三个函数的调用方法:

 

 

 

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);
}

 

从用户态的代码看fork();函数返回了两次,即在父子进程中各返回一次,父进程从系统调用中返回比较容易理解,子进程从系统调用中返回,那它在系统调用处理过程中的哪里开始执行的呢?这就涉及子进程的内核堆栈数据状态和task_struct中thread记录的sp和ip的一致性问题,这是 在哪里设定的?copy_thread in copy_process。

*childregs = *current_pt_regs(); //复制内核堆栈
childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因!  
p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶
p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址

 

二、分析fork函数对应的内核处理过程sys_clone,理解创建一个新进程如何创建和修改task_struct数据结构

 

fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建,函数在/linux-3.18.6/kernel/fork.c中,代码如下:

1618 *  Ok, this is the main fork-routine.
1619 *
1620 * It copies the process, and if successful kick-starts
1621 * it and waits for it to finish using the VM if required.
1622 */
1623long do_fork(unsigned long clone_flags,
1624          unsigned long stack_start,
1625          unsigned long stack_size,
1626          int __user *parent_tidptr,
1627          int __user *child_tidptr)
1628{
1629    struct task_struct *p;
1630    int trace = 0;
1631    long nr;
1632
1633    /*
1634     * Determine whether and which event to report to ptracer.  When
1635     * called from kernel_thread or CLONE_UNTRACED is explicitly
1636     * requested, no event is reported; otherwise, report if the event
1637     * for the type of forking is enabled.
1638     */
1639    if (!(clone_flags & CLONE_UNTRACED)) {
1640        if (clone_flags & CLONE_VFORK)
1641            trace = PTRACE_EVENT_VFORK;
1642        else if ((clone_flags & CSIGNAL) != SIGCHLD)
1643            trace = PTRACE_EVENT_CLONE;
1644        else
1645            trace = PTRACE_EVENT_FORK;
1646
1647        if (likely(!ptrace_event_enabled(current, trace)))
1648            trace = 0;
1649    }
1650
1651    p = copy_process(clone_flags, stack_start, stack_size,
1652             child_tidptr, NULL, trace);
1653    /*
1654     * Do this prior waking up the new thread - the thread pointer
1655     * might get invalid after that point, if the thread exits quickly.
1656     */
1657    if (!IS_ERR(p)) {
1658        struct completion vfork;
1659        struct pid *pid;
1660
1661        trace_sched_process_fork(current, p);
1662
1663        pid = get_task_pid(p, PIDTYPE_PID);
1664        nr = pid_vnr(pid);
1665
1666        if (clone_flags & CLONE_PARENT_SETTID)
1667            put_user(nr, parent_tidptr);
1668
1669        if (clone_flags & CLONE_VFORK) {
1670            p->vfork_done = &vfork;
1671            init_completion(&vfork);
1672            get_task_struct(p);
1673        }
1674
1675        wake_up_new_task(p);
1676
1677        /* forking complete and child started to run, tell ptracer */
1678        if (unlikely(trace))
1679            ptrace_event_pid(trace, pid);
1680
1681        if (clone_flags & CLONE_VFORK) {
1682            if (!wait_for_vfork_done(p, &vfork))
1683                ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);
1684        }
1685
1686        put_pid(pid);
1687    } else {
1688        nr = PTR_ERR(p);
1689    }
1690    return nr;
1691}

上段代码先定义一个task_sturct结构指针,再用copy_process函数复制一个进程,故而真正的复制过程在copy_process函数中,该函数主要执行内容为:

struct task_struct *p;
retval = security_task_create(clone_flags);

p = dup_task_struct(current);
retval = sched_fork(clone_flags, p);
retval = copy_files(clone_flags, p);
retval = copy_fs(clone_flags, p);
retval = copy_sighand(clone_flags, p);
retval = copy_signal(clone_flags, p);
retval = copy_mm(clone_flags, p);
retval = copy_io(clone_flags, p);
retval = copy_thread(clone_flags, stack_start, stack_size, p);

/linux-3.18.6/arch/x86/kernel/process_32.c的copy_thread函数中

 
p->thread.sp = (unsigned long) childregs; p->thread.ip = (unsigned long) ret_from_fork; childregs->ax = 0; fork子进程返回为0 

决定了子进程从系统调用中返回后的执行.ret_from_fork决定了新进程的第一条指令地址。p->thread.ip = (unsigned long) ret_from_fork;将子进程的ip设置为ret_from_fork的首地址,子进程从ret_from_fork开始。

 

三. 使用gdb调试:

在调试前,我们可以看到如下信息


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值