私が知る限り、vfork
呼び出されると、子プロセスは親プロセスと同じアドレス空間を使用し、親プロセスのss変数で子プロセスが変更したものは親プロセスに反映されます。私の質問は次のとおりです
- 子プロセスが作成されると、親プロセスは一時停止されますか?
- では、なぜそうなのでしょうか?
- スレッドのように並列に実行できますか?結局のところ、スレッドとプロセスは両方とも同じ
clone()
関数を呼び出します。
ちょっとした調査やインターネット検索の最後に、親プロセスが実際に中断されるのではなく、呼び出しスレッドが中断されることを知りました。それにもかかわらず、子プロセスが実行されたとき、exit()
またはexec()
親プロセスは子プロセスが終了したことをどのように知ることができますか?子プロセスから戻るとどうなりますか?
答え1
あなたの問題は部分的に間違った命名規則に基づいています。カーネルの「制御スレッド」はユーザーのプロセスです。したがって、vforkの「呼び出しスレッドが中断されました」を読むときは、「マルチスレッドプロセス」の「スレッド」ではなく「プロセス」(または必要に応じて「重いスレッド」)を考えてください。
- はい、親プロセスが中断されました。
vfork
意味スキームは、あるプロセス(最も一般的にはシェル)が分岐し、一部のファイル記述子を混乱させ、exec
他のプロセスがその場所に代わる非常に一般的な状況に対して定義されます。カーネルの人々はexec
コピーされたページだけを捨てるので、コピーをスキップするとページのコピーオーバーヘッドを大幅に減らすことができることに気づきました。 vforked 子プロセスにはカーネルに独自のファイル記述子テーブルがあるため、その操作は親プロセスに影響を与えず、セマンティクスは変更されませfork
ん。
- なぜ?フォーク/実行は一般的で費用がかかり、無駄です。
「カーネル制御スレッド」のより正確な定義を考慮すると、並列に実行できるかどうかに対する答えは次のとおりです。
- いいえ。親プロセスは、子プロセスが終了または実行されるまでカーネルによってブロックされます。
両親は、子どもが自退したことをどのように知ることができますか?
- これは事実ではなく、カーネルは、子プロセスが離れるまで親プロセスがCPUを取得するのを知り、防止します。
最後の質問については、カーネルがリターンに関連する子スタック操作を検出し、キャッチできないシグナルで子プロセスにシグナルを送信したり直接終了したりすると疑われますが、詳細はわかりません。
答え2
私はLinuxプログラマーではありませんが、今日も同じ問題に直面して次のテストを実行しました。
#include<unistd.h>
#include<signal.h>
#include<errno.h>
#include<fcntl.h>
#include<cassert>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<iostream>
#include<fstream>
#include<sstream>
#include<list>
using namespace std;
int single_talk(int thread_id){
fprintf(stderr,"thread %d before fork @%d\n",thread_id,time(0));
int pid=vfork();
if(-1==pid){
cerr << "failed to fork: " << strerror(errno) << endl;
_exit(-3);//serious problem, can not proceed
}
sleep(1);
fprintf(stderr,"thread %d fork returned %d @%d\n",thread_id,pid,time(0));
if(pid){//"CPP"
fprintf(stderr,"thread %d in parent\n",thread_id);
}else{//"PHP"
sleep(1);
fprintf(stderr,"thread %d in child @%d\n",thread_id,time(0));
if(-1 == execlp("/bin/ls","ls",(char*)NULL)){
cerr << "failed to execl php : " << strerror(errno) << endl;
_exit(-4);//serious problem, can not proceed
}
}
}
void * talker(void * id){
single_talk(*(int*)id);
return NULL;
}
int main(){
signal(SIGPIPE,SIG_IGN);
signal(SIGCHLD,SIG_IGN);
const int thread_count = 44;
pthread_t thread[thread_count];
int thread_id[thread_count];
int err;
for(size_t i=0;i<thread_count;++i){
thread_id[i]=i;
if((err = pthread_create(thread+i,NULL,talker,thread_id+i))){
cerr << "failed to create pthread: " << strerror(err) << endl;
exit(-7);
}
}
for(size_t i=0;i<thread_count;++i){
if((err = pthread_join(thread[i],NULL))){
cerr << "failed to join pthread: " << strerror(err) << endl;
exit(-17);
}
}
}
でコンパイルg++ -pthread -o repro repro.cpp
して実行しました./repro
。出力で見ることができるのは、すべてが順番に同時に発生することです。まず、すべてのpthreadがvforkを実行してから1秒間待ってから、子プロセスの現実で「目覚め」、すべての子プロセスがexec()を実行し、最終的にすべての親プロセスが実行されます。覚醒
私にとって、これは1つのpthreadがvforkを呼び出すと他のpthreadが中断されないことを証明します。中断すると、exec()が呼び出されるまでvfork()を呼び出すことはできません。
答え3
子プロセスが作成されると、親プロセスは一時停止されますか?
はい。
それではなぜ?
これは、親プロセスと子プロセスがアドレス空間を共有するためです。特にスタックのアドレス空間。親プロセスが続行しようとすると、他の関数を呼び出して子プロセスの呼び出しスタックを削除できます。したがって、exec()
または_exit()
スレッドのように並列に実行できますか?最後に、スレッドとプロセスの両方が同じclone()関数を呼び出します。
もちろん。呼び出しスレッドのみが中断されます。探す。
親プロセスは、子プロセスが終了したことをどのように知ることができますか?
代わりwait4()
に親に進む方法を尋ねます。しかし、実際にはそうではありません。定義された2つのイベントのいずれかが発生すると、カーネルはそのイベントを再実行します。
子プロセスから戻るとどうなりますか?
vfork()
リリースされたスタックフレームに戻り、以下はreturn;
未定義の動作へのクイックパスです。
答え4
子プロセスから戻るとどうなりますか?
しかし、サブコンテキストで実行されている間にvfork()を呼び出すプロシージャから返すと、vfork()は最後の戻り値が存在しなくなったスタックフレームとして返されるので機能しません。
私はこれを文字通り受け入れません。スタック領域のバイトは、各POP(またはRETURN)コマンドの後に実際には消えません。ただし、後に戻って実行し、PUSH(またはCALL)コマンドを実行すると、スタックの以前の値が置き換えられます。
vfork()
これは、呼び出してからプロセスのメモリを変更するために何かを実行するときに頻繁に発生する可能性がある奇妙なことの代表的な例です。
[技術的には、Linuxと同じ動作を想定しています。 vfork() の他の動作は POSIX で技術的に受け入れられます。誰かがfork()]と同等のvfork()を提供することに加えて、POSIXテクノロジの柔軟性を使用する方法を見つけたのかもしれません。