目次
Linuxプロセス
参考:
-
プロセスの切り替えと「Linuxオペレーティングシステム」の制御にはどのような関係があるのでしょうか?- Zhihu (zhihu.com)、Linux process Analysis_deep_explore のブログ - CSDN ブログ、Tencent インタビュー: プロセスのデータ構造 - Zhihu (zhihu.com)、Linux でマルチプロセス プログラミングを実行する方法 (暫定版) - Zhihu (zhihu. com)、インタビュアーを徹底的に理解する、Linux プロセスの詳細 - Zhihu (zhihu.com)、オペレーティング システム プロセスの概念、プロセスのステータスと状態遷移、プロセス制御 Cheng Xiaozhi のブログ - CSDN ブログ プロセスの状態とその遷移。
-
Linux プロセスの詳細な説明 - Programmers Base Camp (pianshen.com)。Linux プロセスの基本チュートリアルは、Linux Script Home (jb51.net) で詳しく説明されています。
-
[Linux] Linux プロセスの作成と管理 Yngz_Miao のブログ - CSDN ブログ Linux 作成プロセス。
-
『Linux System Prorgrammin』、Linux System Programming _ 中国語版 _ by _ ハルビン工業大学 (翻訳) - 第 5 章 - プロセス管理。
-
Linux オペレーティング システム用の C 言語プログラミングの入門。
プロセスの基本概念
プログラムとプロセス
平たく言えば、プログラムは実行可能コードを含むファイルであり、静的ファイルです。プロセスは、実行を開始したがまだ終了していないプログラムのインスタンスです。これは、実行可能ファイルの特定の実装です。プログラムには多数のプロセスが含まれる場合があり、各プロセスには多数の子プロセスが含まれる場合があります。
プロセスステータス
作成状態: プロセスは作成中であり、まだ準備完了状態に移行していません。
準備完了状態: プロセスはプロセッサを除くすべての必要なリソースを取得しており、プロセッサを取得するとすぐに実行できます。
ステータスの特性: プロセッサー (またはスケジューラー) リソース: プロセッサーのみが欠落しています。リソースの取得: 必要なリソースが取得されました。プロセッサを取得したら、すぐに実行します。
状態遷移: 準備完了状態 -> 実行状態: 準備完了状態のプロセスがスケジュールされた後、プロセッサ リソースを取得するため、プロセスは準備完了状態から実行状態に切り替わります。
実行状態: プロセスはプロセッサ上で実行されています。単一プロセッサの場合、同時に 1 つのプロセスのみが実行状態になります。
状態遷移: 実行状態 -> 準備完了状態: ケース 1: タイム スライスが使い果たされた後、実行状態のプロセスはプロセッサを放棄し、準備完了状態に移行する必要があります。ケース 2: 剥奪可能なオペレーティング システムでは、優先度の高いプロセスの準備が整うと、スケジューラは実行中のプロセスを準備完了状態に変換して、優先度の高いプロセスの実行を許可します。
状態遷移: 実行状態 -> ブロック状態 (アクティブな動作): プロセスが特定のリソース (ペリフェラルなど) の使用を要求するとき、または特定のイベント (I/O 操作の完了など) の発生を待機するとき)、実行状態からブロッキング状態に変わります。プロセスは、システム コールの形式でサービスを提供するようにオペレーティング システムに要求します。システム コールは、ユーザー モード プログラムがオペレーティング システムのカーネル プロセスを呼び出す特別な形式です。
ブロッキング状態: 待機状態とも呼ばれ、プロセスは特定のイベントを待機し、実行/スリープを一時停止します。たとえば、特定のリソースまたは IO が完了するのを待機している場合、プロセッサがアイドル状態であってもプロセスは実行できません。
ステータスの特性: プロセッサ (またはスケジューラ) リソース: 不足している可能性がありますが、不足していない場合もあります。リソースの取得: リソースが利用可能になるのを待っているか、何かが完了するのを待っています。プロセッサを取得する場合: プロセッサがアイドル状態であっても、待機している処理が完了していない場合は実行できません。
状態遷移: ブロック状態 -> 準備完了状態 (受動的な動作、他の関連プロセスからの支援が必要): I/O 操作の終了や割り込みの終了など、プロセスが待機しているイベントが到着すると、割り込みハンドラは、対応するプロセスのステータスをブロック状態遷移から準備完了状態に変更する必要があります。
終了状態: プロセスはシステムから消えています。プロセスが正常に終了したか、他の理由で終了した可能性があります。
Linux カーネルの各プロセス ステータスのフラグに対応します。
TASK_RUNNING
これは、プロセスの準備ができていることを意味します。これは、オペレーティング システムが CPU 上で実行するタイム スライスを割り当てるかどうかによって異なります。プロセスがタイム スライスを取得した場合、それは実行状態になります。タイム スライスを割り当てなかった場合、プロセスは実行状態になります。準備完了状態。ステータスを表すフィールドを変更する必要はありません。
実際、
TASK_RUNING
このフィールドはプロセスの準備完了状態とプロセスの実行状態の両方に対応します。この状態のプロセスのみが CPU 上で実行できます。同時に複数のプロセスが実行可能状態にある可能性があり、これらのプロセスの task_struct 構造体 (プロセス制御ブロック) は、対応する CPU の実行可能キューに入れられます (プロセスは 1 つの CPU の実行可能キューに同時に存在できます)ほとんど)。プロセス スケジューラのタスクは、各 CPU の実行可能キューからプロセスを選択し、その CPU 上で実行することです。
実行可能キューが空でない限り、対応する CPU は遅延させることができず、いずれかのプロセスを実行する必要があります。この時のCPUは一般に「ビジー」と呼ばれます。同様に、CPU の「アイドル」とは、対応する実行可能キューが空であり、CPU が何もすることがないことを意味します。
多くのオペレーティングシステムの教科書では、CPU 上で実行中のプロセスを RUNNING 状態、実行可能だが実行スケジュールがまだ決まっていないプロセスを READY 状態と定義していますが、Linux ではこれら 2 つの状態は TASK_RUNNING 状態に統一されます。
TASK_INTERRUPTIBLE
および はTASK_UNINTERRUPTIBLE
2 つのスリープ状態であり、上記のブロッキング状態に対応します。TASK_INTERRUPTIBLE
再び信号で目覚めることはできますTASK_UNINTERRUPTIBLE
が、信号では目覚めることはできません。
TASK_INTERRUPTIBLE、割り込み可能なスリープ状態。この状態のプロセスは、特定のイベントの発生を待機しているため (ソケット接続の待機、セマフォの待機など)、一時停止されています。これらのプロセスの task_struct 構造体は、対応するイベントの待機キューに入れられます。これらのイベントが発生すると (外部割り込みによってトリガーされるか、他のプロセスによってトリガーされて)、対応する待機キュー内の 1 つ以上のプロセスが起動されます。ps コマンドを使用すると、通常の状況では、プロセス リスト内のプロセスの大部分が TASK_INTERRUPTIBLE 状態にあることがわかります (マシンの負荷が非常に高い場合を除く)。
TASK_UNINTERRUPTIBLE、中断不可能なスリープ状態。TASK_INTERRUPTIBLE 状態と同様に、プロセスはスリープ状態ですが、この時点ではプロセスは中断できません。割り込み不可能とは、CPU が外部ハードウェアからの割り込みに応答しないことを意味するのではなく、プロセスが非同期信号に応答しないことを意味します。つまり、kill -9 はこの種のプロセスをシャットダウン/強制終了できません。TASK_UNINTERRUPTIBLE 状態の重要性は、カーネルの特定の処理フローを中断できないことです。非同期信号に応答すると、プログラムの実行フローに非同期信号を処理する処理が挿入されます(この挿入された処理はカーネルモードのみに存在する場合もあれば、ユーザーモードにまで拡張される場合もあります)ので、元の処理は中断される。
プロセスが特定のハードウェア上で動作するとき (たとえば、プロセスがデバイス ファイルを読み取るために read システム コールを呼び出し、最終的に read システム コールが対応するデバイス ドライバのコードを実行し、対応する物理デバイスと対話する)、 TASK_UNINTERRUPTIBLE 状態を使用してプロセスを保護し、プロセスとデバイス間の対話が中断されてデバイスが制御不能な状態に陥るのを防ぐことが必要です。この場合の TASK_UNINTERRUPTIBLE 状態は常に非常に短期間であり、基本的に ps コマンドでキャプチャすることは不可能です。Linux システムでは簡単にキャプチャできる TASK_UNINTERRUPTIBLE 状態もあります。vfork システムコールの実行後、子プロセスが exit または exec を呼び出すまで、親プロセスは TASK_UNINTERRUPTIBLE 状態になります。
TASK_STOPPED
これは、プロセスが SIGSTOP や SIGTTIN などのシグナルを受信したときの状態であり、Linux プロセスの実行中に Ctrl + z を押すと、プロセスはこの状態になります。
SIGSTOP シグナルをプロセスに送信すると、プロセスはシグナルに応答して TASK_STOPPED 状態に入ります (プロセス自体が TASK_UNINTERRUPTIBLE 状態にあり、シグナルに応答しない場合を除く)。(SIGSTOP は、SIGKILL シグナルと同様、非常に必須です。ユーザー プロセスは、シグナル シリーズのシステム コールを通じて、対応する信号処理関数をリセットすることはできません。)
SIGCONT シグナルをプロセスに送信すると、プロセスを TASK_STOPPED 状態から TASK_RUNNING 状態に復元できます。
TASK_TRACED
監視しているプロセスの状態です。
プロセスがトレースされているとき、プロセスは特別な状態 TASK_TRACED になります。「追跡中」とは、プロセスが一時停止され、追跡しているプロセスがそのプロセスを操作するのを待っていることを意味します。たとえば、gdb で追跡対象のプロセスにブレークポイントを設定した場合、プロセスはブレークポイントで停止すると TASK_TRACED 状態になります。また、追跡されるプロセスが依然として前述の状態にある場合もあります。
TASK_STOPPED、TASK_TRACEDのステータス判定。プロセス自体の場合、TASK_STOPPED 状態と TASK_TRACED 状態は非常に似ており、どちらもプロセスが一時停止されていることを示します。TASK_TRACED 状態は、TASK_STOPPED の上に追加の保護層と同等であり、TASK_TRACED 状態のプロセスは、SIGCONT 信号に応答して起動することはできません。デバッグされたプロセスは、デバッグ プロセスが ptrace システム コール (ptrace システム コールのパラメータで指定された操作) を通じて PTRACE_CONT や PTRACE_DETACH などの操作を実行するか、デバッグ プロセスが終了するまでのみ、TASK_RUNNING 状態に戻ることができます。
TASK_DEAD
-EXIT_ZOMBIE
、終了ステータス、プロセスはゾンビプロセスになります。EXIT_DEAD
これは最終状態です。この状態に入るということは、プロセスがシステムから削除されることを意味します。EXIT_ZOMBIE
これはEXIT_DEAD
前の状態です。この時点では、プロセスは終了していますが、親プロセスはwait()
システム コールがそのプロセスを取得するのをまだ待っていません。終了情報 この状態のプロセスをゾンビプロセスと呼びます。この状態ではkillコマンドでは強制終了できないので、ゾンビプロセスをクリアする方法や、ゾンビプロセスの存在を避ける方法を考えることができます。終了関連のプロセスステータス (上記 4 つ) については、 Linux Process Analysis_deep_explore のブログ - CSDN ブログを参照してください。
Linux プロセスの状態遷移図:
システムプロセスの一般的な STAT コード:
レディキューとブロッキングキュー:
準備完了キュー: システム内には準備完了状態のプロセスが複数存在する可能性があり、通常、それらはキューに入れられます。レディ キューが空でない限り、CPU は常にプロセスの実行をスケジュールし、ビジー状態を維持できます。これはレディ プロセスの数とは関係ありません。レディ キューが空でない限り、CPU は待機状態になり、CPU は待機状態になります。効率が低下します。
ブロックキュー: システムは通常、ブロックされたプロセスをキューに入れ、ブロックのさまざまな理由に基づいて複数のブロックキューを設定することもあります。
プロセス構成
これらのデータ構造は、プロセス - Zhihu (zhihu.com)、Linux の task_struct 構造 - Baidu Library (baidu.com) から引用されています。
このプロセスは通常、次の部分で構成されます。
プロセス制御ブロック (PCB) : 各プロセスが作成されると、システムはプロセスに対応する PCB を作成します。PCB は、プロセスが存在することを示す唯一の指標です。
プロセス作成の本質は、プロセスの PCB を作成することです。PCB は、プロセスの ID と関係の表示、タスクのステータスのマーク、権限のマーク、ヘルプ タスクのスケジュール設定などを行うことができる必要があります。
Linux カーネルでは、プロセスとスレッドはタスクとして統合されており、Linux カーネルのプロセス制御ブロックは次の内容を
task_struct
含む構造です。
プロセスの識別子 (プロセス識別子、または PID) ; (プロセス自体の一意の識別子 PID)
プロセスのレジスタ値(特に、プロセスのプログラム カウンタとスタック ポインタの値を含む)(プログラム カウンタ PC など)
プロセスのアドレス空間。
優先度 (優先度の高いプロセスが最初に優先されます。たとえば、Unix オペレーティング システムの nice 値);(优先级)
プロセスが最後に実行されたとき、プロセスが蓄積した CPU 時間などのプロセス アカウンティング情報。
次の PCB へのポインタ、つまり、次に実行するプロセスの PCB へのポインタ。
I/O 情報 (つまり、このプロセスに割り当てられた I/O デバイス、開かれているファイルのリストなど)。
pid_t pid; // 自身のプロセスの ID を表示 pid_t tgid; // プロセスのメインスレッドの ID struct task_struct *group_leader; // メインスレッドのアドレスを指す各プロセスはメイン スレッドを作成するため、プロセスが 1 つしかなく、デフォルトでプロセスによってメイン スレッドが作成される場合、それ自体がメイン スレッドになり
pid
ますtgid
。プロセスによって作成された子スレッドの場合、pid
それ自体が独自のものでありid
、tgid
プロセスのメイン スレッドの ID を指します。 struct task_struct __rcu * real_parent; struct task_struct __rcu *parent; // 親プロセスを指します struct list_head Children; // 親プロセスのすべての子プロセスは子プロセスのリンク リスト内にあり、これはリンク リストの先頭を指します。 struct list_head sibling; // 兄弟プロセスを接続しますプロセスはツリー状構造(リンクリストで構成されたツリー)になっており、プロセスNo.0を除くすべてのプロセスは親プロセスによって作成されるため、親プロセスに対する操作は子プロセスに影響を与えやすいです。したがって、プロセスのデータ構造は、そのプロセスがどの親プロセス、子プロセス、兄弟プロセスを持つのかを自然に示します。
プログラム セグメント: プログラム セグメントは、プロセス スケジューラによって CPU 上で実行されるようにスケジュールできるプロセス内のプログラム コードのセグメントです。
データセグメント: プログラムに対応するプロセスによって処理された元のデータである場合もあれば、プログラムの実行時に生成される中間結果または結果データである場合もあります。
プロセス制御
プロセス制御の主な機能は、システム内のすべてのプロセスを効率的に管理することであり、新しいプロセスの作成、既存のプロセスのキャンセル、プロセスの状態遷移の実現などの機能を備えています。つまり、プロセス制御とはプロセスの状態遷移を実現することです。
一般的なプロセス制御プログラムセグメントは「アトミック操作」であり、実行プロセス中に中断することはできず、「中断オフ命令」と「中断オン命令」という 2 つの特権命令を使用してアトミック性を実現します。
プロセスの作成と終了
プロセスの作成と終了に関連するプロセス制御の概念。
プロセス作成の原因となるイベント
ユーザーログイン: タイムシェアリングシステムでは、ユーザーが正常にログインすると、システムはそのユーザーのために新しいプロセスを作成します。
ジョブのスケジューリング: マルチチャネルのバッチ処理システムでは、新しいジョブがメモリに置かれると、そのジョブに対して新しいプロセスが作成されます。
サービスの提供: ユーザーがオペレーティング システムに対して特定のリクエストを行うと、そのリクエストを処理するための新しいプロセスが作成されます。プログラムを起動すると、新しいプロセスが作成されます。
アプリケーション要求: ユーザー プロセスは、子プロセスの作成を積極的に要求します。
オペレーティング システムが新しいプロセスを作成するプロセス
ステップ 1: 新しいプロセスに一意のプロセス識別番号を割り当て、空の PCB を申請します (PCB には制限があります)。PCB 申請が失敗した場合、作成は失敗します。
ステップ 2: ファイル、メモリ、I/O デバイス、CPU 時間など、必要なリソースをプロセスに割り当てます。これらのリソースは、オペレーティング システムまたはその親プロセスから取得されます。リソース(メモリなど)が不足している場合、この時点では作成は失敗しませんが、メモリリソースを待っている作成状態となります。
ステップ 3: PCB を初期化します。これには、主に初期化フラグ情報、初期化プロセッサーのステータス情報、初期化プロセッサー制御情報が含まれ、プロセスの優先順位なども設定されます。
ステップ 4: プロセスの準備完了キューが新しいプロセスを受け入れることができる場合は、新しいプロセスを準備完了キューに挿入し、実行がスケジュールされるまで待ちます。
親プロセスが子プロセスを作成する
プロセスは別のプロセスを作成することができ、このとき作成者を親プロセス、作成されたプロセスを子プロセスと呼びます。子プロセスは親プロセスが所有するリソースを継承できます。子プロセスが取り消されると、親プロセスから取得したリソースは親プロセスに返される必要があります。親プロセスが取り消されると、通常はそのすべての子プロセスが取り消されます。同時に。親プロセスと子プロセスは一部のリソースを共有しますが、仮想アドレス空間を共有することはできず、子プロセスの作成時に仮想アドレス空間などのリソースが子プロセスに割り当てられます。
もちろん、親プロセスと子プロセスは同時に実行できます。プロセス制御ブロック (PCB) はプロセスの存在を示す唯一の兆候であり、各プロセスには独自の PCB があります。親プロセスと子プロセスは、同じクリティカルリソースを同時に使用することはできず、一度に 1 つのプロセスのみが使用できます (クリティカルリソースにはロック機構があり、相互にのみアクセスできます)。
Linuxシステム上で作成されるプロセスはすべて既存のプロセスから作成されます(プロセスNo.0を除く)。作成されたプロセスを子プロセス、子プロセスを作成したプロセスを親プロセスと呼びます。Linux プロセスはツリー構造につながっています。
プロセスの終了を引き起こすイベント
正常終了: プロセスのタスクが完了し、終了する準備ができていることを示します。
異常終了: プロセスの実行中に、記憶領域の範囲外、保護エラー、不正な命令、特権命令エラー、実行タイムアウト、算術演算エラー、 I/O障害など
外部介入: オペレーターやオペレーティング システムの介入、親プロセスの要求、親プロセスの終了など、外部の要求によって終了するプロセスを指します。
オペレーティング システムがプロセスを終了するプロセス
ステップ 1: 終了したプロセスの識別子 (PID) に従って、プロセスの PCB を取得し、そこからプロセスのステータスを読み取ります。
ステップ 2: 終了したプロセスが実行状態にある場合は、プロセスの実行を直ちに終了し、プロセッサ リソースを他のプロセスに割り当てる必要があります。
ステップ 3: プロセスに子孫プロセスがある場合、その子孫プロセスをすべて終了する必要があります。
ステップ 4: プロセスが所有するすべてのリソースを親プロセスまたはオペレーティング システムに返します。
ステップ 5: PCB がキューから削除されます。
個々のプロセスを表示および解釈する
Linux システム上のコマンドを使用して、
ps - ef
システムの現在のプロセスを表示できます。
UID はユーザーの識別子です (root ユーザーによって作成されたプロセスの UID は root です。自分で作成する場合は、ユーザー名にする必要があります)。
PID は現在のプロセスの ID を表します。
PPID は、現在のプロセスの親プロセス ID を表します。
プロセスNo.1、プロセスNo.2~プロセスNo.0を作成し、プロセスNo.1によってユーザーモードのプロセスを作成し、プロセスNo.2によってカーネルモードのプロセスを作成し、Linuxのプロセスツリーを生成します。
0号进程
: カーネルの初期化プロセスでは、struct task_struct init_task = INIT_TASK(init_task) 命令によってプロセス番号 0 が最初に作成されます。これは、fork または kernel_thread 経由で生成されなかった唯一のプロセスです。プロセスリストの最初にあります。ただし、このプロセスは実際の意味でのプロセスではなく、リンクされたリストの先頭に似ています。したがって、プロセスNo.0がカーネル状態で生成されたとしても、プロセスNo.0がカーネル状態の最初のプロセスとは言えず、プロセスNo.2がカーネル状態の最初のプロセスであると言わざるを得ません。
1号进程
: カーネルモードからユーザーモードに切り替える命令 kernel_thread(kernel_init, NULL, CLONE_FS) を呼び出して作成され、プロセス No. 1 がすべてのユーザーモードの祖先となります。プロセス 1 は、 init プロセスとも呼ばれます。これは、カーネルの初期化中に作成される 2 番目のカーネル スレッドです。その実行コードは、カーネル関数 init() です。システムが終了しない限り、init プロセスは終了することはなく、オペレーティング システムの外部にあるすべてのプロセスのアクティビティを作成および監視する役割を果たします。
2号进程
: 命令 kernel_thread(kthreadd, NULL, ClONE_FS | CLONE_FILES) を呼び出すことによって作成されます。プロセス No. 2 は、すべてのカーネル状態プロセスのスケジューリングと管理を担当し、カーネル状態のすべてのプロセスの祖先です。(カーネル状態ではスレッドとプロセスが区別されないため、プロセスとスレッドは両方ともタスクであることに注意してください)。
pstree
プロセス ツリー全体を表示するコマンド、 Linux 基本コマンド---プロセス ツリーの表示 pstree_weixin_34023863 のブログ - CSDN ブログ。