プロセス制御 (Linux)
プロセスの作成
フォーク機能の紹介
fork 関数の機能は、既存のプロセス内に子プロセスを作成することです。
#include <unistd.h>
pid_t fork(void);
戻り値: 作成に失敗した場合は -1 が返され、作成が成功した場合、子プロセスは 0 を返し、親プロセスは子プロセスのプロセス ID を返します。簡単な例えができます: 父親は複数の子を持つことができますが、子は父親は 1 人だけなので、プロセスの作成後、子のプロセスをid
父親に渡す必要があり、子プロセスは必要なgetpid()
限り独自のプロセスを取得できます。id
プロセスが fork 関数を呼び出した後、オペレーティング システムは新しいメモリ ブロックとカーネル データ構造を子プロセスに割り当て、親プロセスのデータ構造の内容の一部を子プロセスにコピーし、子プロセスをプロセス リストに追加します。
fork関数の戻り値
fork
関数にはなぜ2 つの戻り値があるのでしょうか? そして、戻った後、なぜ子プロセスの ID を親プロセスに割り当て、値 0 を子プロセスに割り当てるのでしょうか? if else
なぜ同時に同じ戻り値が成立するのでしょうか?
次に、機能を詳しく見てみましょう
まず、ユーザーがfork
関数を使用すると、オペレーティング システムが関数の呼び出しを開始して、対応するタスクを完了します。
関数fork()
が準備完了return pid;
すると、コア コードが実行され、子プロセスも作成され、オペレーティング システムの操作キューにスケジュールする準備が整います。したがって、実行前に親プロセスと子プロセスが分割されます。ステートメントはreturn pid;
別々に実行できますreturn pid;
; return の本質 書き込みです、子プロセスまたは親プロセスが最初に戻り、コピー時の書き込みのため、誰が最初に ID に書き込むか、同じ ID が 2 つの異なる内容を持つことになります、異なる内容と判断if else
文に応じて一致します
フォークの一般的な使い方
- 親プロセスは自分自身をコピーして、親プロセスと子プロセスが異なるコードセグメントを同時に実行したいと考えています。
- 親プロセスは子プロセスに別のプログラムを実行させることを望んでいます
フォーク呼び出しが失敗した理由
- システム内のプロセスが多すぎます
- 実ユーザープロセス数が制限を超えています
プロセスが終了しました
プロセス終了シナリオ
- コードが実行され、結果は正しいです
- コードは最後まで実行されますが、エラーが発生します
- コードが異常終了する
プロセスを終了する一般的な方法
まず終了コードを紹介します。これまでに書かれたコードは最後に追加されますreturn 0
が、なぜ他の数字ではなく数字 0 でなければならないのでしょうか? ? ?
プロセスが終了すると、プロセスの実行結果が正しいかどうかを示す、対応する終了コードが返されます。一般に、終了コードには対応するテキストの説明が必要です。終了コード: 0 は、プログラムが正常に実行されていることを意味します
。0 特定の数字はさまざまなエラーを示します
プログラムは正常に終了します。
最新のプロセス終了コードecho $
を確認する
-
戻る 戻る
-
終了
プログラムを呼び出すと、すぐには出力されずhello world
2 秒後に出力され、終了コードもプログラムにexit(1)
設定されたものと一致します。 -
_exit
プログラムを呼び出してもまったく出力されずhello world
、終了コードも同じままです。
理由は次のとおりです。
exit はプロセスを終了し、バッファをアクティブにリフレッシュします。
_eixt はバッファをリフレッシュせずにプロセスを終了します。
2 つの違い: exit はライブラリ関数、_exit はシステム コールです。
戻り関数
return
これはプロセスを終了するより一般的な方法であり、実行はreturn n
実行と同等です。exit(n)
プロセス待機中
処理待ちの必要性
- 子プロセスが終了すると、親プロセスはステータスを取得できなくなり、子プロセスがゾンビ状態になり、メモリ リークが発生します。
- プロセスがゾンビになった場合、OS は何もできません
- 親プロセスが子プロセスを作成する目的はタスクを完了することなので、子プロセスが実行されているかどうか、結果が正しいかどうか、正常に終了するかどうかを知る必要があります。
- 親プロセスは子プロセスのリソースを回収し、プロセス待機によって子プロセスの終了情報を取得します。
サブプロセスのステータスを取得する
プロセスの待機方法
wait/waitpid
、それぞれにstatus
パラメータが、オペレーティング システムによって設定される出力パラメータです。- NULLが渡された場合は、子プロセスの終了ステータス情報は関係ない(子プロセスはブロックされている)ことを意味します。
- オペレーティング システムは、このパラメータに従って、子プロセスの終了情報を親プロセスにフィードバックします。
status
単純に全体として見ることはできません。ビットマップは次のとおりです: 信号が 0 に等しい場合は正常終了を意味し、終了ステータスは終了コードです (ビットマップについては後ほど詳細に検討します)。
status&0x7F
プロセス終了情報、status >>8)&0xFF
プロセス終了ステータスの取得
waitメソッド
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
戻り値: 成功した場合は、待機中のプロセスの ID を返します。失敗した場合は、-1 を返します。
パラメーター: 出力パラメーター、子プロセスの終了ステータスを取得します。気にしない場合は、NULL に設定できます。
子プロセスは最初の 10 秒間は実行されており、ステータスは ですS+
。10 秒から 15 秒の間、子プロセスは終了し、ゾンビ プロセスになりますZ+
。15 秒後、親プロセスはwait
子プロセスのリソースを再利用します。
waitpid メソッド
#include<sys/types.h>
#include<sys/wait.h>
pid_t waitpid(pid_t pid,int*status,int options);
戻り値: 成功した場合は、子プロセスの ID を返します。オプションが設定されているが、WNOHANG
収集waitpid
する終了した子プロセスがない場合は 0 を返します。失敗した場合は -1 を返し、errno は対応する値に設定されます。値が間違った表示になっている
パラメータ:
pid
: 子プロセスID
status
: WIFEXITED
: 子プロセスが正常に終了した場合は true、プロセスが正常に終了したかどうかを確認します; WEXITSTATUS
: WIFEXIT が 0 以外の場合、プロセスの終了コードを抽出し、プロセスの終了コードを確認します
options
:
0 はデフォルトでブロッキング状態になります; WNOHANG
(ノンブロッキング): pid で指定されたサブプロセスが終了していない場合、waitpd
関数は待機せずに 0 を返します; プロセスが正常に終了した場合は、サブプロセスの ID が返されます
status
子プロセス情報を取得するプロセス: 子プロセスが終了すると、終了ステータスと終了情報が PCB に保存されます。親プロセスのシステム コールは、子プロセスの終了ステータスを取得するwaitpid
ことによって、情報を終了します。status
マクロでステータスを取得する
ブロッキングとノンブロッキング
ブロッキング: 親プロセスが子プロセスのリソースの取得を待機しているとき、子プロセスが終了していない場合、親プロセスは子プロセスの終了を待機しています; ノンブロッキング: 親プロセスが子プロセスのリソースの取得を待機しているとき、子プロセスが終了していない場合、親プロセスは子プロセスを常に待たずに他のプログラムを実行し、ポーリングを取ることができます
pid_t waitpid(pid_t pid,int*status,int options);
このうち、option
値が0の場合はブロッキング状態を表し、値が0option
の場合はWNOHANG
ブロッキング状態は上に示されており、子プロセス リソースの非ブロッキング取得は次に示されます。
waitpid
戻り値: 0 に等しい場合、呼び出しは成功しますが、子プロセスは終了しません。0 より大きい場合、呼び出しは成功し、子プロセスも終了します。0 より小さい場合、呼び出しは失敗します。
- 子プロセスがすでに終了している場合、
wait/waitpid
呼び出されるとすぐに戻り、リソースを解放し、子プロセスの終了情報を取得します。 - いつでも呼び出されると
wait/waitpid
、子プロセスが存在し、正常に実行されますが、プロセスがブロックされる可能性があります。 - 子プロセスが存在しない場合は、すぐにエラーが返されます。
プロセスプログラムの置き換え
交換原理
fork
プログラムを実行するために子プロセスが作成された後、子プロセスexec
は別のプログラム、つまり親プロセスとは異なるプログラムを実行するために関数を呼び出す必要があることがよくあります。プロセスが関数を呼び出すとき、作成された子プロセスexec
は、プログラムはメモリにロードされて実行されます。呼び出しによってexec
新しいプロセスは作成されないため、exec
プロセスの ID は呼び出しの前後で変わりません。
プログラム置換の本質: 指定されたプログラムのコードとデータを指定された場所にロードし、親プロセスのデータとコードを上書きしない
置換関数
から始まるexec
6種類の関数があります。exec
int execl(const char *path, const char *arg, ...);
l
: リスト、パラメータを 1 つずつexecl
渡し
最初にプログラムを実行します。最初にプログラムを見つけてから実行する必要があります。実行方法も含めて、
path
プログラムのパスを示します。コマンド ライン パラメータと同様にarg
、プログラムの実行方法を示します。可変パラメータのリストを示します。 、最後のパラメータは次のようにする必要があります...
NULL
実行結果では、最初の結果のみが出力されprintf
、2 番目の結果は出力されません。なぜですか? もコードであり、2番目は置換関数の後にあるため、置換関数が実行されると、元のコードは完全に覆われ、新しいプログラムコードが起動して実行されるため、2番目は実行できませんprintf
execl
printf
int execlp(const char *file, const char *arg, ...);
p
:path、プログラムが配置されているパスを自動的に見つけることができ、実行するプログラムを入力するだけです
1 つ目はls
実行するプログラムを表し、2 つ目はls
実行方法を表します。
int execv(const char *path, char *const argv[]);
v
: ベクトル、変数パラメータを使用する代わりに、すべての実行可能なパラメータを配列に入れて均一に渡すことができます。
int execvp(const char *file, char *const argv[]);
実行するプログラムとその実行方法を直接入力します。
ここまででシステムコマンドが実行できましたので、作成したプログラムを実行してみましょう。
自分で書いたプログラム:
操作結果:
実行結果によると、呼び出しは非常に成功しているため、置換関数を使用して任意の言語でプログラムを呼び出すことができます。
int execle(const char *path, const char *arg,
..., char * const envp[]);
e
: カスタム環境変数
カスタム環境変数を追加せずに最初に試してください
上記は 2 つのシステムで環境変数を取得する簡単な方法ですが、環境変数をカスタマイズしたい場合はどうすればよいですか? ? ?
最初に機能を導入します:システムで指定されている環境変数テーブルputenv
に環境変数を追加します。カスタム環境変数の操作は次のとおりです。environ
execle
環境変数の取得が可能 上記置換関数でも同様に環境変数の取得が可能であり、仮想アドレス空間には環境変数パラメータが存在し、子プロセスはアドレス空間を通じて環境変数を取得することができます。
execle
の引数リストは、mian
関数
main
これは関数でもあり、呼び出してパラメータとして渡す必要があり、execl
プログラムはメモリにロードされるため、プログラムの実行順序は、最初にロードし、次に実行します。
プログラム置換機能の本質はプログラムをメモリにロードすることであり、ロード作業は Linux exec* ローダによって行われます。
機能説明
- 置換関数が正常に呼び出された場合、新しいプログラムがロードされ、リターンせずにスタートアップ コードから実行されます。
- 置換が失敗した場合は -1 を返します
- 置換関数、失敗戻り値のみ、成功戻り値なし