0

次のコードを検討してください。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int g_a = 1;

int main(void){
    int l_b = 1;
    pid_t p;
    if((p = vfork()) < 0){
            perror("vfork");
            return -1;
    }else if(p == 0){
            g_a++;
            l_b++;
            _exit(0);   //use the system call _exit
    }
    printf("ppid = %d,pid = %d,g_a = %d,l_b = %d\n", getppid(), getpid(), g_a, l_b);
    return 0;
}

結果は次のとおりです: ppid = 21297,pid = 21553,g_a = 2,l_b = 2

しかし、_exit(0); を置き換えると、return 0; を使用すると、結果は次のようになりました。

ppid = 21297、pid = 21563、g_a = 2、l_b = -1216841009

セグメンテーション違反

セグメンテーション障害があります,_exit と return の違いは何ですか??

4

1 に答える 1

4

から戻ることは、一連のクリーンアップ操作 (開いているオブジェクトのフラッシュやプロシージャの実行など) を実行mainする を呼び出すことと同じです。次に、プロセスを実際に終了させるシステム コールである を呼び出します。したがって、直接呼び出すと、これらのクリーンアップ操作はすべてスキップされます。exitFILEatexit_exit_exit

を使用してvforkいます。except 呼び出しまたはの子側で何かを行うのは正しくありません (正式には、「未定義の動作を引き起こす」) 。から戻ることは、何か他のこと (つまり、 を呼び出すこと) としてカウントされます。したがって、プログラムがクラッシュすることはまったく驚くべきことではありません。vfork_exitexecvemainexit

編集:関連する仕様に関する限り、変数の変更g_al_bの子側vforkも同じ悲惨な条件禁止されています (強い意味での「未定義の動作」、つまり「プログラムをクラッシュさせることが許可されています」 )。 ただし、私が認識している現存するすべての実装では、子がメモリ (スタック フレームを含む) の割り当てまたは割り当て解除を引き起こすために何かを行った場合にのみ、大惨事が発生します。親によって割り当てられたが、子 (ローカルかグローバルかに関係なく) に見える変数を変更することは、はるかに制限された方法で予測できません。

  1. vforkが の別の名前である場合fork、子が行うことは何も親に表示されません。
  2. しかしvfork、新しいアドレス空間の割り当てを まで遅らせるという特別な動作がある場合、親が実行を再開した後、メモリに常駐する変数execveに対する子による変更が親に表示されます。(レジスタに常駐する変数は、カーネルが親の実行コンテキストを復元するときにリセットされる場合とされない場合があります。これは、コンテキスト スイッチがどれだけ完全かによって異なります。)

また、OS がタイプ 2 であることがわかっている場合は、これを有利に使用して回避できます。たとえば、失敗errnoした後に親に戻すことexecveができます。これは、plain では非常に困難ですfork(終了ステータスが狭すぎるため)。ただし、それはあなたがする権利があることではなく、あなたが逃げていることです。特に、将来の移植性の問題の原因となります。

于 2013-11-14T03:19:59.430 に答える