目次
6. システムコール - フォークを通じてプロセスを作成します
1. フォン・ノイマン建築
ノートブックなどの一般的なコンピューター。サーバーなどの一般的なコンピューターのほとんどは、フォン ノイマン システムに従っています。
以下は、フォン ノイマン アーキテクチャの図です。
私たちが知っているコンピューターは、入力デバイス、メモリ、演算装置、コントローラー、出力デバイスで構成されています。
- 入力ユニット: キーボード、マウス、スキャナ、タブレット、ネットワークカード、ディスクなどを含む;
- 中央処理装置 (CPU): 計算機やコントローラーなどが含まれます。
- 出力装置:ディスプレイ、ネットワークカード、プリンターなど
フォン・ノイマンについては、いくつかの点を強調しておく必要があります。
- ここでのストレージとはメモリを指します。
- キャッシュの状況に関係なく、ここでの CPU はメモリの読み取りと書き込みのみが可能であり、周辺機器 (入力デバイスまたは出力デバイス) にはアクセスできません。
- 周辺機器 (入力または出力デバイス) は、データの入力または出力を行う場合にのみ、メモリへの書き込みまたはメモリからの読み取りが可能です。
- つまり、すべてのデバイスはメモリを直接処理することしかできません。
まずデータをディスクからメモリにロードし、次に CPU によって読み取られて計算され、計算結果を再度メモリにロードし、最後にデータをメモリからディスクに書き込み、データを私たちに渡す必要があります。出力デバイス。
CPU が周辺機器に直接アクセスできないのはなぜですか?
入出力デバイスは周辺機器と呼ばれるため、一般にディスクなどの周辺機器はメモリに比べて速度が非常に遅いですが、CPU の計算速度は確かに非常に高速です。ディスクからの読み取り速度は非常に遅いのですが、CPU の計算速度は非常に速いようなものですが、全体の速度は依然としてディスクの読み取り速度によって支配されているため、全体の効率は外部によって支配されます。
フォン ノイマンの理解は概念にとどまらず、ソフトウェアのデータ フローを深く理解する必要があります。QQ にログインして友人とチャットを開始した瞬間から、データ フローのプロセスを説明してください。ウィンドウを開いたときからメッセージの送信を開始し、メッセージを受信した後のデータ フロー プロセスまでを開始します。ファイルが qq で送信された場合はどうなりますか?
まずキーボード から情報を読み取ってメモリにロードし、一連の操作を通じてメモリから出力デバイス(ネットワーク カード)にデータを送信し、次にそのデータを友人の入力デバイス(ネットワーク カード)に送信します。一連のネットワーク操作では、友人のコンピュータが入力装置からメモリにデータを読み出し、出力装置(ディスプレイ)を介して友人のコンピュータに情報を送信します。
2. オペレーティングシステム
1.コンセプト
どのコンピュータ システムにも、オペレーティング システム (OS) と呼ばれる基本的なプログラムの集合が含まれています。一般に、オペレーティング システムには次のものが含まれます。
- カーネル (プロセス管理、メモリ管理、ファイル管理、ドライバー管理);
- その他のプログラム (関数ライブラリ、シェル プログラムなど)。
オペレーティング システムは、ハードウェアとソフトウェアのリソースを管理するソフトウェアです。オペレーティング システムがソフトウェアとハードウェアを管理するのはなぜですか?
なぜなら、オペレーティング システムはソフトウェアとハードウェアのリソースを適切に管理する必要があり、ユーザーに優れた (安全、安定、効率的、豊富な機能など) 実行環境を提供する必要があるからです。
オペレーティング システム管理の本質:まず説明し、次に整理します。
- 説明:構造体を通じてさまざまなデータを記述します。
- 組織:リンクされたリストなどの効率的なデータ構造を通じてデータを整理および管理します。
コンピュータでは、オペレーティング システムはマネージャーに相当し、ハードウェア ドライバーは実行者に相当し、ソフトウェアは管理対象に相当します。
まず第一に、オペレーティング システムは誰も信頼しません. 私たちが銀行ユーザーであるのと同じように、お金を節約するためによく銀行に行きますが、銀行は私たちを信頼していますか? 一部のユーザーによる悪意のある破壊やオペレーティング システムへの損害を避けるために、オペレーティング システムはすべての機能を公開するのではなく、システム コールを使用してオペレーティング システムにアクセスします。システム コールを使用するとコストが高くなる可能性があるため、一部の人々はこれに基づいて二次ソフトウェア開発を実行し、その結果、グラフィカル インターフェイス 、シェル、およびツール セットが誕生しました。
システムコールとライブラリ関数
- 開発の観点から見ると、オペレーティング システムは全体として外部に現れますが、上位レベルの開発のためにそのインターフェイスの一部が公開されます。オペレーティング システムが提供するインターフェイスのこの部分はシステム コールと呼ばれます。
- システムコールの使用において、機能は比較的基本的であり、ユーザーの要件は比較的高いため、興味のある開発者は、いくつかのシステムコールを適切にカプセル化してライブラリを形成することができます。ライブラリを使用すると、より高いレベルのユーザーにとって非常に有益ですまたは開発者が二次開発を実施します。
2. OS設計の目的
- ハードウェアと対話し、すべてのハードウェアとソフトウェアのリソースを管理します。
- ユーザープログラム(アプリケーション)に良好な実行環境を提供します。
コンピュータシステムの構造は階層化されており、通常、特定の層をスキップすることはできません
3. プロセス
1. 基本的な考え方
- 教科書的な概念: プログラムの実行インスタンス、実行されるプログラムなど。
- カーネルの観点: システム リソース (CPU 時間、メモリ) を割り当てるエンティティとして機能します。
タスク マネージャーを開くと、これらの実行可能ファイルがすべてプロセスであることがわかります。
2. プロセスの説明 - PCB
- プロセス情報は、プロセス制御ブロックと呼ばれるデータ構造に配置されます。プロセス制御ブロックは、プロセス属性の集合として理解できます。
- 教科書ではPCB (プロセス コントロール ブロック) と呼ばれており、Linux オペレーティング システムでの PCB は次のとおりです。
task_struct-PCB のタイプ
- Linux でプロセスを記述する構造体は task_struct と呼ばれます。
- task_struct は、RAM (メモリ) にロードされ、プロセス情報を含む Linux カーネルのデータ構造です。
オペレーティング システムが最初にプロセスを記述し、それを編成するとき、最初にプログラムの共通属性を持つ構造を作成し、次に各プロセスの構造オブジェクトを作成します。これが最初に記述されたプロセスです。次に、オペレーティング システムは、特徴的なデータ構造 (リンク リストなど) を使用して構造オブジェクトを整理します。これが整理後のプロセスです。その後、オペレーティング システムのプロセス管理が特定のデータ構造の管理に変換されます。
したがって、プロセス = プロセスに関連するカーネル データ構造 + 現在のプロセスのコードとデータになります。
task_ 構造体の内容分類
- 識別子: 他のプロセスを区別するために使用される、このプロセスの一意の識別子を記述します。
- ステータス: タスクのステータス、終了コード、終了信号など。
- 優先度: 他のプロセスとの相対的な優先度。
- プログラム カウンタ: プログラム内で実行される次の命令のアドレス。
- メモリ ポインタ: プログラム コードおよびプロセス関連データへのポインタ、および他のプロセスと共有されるメモリ ブロックへのポインタを含みます。
- コンテキストデータ: プロセスが実行されるときのプロセッサのレジスタ内のデータ [学習の一時停止、CPU のピクチャ、レジスタの追加の例]。
- I/O ステータス情報: 表示された I/O リクエスト、プロセスに割り当てられた I/O デバイス、プロセスで使用されるファイルのリストなど。
- 請求情報: プロセッサー時間の合計、使用されたクロックの合計、制限時間、請求先アカウント番号などが含まれる場合があります。
- その他の情報。
3. 組織化プロセス
これはカーネルのソース コードにあります。システム内で実行されているすべてのプロセスは、task_struct リンク リストの形式でカーネルに保存されます。
4. プロセスを表示して終了する
プロセスの基本情報を表示するには、コマンド ps -axj を使用して、現在のシステムで使用されているプロセス情報を一覧表示します。
まずコードの一部をテストします。
#include<stdio.h>
#include<unistd.h>
int main()
{
while(1)
{
printf("我是一个进程!\n");
sleep(1);
}
return 0;
}
次のようにコマンドを入力してプロセスを表示します。
ps -axj | head -1 && ps -axj | grep "test" と入力して、ヘッダーと test によるプロセスを取得します。
ここでは、オペレーティング システムのプロセス識別番号、つまりプロセス識別子を 指す PID と PPID の概念についても説明する必要があります。オペレーティング システムでプログラムが開かれるたびに、プロセス ID (PID) が作成されます。もちろん、PPIDは親プロセスのプロセスID番号です。
プロセスを強制終了する
方法は2つあり、1つ目はCtrl+cでプロセスを強制終了する方法、2つ目はコマンド形式でkill -9 PIDでkill対象プロセスを指定する方法です。
ここでは 2 番目の方法を使用することをお勧めします。
5. システムコールを通じてプロセス識別子を取得する
- プロセス ID (PID);
- 親プロセス ID (PPID)。
#include<iostream>
#include<sys/types.h>
#include<unistd.h>
using namespace std;
int main()
{
pid_t t = fork();
if (t == 0)
{
while (1)
{
cout << "我是一个子进程" << " pid:" << getpid() << " ppid:" << getppid() << endl;
sleep(1);
}
}
else if (t > 0)
{
while (1)
{
cout << "我是一个父进程" << " pid:" << getpid() << " ppid:" << getppid() << endl;
sleep(1);
}
}
return 0;
}
6. システムコール - フォークを通じてプロセスを作成します
- フォークを知るためにマンフォークを実行します。
- fork には 2 つの戻り値があります。
- 親子プロセスのコード共有。それぞれがデータ用のスペース、プライベート コピー (コピーオンライトを使用) を開きます。
- fork の後の if は通常、分岐に使用されます。
fork() 関数は非常に特殊であることがわかります。子プロセスの作成に成功すると、実際には 2 つの戻り値があります。子プロセスの pid を親プロセスに返し、子プロセスに 0 を返し、そして - を返します。作成が失敗した場合は 1。
2 つの戻り値を理解する方法
fork() が値を返したいとき、実は関数内で子プロセスを作成する作業が完了しているのですが、この時点ではすでにプロセスが 2 つあり、その 2 つのプロセスは引き続き次のステートメントを実行します。 fork() を実行すると、当然戻り値があるので、戻り値が 2 つあるように見えますが、実際には、戻り値を受け取った時点で、コピー オン ライトがトリガーされています (現実的なコピーは、動作中のコピーです)システムは、子プロセスに書き込み操作があることを検出します。オペレーティング システムは、対応する物理スペースを子プロセスに割り当てます)、一見同一の ret は、実際には異なるスペースに格納されます。
7. プロセスステータス
- R 実行ステータス (実行中): プロセスが実行中である必要があるという意味ではなく、プロセスが実行中であるか、実行キュー内にあることを示します。
- S スリープ状態 (スリープ): プロセスがイベントの完了を待っていることを意味します (ここでのスリープは、割り込み可能なスリープ (割り込み可能なスリープ) と呼ばれることもあります)。
- D ディスク スリープ状態 (ディスク スリープ) は、無中断スリープ状態 (無中断スリープ) と呼ばれることもあります。この状態のプロセスは通常、IO の終了を待ちます。
- T 停止状態 (停止): プロセスは、SIGSTOP シグナルをプロセスに送信することによって停止できます (T)。一時停止されたプロセスは、SIGCONT シグナルを送信することで再開できます。
- X デッド状態 (デッド): この状態は単なる復帰状態であり、タスク リストにはこの状態は表示されません。
ps -axj コマンドは、プロセスの情報を表示します。
(1) R 実行ステータス (実行中): プロセスが実行中である必要があるという意味ではなく、プロセスが実行中であるか実行キュー内にあるかを示します。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
while (1)
{
;
}
return 0;
}
実行中に、プロセスのステータスが R+ であり、実行中であることを確認します。
後ろの + 記号は何を意味しますか?
ここで + はプロセスがフォアグラウンドで実行中であることを意味します ctrl+c を使用するとプロセスを終了できます + を書かない場合はプロセスがバックグラウンドで実行されていることを意味します このとき ctrl+cプログラムを終了できません。コマンド kill process を使用して終了してください。
(2) S スリープ状態 (スリープ): プロセスがイベントの完了を待っていることを意味します (ここでのスリープは、割り込み可能なスリープ (割り込み可能なスリープ) と呼ばれることもあります)。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
int n = 0;
scanf("%d", &n);
return 0;
}
実行中、プロセスのステータスが S+ (スリープ状態であることを示す) であることを確認します。
(3) T stop 状態 (stopped): (T) プロセスは、SIGSTOP シグナルをプロセスに送信することで停止できます。一時停止されたプロセスは、SIGCONT シグナルを送信することで再開できます。
kill -l: すべてのシグナルを表示できます。SIGSTOP シグナルは 19、SIGCONT シグナルは 18 に対応します。
kill -19 PID を入力すると、プロセスが停止します。
kill -18 PID を入力すると、プロセスが復元されます。
(4) 死亡状態: この状態は単なる復帰状態であり、瞬間的なものであり、プロセスが死亡するとゾンビ プロセスになるため、タスク リストにはこの状態が表示されない場合があります。
(5) ゾンビ状態: プロセスが終了した後の状態 プロセスが終了するとゾンビ状態になり、親プロセスがリサイクルされないと常にリソースを占有し、メモリ リークが発生します。
8.特殊加工
8.1 ゾンビプロセス
- ゾンビ状態 (Zombies) は特別な状態です。ゾンビ プロセスは、プロセスが終了し、親プロセス (wait() システム コールを使用) が子プロセスの終了からの戻りコードを読み取らない場合に作成されます。
- ゾンビプロセスは終了状態でプロセステーブルに残り、親プロセスが終了ステータスコードを読み取るのを待ち続けます。
- 子プロセスが終了する限り、親プロセスは引き続き実行されますが、親プロセスは子プロセスの状態を読み取らず、子プロセスは Z 状態に入ります。
ゾンビ プロセスを示してみましょう。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
pid_t id = fork();
if (id == 0)
{
//子进程
while (1)
{
printf("我是子进程,我的pid :%d,我的ppid: %d\n", getpid(), getppid());
sleep(1);
}
}
else if (id > 0)
{
//父进程
while (1)
{
printf("我是父进程,我的pid :%d,我的ppid: %d\n", getpid(), getppid());
sleep(1);
}
}
else
{
perror("fail");
exit(-1);
}
return 0;
}
実行時に、kill -9 PID を使用して途中の子プロセスを強制終了します。このとき、子プロセスはゾンビ プロセスになります。
8.2 孤立したプロセス
- 親プロセスが早期に終了し、子プロセスが後で終了して Z に入った場合、どうすればよいでしょうか?
- 親プロセスが最初に終了し、子プロセスは「孤立プロセス」と呼ばれます。
- 1 番の init プロセスにはオーファンプロセスが採用されており、当然 init プロセスは再利用する必要があります。
次のコードを見ると、親プロセスが 3 秒間実行されてから終了しますが、この時点では子プロセスは孤立プロセスになっています。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t id = fork();
if (id < 0)
{
perror("fail");
return 1;
}
else if (id == 0)
{
//子进程
printf("I am child, pid : %d\n", getpid());
sleep(10);
}
else
{
//父进程
printf("I am parent, pid: %d\n", getpid());
sleep(3);
exit(-1);
}
return 0;
}
実行結果の比較:
この記事に不備がある場合は、以下にコメントしてください。できるだけ早く修正します。
古いアイアン、好きになることと注目することを忘れないでください!