2

次のコードは決して終了しません。何故ですか?

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#define SIZE 5
int nums[SIZE] = {0, 1, 2, 3, 4};
int main()
{
  int i;
  pid_t pid;
  pid = vfork();
  if(pid == 0){  /* Child process */
    for(i = 0; i < SIZE; i++){
      nums[i] *= -i;
      printf(”CHILD: %d “, nums[i]);    /* LINE X */
    }
  }
  else if (pid > 0){  /* Parent process */
    wait(NULL);
    for(i = 0; i < SIZE; i++)
      printf(”PARENT: %d “, nums[i]);   /* LINE Y */
  }
  return 0;
}

アップデート:

このコードは、 に関するいくつかの混乱を説明するためのものvfork()です。を使用するvfork()と、子プロセスが親のアドレス空間をコピーしないようです。代わりに、アドレス空間を共有します。その場合、両方のプロセスによって nums 配列が更新されると予想されますが、私の質問はどのような順序ですか? OS は 2 つの間でどのように同期しますか?

コードが終了しない理由については、おそらく終了用に明示的に_exit()orexec()ステートメントを持っていないためです。私は正しいですか?

UPDATE2:
読んだところ: 56. fork() と vfork() システム コールの違いは? この記事は、私の最初の混乱を解決するのに役立つと思います。

vfork() システム コールからの子プロセスは、親のアドレス空間で実行され (これにより、親のデータと stack が上書きされる可能性があります)、子プロセスが終了するまで親プロセスが中断されます。

4

5 に答える 5

8

vfork(2)マニュアルページから引用するには:

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

あなたはそれらのことをたくさんやっているので、それがうまくいくと期待すべきではありません。ここでの本当の質問は、なぜではvfork()なく使用しているのかということだと思いますfork()

于 2013-01-23T12:50:18.943 に答える
3

使用しないでくださいvfork。それはあなたが得ることができる最も簡単なアドバイスです。あなたに与える唯一のことは、子がまたはvforkを呼び出すまで親を一時停止することです。アドレス空間の共有に関する部分は正しくありません。いくつかのオペレーティング システムはそれを行いますが、他のオペレーティング システムはそうしないことを選択します。exec*_exit

前回、アプリケーションvforkが実際にどのように使用されているかを見たとき、絶対多数が間違っていました。当時取り組んでいたオペレーティング システムでアドレス空間共有を可能にする 6 文字の変更を破棄したのは非常に残念でした。vfork少なくとも使用するほぼすべての人が、メモリ リークを悪化させることはありません。

本当に使いたい場合は、すぐに呼び出すか、子プロセスに戻った後vfork以外は何もしないでください。それ以外の場合は、未定義の領域に入っています。そして、私は本当に「何でも」を意味します。文字列の解析を開始して、exec 呼び出しの引数を作成すると、何かが触れてはならないものに触れることがほぼ保証されます。また、exec ファミリの他の関数ではありません。そこにある多くの libc は、、 、 などでコンテキスト内で安全でないことを行います。_exitexecveexecveexecvpexeclexeclevfork

あなたの例で具体的に何が起こっているのですか:

オペレーティング システムがアドレス空間を共有している場合、main から返された子は、環境がクリーンアップされることを意味します (printf を呼び出して stdout をフラッシュし、printf によって割り当てられたメモリを解放するなど)。これは、親がスタックしていたスタック フレームを上書きする他の関数が呼び出されていることを意味します。vfork親に戻ると、上書きされたスタック フレームに戻り、何かが起こる可能性があります。もう戻る。最初に printf を呼び出して未定義の動作の国に入り、次に main から戻ると未定義の動作の大陸に入り、main から戻った後のクリーンアップの実行により、未定義の動作の惑星に移動しました。

于 2013-01-23T14:26:24.363 に答える
1

公式仕様より:

vfork()によって作成されたプロセスが、vfork()からの戻り値を格納するために使用されるpid_t型の変数以外のデータを変更する場合、動作は未定義です。

プログラムでは、変数以外のデータを変更しますpid。つまり、動作は未定義です。

また_exit、プロセスを終了するために呼び出すか、exec関数ファミリーの1つを呼び出す必要があります。

于 2013-01-23T12:50:06.667 に答える
0

_exitから戻るのではなく、子供でなければなりませんmain。子が から戻った場合、 からmain戻ったときに親のスタック フレームは存在しませんvfork

于 2013-01-23T12:49:25.393 に答える
0

return を呼び出す代わりに _exit を呼び出すか、「子プロセス」の最後の行に _exit(0) を挿入します。return 0 は、stdout を閉じるときに exit(0) を呼び出すため、別の printf が続くと、プログラムがクラッシュします。

于 2013-01-23T13:13:16.873 に答える