LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 997|回复: 0

fork()、vfork()与clone()

[复制链接]
发表于 2006-8-16 15:54:09 | 显示全部楼层 |阅读模式
fork()、vfork()及clone()


fork()、vfork()和clone()分别通过系统INT 0X80调用sys_fork()、sys_vfork()或
sys_clone(),从下面的
代码可以看出,它们都调用了do_fork(),唯一的区别是所传参数中clone_flags不同。
fork():建立一个新的process
vfork():建立一个新的process,并且父进程阻塞,直到子进程SIG或exit.(与此相对的是
copy on write技术)
clone():用于建立一个LWT()
写个简单的程序来看。

test.c
[back@Test back]$ cat test.c
//======================================================================
//      Illustrate fork() && vfork()    by jie.hong
//======================================================================
#include <stdio.h>
#include <sys/types.h>
#include <sys/unistd.h>

int main()
{
        int i = -1;
        int j = -1;
        i = vfork();
        j = fork();
        printf("\nThe PID is [%d],[%d]\n", i,j);

        return 0;
}


[back@Test back]$ gcc test.c -o test
[back@Test back]$ ./test

The PID is [0],[29304]                        //29303打印       

The PID is [29303],[29305]                //29302打印
Segmentation fault
[back@Test back]$
The PID is [29303],[0]                        //29305打印

The PID is [0],[0]                        //29304打印

初始PID为29302,vfork后其阻塞,其子进程(29303)执行fork()产生29304 ,而29302
结束阻塞
后则fork()产生29305如下图.
        vfork()                   fork()       
29302------------->29303-------------->29304
  |
  |
  |fork()
  |
  |
29305  


关键区别为clone_flag不同。

