调用vfork后的奇怪输出 #包括 #包括 #包括 #包括 int createproc(); pid_t pid; int main() { createproc(); printf(“%d\n”,pid); 退出(0);//_退出(0)给出相同的结果 } int createproc() { 如果(!(pid=vWork()){ printf(“子进程:%d\n”,pid); } 其他的 printf(“父进程:%d\n”,pid); }

调用vfork后的奇怪输出 #包括 #包括 #包括 #包括 int createproc(); pid_t pid; int main() { createproc(); printf(“%d\n”,pid); 退出(0);//_退出(0)给出相同的结果 } int createproc() { 如果(!(pid=vWork()){ printf(“子进程:%d\n”,pid); } 其他的 printf(“父进程:%d\n”,pid); },c,linux,disassembly,vfork,C,Linux,Disassembly,Vfork,程序的输出如下: 子进程:0 0 父进程:6958 子进程:0 分段故障 我知道,vWork将挂起父进程,除非调用exec或exit函数并共享堆栈段。 所以我有两个问题: 由于它们共享一个公共地址空间,出口(0)是否会影响这两个进程?如果是,怎么做?若否,原因为何 为什么在“父进程:6958”之后有一行“子进程:0”?我不希望得到像意外行为这样的回答 此外,通过反汇编,我注意到vfork的调用没有正常的功能。没有堆栈平衡: 函数vfork的汇编程序代码转储: #include <sys/t

程序的输出如下:

子进程:0

0

父进程:6958

子进程:0

分段故障

