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

プログラムの出力は以下のとおりです。

子プロセス:0

0

親プロセス:6958

子プロセス:0

セグメンテーション違反

私が知っているように、vfork は、exec または exit 関数が呼び出され、スタック セグメントが共有されていない限り、親プロセスを中断します。だからここに私は2つの質問があります:

  1. それらは共通のアドレス空間を共有しているため、exit(0) は両方のプロセスに影響しますか? もしそうなら、どのように?そうでない場合、なぜですか?

  2. 「parent proc:6958」の後に「child proc:0」の行があるのはなぜですか? 予期しない動作のような答えは期待していません。

また、逆アセンブルしてみると、vfork の呼び出しが正常に機能していないことに気付きました。スタック バランスはありません: 関数 vfork のアセンブラー コードのダンプ:

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> 

実際にはリターン アドレスを ecx にポップし、システム コール (0xb7ed206e <+30>: int 0x80 0xb7ed2070 <+32>: push ecx) の後にプッシュ バックします。一番珍しいのは ret 命令があること: 0xb7ed2083 <+51>: ret

アセンブル言語に詳しくないのですが、どなたか教えていただけないでしょうか?

4

2 に答える 2

3

子プロセスでできることvforkは次のとおりです。

  • 関数の戻り値を変数に格納します。
  • 電話_exit
  • で始まる名前を持つ、オペレーティング システムによって提供される関数を呼び出しますexec

絶対に他には何もありません。他のことをするつもりなら (そしてprintfおそらく内部で呼び出すことができる最悪の関数の 1 つでありvfork、間違いなく「その他」としてカウントされる)、 を使用せず、代わりvforkに使用してください。fork

この理由は、昔はforkシステム コールがプロセスのアドレス空間全体をコピーするときにかなり遅くなる可能性があり、ほとんどの場合、forkすぐにexec*そのアドレス空間を捨てたということです。そのため、アドレス空間をコピーする代わりに、新しいプロセスが親プロセスのアドレス空間を借りて、またはが呼び出されるvforkまで一時停止することが発明されました。exitexec

したがって、コードが最初に行うことは、子プロセス (ただし、親のアドレス空間) で printf バッファーのメモリ割り当てを行うことです。これは安全ではないかもしれませんし、親を混乱させるかもしれません。次に、createproc関数は、親が戻ってきたときに使用するスタック フレームを上書きして戻り、printf再度呼び出します。今回は確実にスタック フレームを破棄し、printf の内部をさらに混乱させます。次に、戻りからmainprintf バッファーをフラッシュし、破棄しますスタックフレームmain使用されている親では、おそらく printf が機能するために必要な多くの状態と stdio 状態を解放し、終了します。その exit は親のサスペンドを解除しますが、おそらくクラッシュするのは、それが返すスタック フレームが既に破壊されているためです。それが破壊されておらず、何らかの方法で printf を呼び出すことができた場合、printf の内部状態が破壊され、それが破壊されていない場合、stdout 記述子は子によって既に解放されており、確実にクラッシュします。

つまり、子で実行されるコード内の唯一のものが または でない_exit場合exec、それは処理できるものではないため、コードが機能する可能性はありませんvfork

于 2014-04-29T13:15:33.703 に答える
0

私はちょうど引用することができますman vfork

(POSIX.1 より) vfork() 関数は fork(2) と同じ効果がありますが、vfork() によって作成されたプロセスが、変数の格納に使用される pid_t 型の変数以外のデータを変更した場合の動作は未定義です。 vfork() からの戻り値、または vfork() が呼び出された関数からの戻り、または _exit(2) または関数の exec(3) ファミリの 1 つを正常に呼び出す前に、他の関数を呼び出します。

ここで重要な部分は次のとおりです。

vfork()呼び出す前に呼び出された関数から子が戻った場合の動作は未定義です。_exit

したがって、コードを変更します。

int createproc()
{
    if(!(pid=vfork())) {
        _exit(6);
    }
    else
        printf("parent proc:%d\n", pid);
 }

未定義の動作を回避します。そのため、特にアプリケーションを終了する必要があります。(はい、6 は単なる値です)

もちろん、必要に応じて のファミリから関数を呼び出すこともできますexec(つまり、別のアプリケーションを実行するため)。

于 2014-04-29T12:15:06.540 に答える