コンテンツ
1.プロセスの作成
プロセス管理ブログでフォークの使い方についてはすでにお話しましたが、詳しくは前のブログをご覧ください。プロセスが作成されるときにOSは何をする必要がありますか?
1.PCB(プロセス制御ブロック)ページテーブルとmm_struct
2.親プロセスのデータの一部を子プロセスにコピーします(変更が発生しない場合は、コードとデータの共有)
3.子プロセスのPCBをCPUスケジューリングの許可キューに入れます。
以下のコードを見てみましょう。
1#include<iostream> 2 using namespace std; 3 #include<sys/types.h> 4 #include<unistd.h> 5 int main() 6 { 7 if(fork()==0) 8 { 9 cout<<"I am a child pid:"<<getpid()<<"ppid:"<<getppid()<<endl; 10 } 11 else 12 { 13 cout<<"I am father pid:"<<getpid()<<"ppid:"<<getppid()<<endl; 14 sleep(1); 15 } 16 17 18 19 return 0; 20 } ~
フォークは作成されるたびに成功しますか?答えはいいえだ。システム内のプロセスが多すぎると、子プロセスを作成できません
または、実際のユーザー数が制限を超えているなど。
2.コピーオンライト
Linuxで親プロセスと子プロセスのコードとデータが共有されているという前提は、変更が発生しないという条件に基づいています。いずれかの当事者が変更を試みると、コピーオンライトが発生し、子プロセスと親プロセスが変更されます。プロセスにはそれぞれプライベートコピーがあります。
コピーオンライトが発生すると、変更しようとしているパーティのページテーブルのマッピング関係のみが変更され、コピーオンライトが発生した後に書き込み権限が付与されます。そして、なぜコピーオンライトなのか?
1.スペースの浪費を減らします。親プロセスと子プロセスがコードを変更しない場合、親プロセスと子プロセスはスペースの一部を共有できるため、スペースの浪費が減ります。
2.プロセス間の独立性を維持します。親子コードは共有されますが、一方の当事者が共有データを変更しようとします。コピーオンライトが発生しない場合、プロセス間の独立性を維持できません。オンライトパーティ対応するデータへの変更は、相手に影響を与えません。
3.プロセスの終了
プロセスが終了する状況は3つあります。
1.コードの実行後、結果は正しい(ロジックは正しい、正常に終了する)
2.コードの実行後、結果は正しくない(コードロジックに問題がある、結果は正しくない)
3.コード異常終了
プロセスの終了コードを確認する方法:
echo $?は、プロセスの終了コードを表示できます。例は次のとおりです。
1#include<iostream> 2 using namespace std; 3 #include<sys/types.h> 4 #include<unistd.h> 5 int main() 6 { 7 if(fork()==0) 8 { 9 cout<<"I am a child pid:"<<getpid()<<"ppid:"<<getppid()<<endl; 10 } 11 else 12 { 13 cout<<"I am father pid:"<<getpid()<<"ppid:"<<getppid()<<endl; 14 sleep(1); 15 } 16 17 18 19 return 0; 20 } ~
上記のコードを例として取り上げます。次のプログラムを実行します。
C言語を学習しているとき、メイン関数の戻り値は0を返し、メイン関数の戻り値は実際にはプロセスの終了コードです。通常、0は正常を意味し、ゼロ以外はエラーを意味します。
mainに0を返すだけでプロセスを終了できますか?関数でプロセスを終了したい場合、通常の関数でreturnを実行すると、プロセスの終了ではなく関数が実行されるため、returnを使用できません。通常の関数内でプロセスを終了する場合は、exitまたは_exitを使用できます。
_出口
コードを使ってデモンストレーションしましょう。
1.#include<iostream> 2 using namespace std; 3 #include<sys/types.h> 4 #include<unistd.h> 5 void func(int b,int a) 6 { 7 if(a==0) 8 { 9 cout<<"非法数据"<<endl; 10 exit(1); 11 } 12 cout<<b/a<<endl; 13 } 14 int main() 15 { 16 int a=0; 17 int b=10; 18 func(b,a); 19 cout<<"hello word"<<endl; 20 return 0; 21 22 }
exitがプロセスを強制終了できる場合、hellowordは出力されません。このプログラムを実行してみましょう:
hellowordは出力されず、プロセスの終了コードは正確に終了に渡されるパラメーターであることがわかります。
_出口
_exitとexitの違いは、exitがバッファーをフラッシュし、_exitがバッファーをフラッシュしないことです。
同様に、上記のコードを使用します。
#include<stdlib.h> 2 #include<iostream> 3 using namespace std; 4 #include<sys/types.h> 5 #include<unistd.h> 6 void func(int b,int a) 7 { 8 if(a==0) 9 { 10 cout<<"非法数据"; 11 exit(1); 12 } 13 cout<<b/a<<endl; 14 } 15 int main() 16 { 17 int a=0; 18 int b=10; 19 func(b,a); 20 cout<<"hello word"<<endl; 21 return 0; 22 23 }
稼働中:
違法データの文章が出力される可能性があることがわかりました。_exitを使用するとどうなりますか?
この時点で、不正な入力が印刷されていないことがわかりました。
異常終了:
1. ctr + cを使用して、フォアグラウンドプロセスを終了します
2. killコマンドを使用して、終了信号を生成します
4.プロセス待機
1.プロセス待機の必要性:
1.子プロセスが終了します。親プロセスが子プロセスの終了情報を読み取らない場合、「ゾンビプロセス」の問題が発生し、メモリリークが発生する可能性があります。
2.プロセスがゾンビになると、それは無敵になり、「点滅せずに殺す」のキル-9は、死んだプロセスを誰も殺すことができないため、何もできません。
3.親プロセスによって子プロセスに割り当てられたタスクがどのように完了するかを知る必要があります。たとえば、子プロセスが完了した場合、結果は正しいか間違っているか、または正常に終了するかどうかです。
4.親プロセスは、子プロセスのリソースをリサイクルし、プロセス待機の方法を通じて子プロセスの終了情報を取得します。プロセス待機の方法
2.プロセス待機の方法
プロセスの待機方法:
1.待つ
2.waitpid
まず、待機を見てみましょう。
関数の戻り値:
戻り値には2つのケースがあります。1。成功を待って待機中のプロセスのIDを返す、2。失敗を待って-1を返す、つまり、成功した場合は待機中のプロセスのIDを返す。失敗した場合は-1が返されます。
パラメータ:ステータスは出力パラメータです。c言語では、関数内の値を取得する場合は、アドレスを渡し、関数内でそれを逆参照して、関数外の関数内でいくつかの値を取得できるようにする必要があります。stautsを使用すると、待機中のプロセスの終了ステータスを取得できます。もちろん、これを気にしない場合は、NULLに設定できます。
例:
#include<stdlib.h> 2 #include<iostream> 3 using namespace std; 4 #include<sys/types.h> 5 #include<unistd.h> 6 #include<wait.h> 7 int main() 8 { 9 pid_t id=fork(); 10 if(id<0) 11 { 12 cerr<<"fokr fail"<<endl; 13 } 14 else if(id==0) 15 { 16 //子进程 17 int cnt=10; 18 while(cnt--) 19 { 20 cout<<"I am a child pid: "<<getpid()<<"ppid:"<<getppid()<<endl; 21 sleep(1); 22 } 23 exit(1); 24 } 25 //父进程 26 cout<<"father process begin"<<endl; 27 sleep(5); 28 pid_t Id=wait(0);//不关系子进程的退出状态 29 cout<<"parent process wait finish"<<endl; 30 if(Id<0) 31 { 32 cout<<"parent wait fail"<<endl; 33 } 34 else 35 { 36 cout<<"wait success"<<endl; 37 } 38 39 }
以下では、スクリプトを使用してプロセスを監視します。
while :; do ps axj | head -1 && ps ajx | grep test; sleep 2; echo "#############################################"; done
子プロセスは終了してゾンビ状態になります。
親プロセスは子プロセスを待機し、子プロセスのリソースをリサイクルした後、子プロセスもz状態からx状態に変化します
2.waitpid
説明:
1.戻り値:成功を待つと、成功を待つ子プロセスのIDが返され、失敗の場合は-1が返されます。戻り値はwaitと同じです。WNOHANGが設定されていて、waitpidが終了した子プロセスを検出しない場合、0を返します。
2.パラメータの最初のパラメータは、待機するプロセスです。pid= -1の場合、他のプロセスを待機するのと同じです。pidが0より大きい場合、pidと同じプロセスを待機します。
3. 2番目のパラメーター:出力パラメーター。待機中のプロセスの終了ステータスを気にしない場合は、NULLに設定できます。WIFEXITD(ステータス)は、子プロセスが正常に戻る場合にtrueになります(通常、プロセスが正常に終了するかどうかを判別するために使用されます)。WEXITSTATUS(status):WIFEXITD(status)がゼロ以外の場合、子プロセスの終了コードを抽出するために使用できます(子プロセスの終了コードを参照)。
4. 3番目のパラメーターオプション:pidで指定された子プロセスが終了しない場合、waitpidの戻り値は0です。正常に終了すると、子プロセスのIDを返します(ブロッキングベースのポーリングアクセス待機用)0待機のブロックを表します。親プロセスは待機中に何もしません。
同様に、上記の待機をwaitpidに変更します。
#include<stdlib.h> 2 #include<iostream> 3 using namespace std; 4 #include<sys/types.h> 5 #include<unistd.h> 6 #include<wait.h> 7 int main() 8 { 9 pid_t id=fork(); 10 if(id<0) 11 { 12 cerr<<"fokr fail"<<endl; 13 } 14 else if(id==0) 15 { 16 //子进程 17 int cnt=5; 18 while(cnt--) 19 { 20 cout<<"I am a child pid: "<<getpid()<<"ppid:"<<getppid()<<endl; 21 sleep(1); 22 } 23 exit(10); 24 } 25 //父进程 26 27 cout<<"father process begin"<<endl; 28 pid_t Id=waitpid(-1,NULL,0);//等待任意一个子进程 29 cout<<"parent process wait finish"<<endl; 30 if(Id<0) 31 { 32 cout<<"parent wait fail"<<endl; 33 } 34 else 35 { 36 cout<<"wait success"<<endl; 37 } 38 39 }
プログラムを実行します。
3.子プロセスのステータスを取得します。
waitとwaitpidの両方に出力パラメーターがあります。これは気にしない場合はNULLに設定できます。使用目的について説明します。ステータスの32ビットはハイで、16ビットは調査されず、ロー16ビットのみが調査されます。研究されています。ビット
信号によって殺された場合:
この図から、下位7ビットが終了信号、8ビットのcore_dumpフラグ、上位8ビットが終了コードを表していることがわかります(終了コードは、プロセスが正常に終了したときに意味があります)。下位7ビットの値を取り出します。0の場合はプロセスが正常に終了し、上位8ビットの値はプロセスの終了コードを意味します。同様に0の場合は終了を意味します。下位7ビットが0でない場合は、プロセスが異常であることを意味します。この時点では、終了コードは無意味です。
例:
#include<stdio.h> 2 #include<stdlib.h> 3 #include<iostream> 4 using namespace std; 5 #include<sys/types.h> 6 #include<unistd.h> 7 #include<wait.h> 8 int main() 9 { 10 pid_t id=fork(); 11 if(id<0) 12 { 13 cerr<<"fokr fail"<<endl; 14 } 15 else if(id==0) 16 { 17 //子进程 18 int cnt=5; 19 while(cnt--) 20 { 21 cout<<"I am a child pid: "<<getpid()<<"ppid:"<<getppid()<<endl; 22 sleep(1); 23 } 24 exit(10); 25 } 26 //父进程 27 28 29 int status=0; 30 pid_t Id=waitpid(-1,&status,0 );//等待任意一个子进程默认行为阻塞等待 31 32 if(Id>0) 33 {//子进程退出了waitpid也成功了 34 printf("father wait: %d ,sucess,status:exit code:%d,status exit signal:%d\n",Id,((status)>>8)&0xFF,status&0x7F); 35 } 36 else if(Id==0) 37 { 38 39 cout<<"Do my things"<<endl; 40 } 41 else if(Id<0) NORMAL ?? test.cpp ? main() ? cpp ? ? utf-8 ?? ? 72% 35/48 : 4
4.ブロッキング待機と非ブロッキング待機
1.ブロックと待機:親プロセスは子プロセスを待機しており、子プロセスは終了しません。待機し、他に何もしません。
2.ブロッキングの本質:親プロセスのPCBは、R状態からS状態に変化し、実行キューから待機キューに変化し、子プロセスを待機します。
3.終了を待つことの本質:親プロセスが待機キューから実行中のキューに変わり、親プロセスのPCBもS状態からR状態に変わります。
#include<stdlib.h> 2 #include<iostream> 3 using namespace std; 4 #include<sys/types.h> 5 #include<unistd.h> 6 #include<wait.h> 7 int main() 8 { 9 pid_t id=fork(); 10 if(id<0) 11 { 12 cerr<<"fokr fail"<<endl; 13 } 14 else if(id==0) 15 { 16 //子进程 17 int cnt=5; 18 while(cnt--) 19 { 20 cout<<"I am a child pid: "<<getpid()<<"ppid:"<<getppid()<<endl; 21 sleep(1); 22 } 23 exit(10); 24 } 25 //父进程 26 27 cout<<"father process begin"<<endl; 28 int status=0; 29 pid_t Id=waitpid(-1,&status,0);//等待任意一个子进程默认行为阻塞等待 30 cout<<"parent process wait finish"<<endl; 31 if(Id>0) 32 { 33 cout<<"wait sucess"<<endl; 34 } 35 else if(Id<0) 36 { 37 cout<<"parent wait fail"<<endl; 38 } 39
ノンブロッキング待機
非ブロッキング待機:親プロセスは子プロセスの終了ステータスを常にチェックし、子プロセスの検出中に独自の処理を実行できます。
#include<stdio.h> 2 #include<stdlib.h> 3 #include<iostream> 4 using namespace std; 5 #include<sys/types.h> 6 #include<unistd.h> 7 #include<wait.h> 8 int main() 9 { 10 pid_t id=fork(); 11 if(id<0) 12 { 13 cerr<<"fokr fail"<<endl; 14 } 15 else if(id==0) 16 { 17 //子进程 18 int cnt=5; 19 while(cnt--) 20 { 21 cout<<"I am a child pid: "<<getpid()<<"ppid:"<<getppid()<<endl; 22 sleep(1); 23 } 24 exit(10); 25 } 26 //父进程 27 28 while(1){ 29 int status=0; 30 pid_t Id=waitpid(-1,&status,WNOHANG );//等待任意一个子进程默认行为阻塞等待 31 32 if(Id>0) 33 {//子进程退出了wait也成功了 34 printf("father wait: %d ,sucess,status:exit code:%d,status exit signal:%d\n",Id,((status)>>8)&0xFF,status&0x7F); 35 break; 36 } 37 else if(Id==0) 38 { 39 //子进程还没有退出,但是waitpid是成功的,需要父进程重复等待 40 cout<<"Do my things"<<endl; 41 } NORMAL ?? test.cpp ? main() ? cpp ? ? utf-8 ?? ? 69% 34/49 : 5
結果は次のとおりです。