オペレーティング システム: 3 つの簡単な要素。 パート XNUMX: プロセス API (翻訳)

オペレーティング システムの概要

おい、ハブル! 私の意見では、興味深い文献の XNUMX つである OSTEP の翻訳シリーズの記事を紹介したいと思います。 この資料では、UNIX に似たオペレーティング システムの動作、つまり、最新の OS を構成するプロセス、さまざまなスケジューラ、メモリ、およびその他の同様のコンポーネントの動作について非常に詳しく説明します。 すべての資料のオリジナルはこちらからご覧いただけます ここで。 翻訳は専門的ではなく(まったく自由に)行われたことに注意してください。しかし、一般的な意味を保持していれば幸いです。

このテーマに関するラボの作業は、次の場所にあります。

その他の部品

私のチャンネルもチェックできます 電報 =)

警報! この講義にはラボがあります! 見て ギットハブ

プロセスAPI

UNIX システムでプロセスを作成する例を見てみましょう。 XNUMX つのシステム コールを通じて発生します フォーク() и exec().

コールフォーク()

オペレーティング システム: 3 つの簡単な要素。 パート XNUMX: プロセス API (翻訳)

fork() 呼び出しを行うプログラムを考えてみましょう。 実行結果は以下のようになります。

オペレーティング システム: 3 つの簡単な要素。 パート XNUMX: プロセス API (翻訳)

まず、main() 関数を入力し、文字列を画面に出力します。 この行には、元のプロセスで呼び出されるプロセス識別子が含まれています。 PID またはプロセス識別子。 この識別子は、UNIX でプロセスを参照するために使用されます。 次のコマンドは fork() を呼び出します。 この時点で、プロセスのほぼ正確なコピーが作成されます。 OS の場合、システム上で同じプログラムの 2 つのコピーが実行されているように見えます。その結果、fork() 関数が終了します。 新しく作成された子プロセス (作成した親プロセスとの関係で) は、main() 関数から実行されなくなります。 子プロセスは親プロセスの正確なコピーではなく、特に、独自のアドレス空間、独自のレジスタ、実行可能命令への独自のポインタなどを持っていることに注意してください。 したがって、fork() 関数の呼び出し元に返される値は異なります。 特に、親プロセスは子プロセスの PID 値を戻り値として受け取り、子プロセスは 0 に等しい値を受け取ります。これらの戻りコードを使用すると、プロセスを分離し、それぞれに独自の作業を実行させることができます。 。 ただし、このプログラムの実行は厳密に定義されていません。 2 つのプロセスに分割した後、OS はプロセスの監視と作業の計画を開始します。 シングルコア プロセッサで実行される場合、プロセスの XNUMX つ (この場合は親プロセス) が引き続き動作し、子プロセスが制御を受け取ります。 再起動すると状況が異なる場合があります。

wait() を呼び出す

オペレーティング システム: 3 つの簡単な要素。 パート XNUMX: プロセス API (翻訳)

次のプログラムを考えてみましょう。 この番組では、通話の存在により、 待つ() 親プロセスは常に子プロセスが完了するまで待機します。 この場合、厳密に定義されたテキストが画面上に出力されます。

オペレーティング システム: 3 つの簡単な要素。 パート XNUMX: プロセス API (翻訳)

exec() 呼び出し

オペレーティング システム: 3 つの簡単な要素。 パート XNUMX: プロセス API (翻訳)

挑戦を検討してください exec()。 このシステムコールは、まったく異なるプログラムを実行したい場合に便利です。 ここで電話します execvp() ワードカウントプログラムであるwcプログラムを実行します。 exec() が呼び出されると何が起こるでしょうか? この呼び出しには、実行可能ファイルの名前といくつかのパラメーターが引数として渡されます。 その後、この実行可能ファイルのコードと静的データがロードされ、コードを含む独自のセグメントが上書きされます。 スタックやヒープなどの残りのメモリ領域は再初期化されます。 その後、OS は一連の引数を渡してプログラムを実行するだけです。 したがって、新しいプロセスを作成するのではなく、現在実行中のプログラムを別の実行中のプログラムに変換するだけでした。 子孫で exec() 呼び出しを実行すると、元のプログラムがまったく実行されていないかのように見えます。

この起動の複雑さは Unix シェルではまったく正常であり、そのシェルは呼び出し後にコードを実行できます。 フォーク()ただし、電話をかける前に exec()。 このようなコードの例としては、プログラムを起動する前に、起動するプログラムのニーズに合わせてシェル環境を調整することが挙げられます。

シェル(Shell) - 単なるユーザープログラム。 彼女はあなたに招待状を見せ、あなたがそこに何かを書くのを待ちます。 ほとんどの場合、そこにプログラムの名前を書き込むと、シェルはその場所を見つけて fork() メソッドを呼び出し、次に何らかのタイプの exec() を呼び出して新しいプロセスを作成し、それが完了するのを待ちます。 wait() 呼び出し。 子プロセスが終了すると、シェルは wait() 呼び出しから戻り、プロンプトを再度出力して、次のコマンドが入力されるのを待ちます。

fork() と exec() の分割により、シェルは次のことを実行できるようになります。
wc ファイル > new_file.

この例では、wc プログラムの出力がファイルにリダイレクトされます。 シェルがこれを実現する方法は非常に簡単です。呼び出す前に子プロセスを作成します。 exec()、シェルは標準出力を閉じてファイルを開きます 新しいファイルしたがって、さらに実行中のプログラムからのすべての出力 wc 画面ではなくファイルにリダイレクトされます。

Unixパイプ これらは同様の方法で実装されますが、pipe() 呼び出しを使用する点が異なります。 この場合、プロセスの出力ストリームはカーネル内にあるパイプ キューに接続され、そこに別のプロセスの入力ストリームが接続されます。

出所: habr.com

コメントを追加します