我知道,vWork将挂起父进程,除非调用exec或exit函数并共享堆栈段。 所以我有两个问题:

  • 由于它们共享一个公共地址空间,出口(0)是否会影响这两个进程?如果是,怎么做?若否,原因为何

  • 为什么在“父进程:6958”之后有一行“子进程:0”?我不希望得到像意外行为这样的回答

  • 此外,通过反汇编,我注意到vfork的调用没有正常的功能。没有堆栈平衡: 函数vfork的汇编程序代码转储:

    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int createproc();
    pid_t pid;
    int main()
    {
        createproc();
        printf("%d\n", pid);
        exit(0);//_exit(0) gives the same result
    }
    int createproc()
    {
        if(!(pid=vfork())) {
            printf("child proc:%d\n", pid);
        }
        else
            printf("parent proc:%d\n", pid);
    }
    
    0xb7ed2050:pop ecx
    =>0xb7ed2051:mov edx,DWORD PTR gs:0x6c
    0xb7ed2058:mov eax,edx
    0xb7ed205a:neg eax
    0xb7ed205c:jne 0xb7ed2063
    0xb7ed205e:mov eax,0x8000000
    0xb7ed2063:mov gs:0x6c,eax
    0xb7ed2069:mov eax,0xbe
    0xb7ed206e:int 0x80
    0xb7ed2070:推送ecx
    0xb7ed2071:测试eax,eax
    0xb7ed2073:je 0xb7ed207c
    0xb7ed2075:mov DWORD PTR gs:0x6c,edx
    0xb7ed207c:cmp eax,0xfffff001
    0xb7ed2081:jae 0xb7ed2084
    0xb7ed2083:ret
    0xb7ed2084:调用0xb7f44d87
    0xb7ed2089:添加ecx,0xedf77
    0xb7ed208f:mov ecx,DWORD PTR[ecx-0x104]
    0xb7ed2095:xor edx,edx
    0xb7ed2097:子edx,eax
    0xb7ed2099:添加ecx,DWORD PTR gs:0x0
    0xb7ed20a0:mov DWORD PTR[ecx],edx
    0xb7ed20a2:或eax,0xffffffff
    0xb7ed20a5:jmp 0xb7ed2083
    
    实际上,它将返回地址弹出到ecx中,并在系统调用后回推(0xb7ed206e:int 0x80 0xb7ed2070:push ecx)。最不寻常的是有一条ret指令:0xb7ed2083:ret


    我不熟悉汇编语言,有人能给我解释一下吗?

    我只能引用
    人的工作:

    (来自POSIX.1)vfork()函数与fork(2)具有相同的效果, 但是,如果vfork()创建的进程修改除用于存储数据的pid类型变量以外的任何数据,则该行为是未定义的 从vfork()返回值,或从调用vfork()的函数返回值,或在成功调用_exit(2)或exec(3)系列函数之一之前调用任何其他函数

    这里的重要部分是:

    如果在调用
    \u exit

    因此,请修改您的代码:

    0xb7ed2050 <+0>:        pop    ecx 
    => 0xb7ed2051 <+1>:         mov    edx,DWORD PTR gs:0x6c 
       0xb7ed2058 <+8>:     mov    eax,edx 
       0xb7ed205a <+10>:    neg    eax 
       0xb7ed205c <+12>:    jne    0xb7ed2063 <vfork+19> 
       0xb7ed205e <+14>:    mov    eax,0x80000000 
       0xb7ed2063 <+19>:    mov    gs:0x6c,eax 
       0xb7ed2069 <+25>:    mov    eax,0xbe 
       0xb7ed206e <+30>:    int    0x80 
       0xb7ed2070 <+32>:    push   ecx 
       0xb7ed2071 <+33>:    test   eax,eax 
       0xb7ed2073 <+35>:    je     0xb7ed207c <vfork+44> 
       0xb7ed2075 <+37>:    mov    DWORD PTR gs:0x6c,edx 
       0xb7ed207c <+44>:    cmp    eax,0xfffff001 
       0xb7ed2081 <+49>:    jae    0xb7ed2084 <vfork+52> 
       0xb7ed2083 <+51>:    ret    
       0xb7ed2084 <+52>:    call   0xb7f44d87 <__i686.get_pc_thunk.cx> 
       0xb7ed2089 <+57>:    add    ecx,0xedf77 
       0xb7ed208f <+63>:    mov    ecx,DWORD PTR [ecx-0x104] 
       0xb7ed2095 <+69>:    xor    edx,edx 
       0xb7ed2097 <+71>:    sub    edx,eax 
       0xb7ed2099 <+73>:    add    ecx,DWORD PTR gs:0x0 
       0xb7ed20a0 <+80>:    mov    DWORD PTR [ecx],edx 
       0xb7ed20a2 <+82>:    or     eax,0xffffffff 
       0xb7ed20a5 <+85>:    jmp    0xb7ed2083 <vfork+51> 
    
    避免未定义的行为。因此,您必须退出应用程序。(是的,6只是一个值)


    当然,如果您希望调用
    exec
    系列中的函数(即:运行另一个应用程序),也可以调用该系列中的函数。

    vWork
    之后,在子进程中只允许执行以下操作:

    • 将函数的返回值存储到变量中
    • 呼叫
      \u退出
    • 调用操作系统提供的某些函数,该函数的名称以
      exec
      开头
    绝对没有别的了。如果您打算执行其他操作(并且
    printf
    可能是您可以在
    vfork
    内部调用的最差函数之一,并且肯定被视为“其他操作”),请不要使用
    vfork
    ,而是使用
    fork

    这是因为在旧时代,
    fork
    系统调用在复制进程的整个地址空间时可能会非常慢,在大多数情况下,
    fork
    后面紧跟着
    exec*
    ,这就把地址空间扔掉了。因此
    vfork
    被发明出来,新进程不复制地址空间,而是借用父进程的地址空间并将其挂起,直到调用
    exit
    exec

    因此,您的代码首先为子进程中的printf缓冲区(但父进程的地址空间)分配内存。这可能不安全,也可能不安全,并使家长感到困惑。然后,
    createproc
    函数返回时可能会覆盖父级返回时将使用的堆栈帧,然后再次调用
    printf
    ,这一次肯定会破坏堆栈帧并进一步混淆printf的内部,然后从刷新printf缓冲区的
    main
    返回,销毁所用父级中
    main
    的堆栈框架,可能会释放printf工作所需的大量状态和stdio状态,然后退出。该出口解除父级的挂起,这可能会崩溃,因为它返回的堆栈帧已经损坏。如果它没有崩溃,并且以某种方式调用printf,printf中的内部状态就会崩溃,如果没有崩溃,则子系统已经释放了stdout描述符,这肯定会崩溃


    换句话说,如果在子级中运行的代码中唯一的东西不是
    \u exit
    exec
    ,那么您的代码就没有工作的机会,因为这不是
    vfork
    可以处理的。

    此代码仍然未定义,因为您正在调用“printf”。实际上,这是一个函数,而不是_exit或exec*@如果你有一个好的观点,我只是假设printf不会修改变量,但你永远不知道…@fritzone,printf是否修改并不重要
    int createproc()
    {
        if(!(pid=vfork())) {
            _exit(6);
        }
        else
            printf("parent proc:%d\n", pid);
     }