[back@Test back]$ gdb test
GNU gdb Red Hat Linux (5.3.90-0.20030710.41.2.1rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...Using host libthread_db
library "/lib/libthread_db.so.1".

(gdb) break fork
Breakpoint 1 at 0x80482b0
(gdb) break vfork
Breakpoint 2 at 0x80482a0
(gdb) r
Starting program: /home/back/test
Breakpoint 1 at 0x400df6b0
Breakpoint 2 at 0x400df6f0

Breakpoint 2, 0x400df6f0 in __vfork () from /lib/i686/libc.so.6
(gdb) disass
Dump of assembler code for function __vfork:
0x400df6f0 <__vfork+0>: pop    %ecx
0x400df6f1 <__vfork+1>: mov    $0xbe,%eax        //0xbe 及120
0x400df6f6 <__vfork+6>: int    $0x80                //INT 0X80
0x400df6f8 <__vfork+8>: cmp    $0xfffff001,%eax
0x400df6fd <__vfork+13>:        jae    0x400df701 <__vfork+17>
0x400df6ff <__vfork+15>:        jmp    *%ecx
0x400df701 <__vfork+17>:        push   %ecx
0x400df702 <__vfork+18>:        cmp    $0xffffffda,%eax
0x400df705 <__vfork+21>:        jne    0x400df71a <__vfork+42>
0x400df707 <__vfork+23>:        mov    $0x2,%eax
0x400df70c <__vfork+28>:        int    $0x80
0x400df70e <__vfork+30>:        cmp    $0xfffff001,%eax
0x400df713 <__vfork+35>:        jae    0x400df71a <__vfork+42>
0x400df715 <__vfork+37>:        ret
0x400df716 <__vfork+38>:        mov    (%esp,1),%ebx
0x400df719 <__vfork+41>:        ret
0x400df71a <__vfork+42>:        push   %ebx
0x400df71b <__vfork+43>:        call   0x400df716 <__vfork+38>
0x400df720 <__vfork+48>:        add    $0x7b934,%ebx
0x400df726 <__vfork+54>:        xor    %edx,%edx
0x400df728 <__vfork+56>:        sub    %eax,%edx
0x400df72a <__vfork+58>:        push   %edx

Breakpoint 3, 0x400df6b0 in __libc_fork () from /lib/i686/libc.so.6
(gdb) disass
Dump of assembler code for function __libc_fork:
0x400df6b0 <__libc_fork+0>:     mov    $0x2,%eax        //为2
0x400df6b5 <__libc_fork+5>:     int    $0x80                //INT 0X80
0x400df6b7 <__libc_fork+7>:     cmp    $0xfffff001,%eax
0x400df6bc <__libc_fork+12>:    jae    0x400df6c3 <__libc_fork+19>
0x400df6be <__libc_fork+14>:    ret
0x400df6bf <__libc_fork+15>:    mov    (%esp,1),%ebx
0x400df6c2 <__libc_fork+18>:    ret
0x400df6c3 <__libc_fork+19>:    push   %ebx
0x400df6c4 <__libc_fork+20>:    call   0x400df6bf <__libc_fork+15>
0x400df6c9 <__libc_fork+25>:    add    $0x7b98b,%ebx
0x400df6cf <__libc_fork+31>:    xor    %edx,%edx
0x400df6d1 <__libc_fork+33>:    sub    %eax,%edx
0x400df6d3 <__libc_fork+35>:    push   %edx
0x400df6d4 <__libc_fork+36>:    call   0x40040074
0x400df6d9 <__libc_fork+41>:    pop    %ecx
0x400df6da <__libc_fork+42>:    pop    %ebx
0x400df6db <__libc_fork+43>:    mov    %ecx,(%eax)
0x400df6dd <__libc_fork+45>:    or     $0xffffffff,%eax
0x400df6e0 <__libc_fork+48>:    jmp    0x400df6be <__libc_fork+14>


  
arch/i386/kernel/Process.c

00703:        asmlinkage int sys_fork(struct pt_regs regs)        //pt_regs为各寄存器值
00704:        {
00705:                return do_fork(SIGCHLD, regs.esp, &regs, 0, NULL, NULL);
00706:        }
00707:
00708:        asmlinkage int sys_clone(struct pt_regs regs)
00709:        {
00710:                unsigned long clone_flags;
00711:                unsigned long newsp;
00712:                int __user *parent_tidptr, *child_tidptr;
00713:       
00714:                clone_flags = regs.ebx;
00715:                newsp = regs.ecx;
00716:                parent_tidptr = (int __user *)regs.edx;
00717:                child_tidptr = (int __user *)regs.edi;
00718:                if (!newsp)
00719:                newsp = regs.esp;
00720:                return do_fork(clone_flags, newsp, &regs, 0, parent_tidptr,
child_tidptr);
00721:        }

00733:        asmlinkage int sys_vfork(struct pt_regs regs)
00734:        {
00735:                return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0,
NULL, NULL);
00736:        }
下面为do_fork()的定义:
kernel/Fork.c(01280)
/*
*  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 pid = alloc_pidmap();        //先分配一个pid       

        if (pid < 0)
                return -EAGAIN;
        //======================================================
        //        likely,unlikely为gcc预指令,用于优化程序,例如下
        //面的unlikely为表明current->ptrace不大会为非零,因此gcc
        //在编译程序时就可以优化,提高程序性能,详见gcc参考手册
        //======================================================
               
        if (unlikely(current->ptrace)) {
                trace = fork_traceflag (clone_flags);        //根据current->ptrace和clone_flags取值
                if (trace)
                        clone_flags |= CLONE_PTRACE;
        }
        //======================================================
        //        copy_process()为主要函数
        //======================================================
        p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr,
child_tidptr, pid);
        /*
         * 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;

                if (clone_flags & CLONE_VFORK) {
                        p->vfork_done = &vfork;
                        init_completion(&vfork);        //done置0,并初始化list
                }

                if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) {
                        /*
                         * We'll start up with an immediate SIGSTOP.
                         */
                         //如果处于调试,则必须马上设SIGSTOP信号
                        sigaddset(&p->pending.signal, SIGSTOP);               
                        set_tsk_thread_flag(p, TIF_SIGPENDING);//设置thread_info中flag字段
                }

                if (!(clone_flags & CLONE_STOPPED))
                        wake_up_new_task(p, clone_flags);//倘若没有设置CLONE_STOPPED
标志马上唤醒新进程
                else
                        p->state = TASK_STOPPED;        //否则将其stat置为TASK_STOPPED

                if (unlikely (trace)) {
                        current->ptrace_message = pid;                //获取些调试跟踪信息
                        ptrace_notify ((trace << 8) | SIGTRAP);
                }

                if (clone_flags & CLONE_VFORK) {
                        wait_for_completion(&vfork);//调度到了才返回
                        if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE))
                                ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
//获取些调试跟踪信息
                }
        } else {
                free_pidmap(pid); //出错处理
                pid = PTR_ERR(p);
        }
        return pid;
}
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表