そのうちの1つがvforkを実行しているときに、pthreadが実際にどうなるかを確認しようとしています。
仕様では、子プロセスがexec *または_exitを呼び出すまで、親の「制御スレッド」は「一時停止」されます。
私が理解しているように、コンセンサスは、親プロセス全体(つまり、すべてのpthreadを含む)が中断されていることを意味します。
実験を使用して確認したいと思います。
これまでのところ私はいくつかの実験を実行しましたが、そのすべてが他のpthreadが実行されていることを示唆しています。私はLinuxの経験がないので、これらの実験の解釈が間違っていると思います。これらの結果の実際の解釈を学ぶことで、私の人生におけるさらなる誤解を避けることができます。
それで、私が行った実験は次のとおりです。
実験I
#include<unistd.h>
#include<signal.h>
#include<errno.h>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
void * job(void *x){
int pid=vfork();
if(-1==pid){
cerr << "failed to fork: " << strerror(errno) << endl;
_exit(-3);
}
if(!pid){
cerr << "A" << endl;
cerr << "B" << endl;
if(-1 == execlp("/bin/ls","ls","repro.cpp",(char*)NULL)){
cerr << "failed to exec : " << strerror(errno) << endl;
_exit(-4);//serious problem, can not proceed
}
}
return NULL;
}
int main(){
signal(SIGPIPE,SIG_IGN);
signal(SIGCHLD,SIG_IGN);
const int thread_count = 4;
pthread_t thread[thread_count];
int err;
for(size_t i=0;i<thread_count;++i){
if((err = pthread_create(thread+i,NULL,job,NULL))){
cerr << "failed to create pthread: " << strerror(err) << endl;
return -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;
return -17;
}
}
}
44個のpthreadがあり、そのすべてがvforkを実行し、子内でexecを実行します。
各子プロセスは、vforkとexec「A」および「B」の間で2つの出力操作を実行します。
理論は次のことを示唆しています。出力は、ネストせずにABABABABABA…と表示されるはずです。
ただし、出力は完全に混乱しています。例:
AAAA
BB
B
B
実験II
vforkの後にI/O libを使用するのは悪い考えかもしれないと思って、job()関数を次のように置き換えました:
const int S = 10000000;
int t[S];
void * job(void *x){
int pid=vfork();
if(-1==pid){
cerr << "failed to fork: " << strerror(errno) << endl;
_exit(-3);
}
if(!pid){
for(int i=0;i<S;++i){
t[i]=i;
}
for(int i=0;i<S;++i){
t[i]-=i;
}
for(int i=0;i<S;++i){
if(t[i]){
cout << "INCONSISTENT STATE OF t[" << i << "] = " << t[i] << " DETECTED" << endl;
}
}
if(-1 == execlp("/bin/ls","ls","repro.cpp",(char*)NULL)){
cerr << "failed to execlp : " << strerror(errno) << endl;
_exit(-4);
}
}
return NULL;
}
今回は、2番目のループが最初のループの結果を元に戻すように2つのループを実行します。したがって、最後にグローバルテーブル t []
初期状態(定義上、すべてゼロ)に戻る必要があります。
子プロセスに入ると、他のpthreadがフリーズし、現在の子がループを終了するまでvforkを呼び出せなくなる場合、配列はすべてゼロである必要があります。
そして、vfork()の代わりにfork()を使用すると、上記のコードは出力を生成しないことを確認しました。
ただし、fork()をvfork()に変更すると、次のようになります。大量の不整合がstdoutに報告されました。
実験III
もう1つの実験については、https://unix.stackexchange.com/a/163761/88901で説明しています。これにはスリープの呼び出しが含まれていましたが、実際には、長い for
> ループ。
承認された回答:
vork
のLinuxマニュアルページ 非常に具体的です:
vfork()
fork(2)
とは異なります 呼び出し元のスレッド 子が終了するまで停止されます
これはプロセス全体ではありませんが、実際にスレッドを呼び出しています。 。この動作はPOSIXまたは他の標準によって保証されていません。他の実装は、異なることを行う可能性があります( vfork
の実装まで)。 プレーンなfork
。
(Rich Felkerは、危険と見なされるvforkでのこの動作にも注意しています。)
fork
の使用 マルチスレッドプログラムでは、 vfork
を呼び出して、すでに推論するのは難しいです。 少なくとも同じくらい悪いです。テストは未定義の動作でいっぱいです。vfork
内で関数を呼び出すこと(I / Oを行うことは言うまでもありません)も許可されていません。 exec
を除く子 -タイプ関数と_exit
( exit
でもありません 、および戻ると騒乱が発生します。
これがあなたの例です。ほぼ int
でのアトミックな読み取りと書き込みの関数呼び出しを出力しないコンパイラ/実装を想定した場合、未定義の動作はありません。 s。 (1つの問題は、 start
への書き込みです。 vfork
の後 –それは許可されていません。)エラー処理を省略して短くしました。
#include<unistd.h>
#include<signal.h>
#include<errno.h>
#include<atomic>
#include<cstring>
#include<string>
#include<iostream>
std::atomic<int> start;
std::atomic<int> counter;
const int thread_count = 4;
void *vforker(void *){
std::cout << "vforker starting\n";
int pid=vfork();
if(pid == 0){
start = 1;
while (counter < (thread_count-1))
;
execlp("/bin/date","date",nullptr);
}
std::cout << "vforker done\n";
return nullptr;
}
void *job(void *){
while (start == 0)
;
counter++;
return NULL;
}
int main(){
signal(SIGPIPE,SIG_IGN);
signal(SIGCHLD,SIG_IGN);
pthread_t thread[thread_count];
counter = 0;
start = 0;
pthread_create(&(thread[0]), nullptr, vforker, nullptr);
for(int i=1;i<thread_count;++i)
pthread_create(&(thread[i]), nullptr, job, nullptr);
for(int i=0;i<thread_count;++i)
pthread_join(thread[i], nullptr);
}
考え方は次のとおりです。通常のスレッドは、アトミックグローバル変数 start
を待機(ビジーループ)します。 1
になる グローバルアトミックカウンターをインクリメントする前。 vfork
を実行するスレッド start
を設定します 1
へ vforkの子で、他のスレッドがカウンターをインクリメントするのを待ちます(再びビジーループ)。
vfork
中に他のスレッドが中断された場合 、進行はありません。中断されたスレッドが counter
をインクリメントすることはありません。 ( start
の前に一時停止されていました 1
に設定されました )、そのため、vforkerスレッドは無限のビジーウェイトでスタックします。