Linuxプロセスの基本概念と管理

1. psコマンドによるプロセス情報の表示

Linuxシステムでは、psコマンドを使用してプロセス情報を表示できます。主要なオプションを以下に示します:

  • -e: すべてのプロセスをリスト表示
  • -f: 完全なフォーマットで表示
  • -h: ヘッダーを非表示
  • -l: 長いフォーマットで表示
  • -w: 幅を広げて詳細情報を表示
  • a: すべてのユーザーのプロセスを表示(他ユーザー含む)
  • r: 実行中のプロセスのみ表示
  • x: 制御端末なしのプロセスを表示
  • u: ユーザーフォーマットで出力
  • j: ジョブ制御フォーマットで表示

最も一般的に使用されるのはauxの3つのオプションです。ps auxコマンドの出力例を以下に示します:

USER: プロセスの所有者
PID: プロセスID番号
%CPU: CPU使用率
%MEM: メモリ使用率
VSZ: 仮想メモリサイズ
RSS: 物理メモリサイズ
TTY: 端末デバイス番号
STAT: プロセスの状態
START: プロセス開始時間
TIME: 実行時間
COMMAND: 実行されたコマンド

psコマンドとgrepコマンドを組み合わせることで、特定のプロセス情報のみを表示できます。例えば:

ps aux | head -1 && ps aux | grep myapp | grep -v grep

このコマンドでは、ps aux | head -1でヘッダーを表示し、ps aux | grep myapp | grep -v grepmyappプロセスをフィルタリングし、grep自体のプロセスを除外しています。

2. システムディレクトリによるプロセス情報の確認

ルートディレクトリには/procというシステムディレクトリがあり、これは動的ファイルシステムです。このディレクトリには多くのプロセス情報が含まれており、数字名のディレクトリはプロセスディレクトリです。現在実行中の各プロセスには、PID番号を名前とする対応するディレクトリが/proc下に存在し、プロセス情報を読み取るためのインターフェースとなります。

3. システムコールによるPIDとPPIDの取得

getpid()getppid()システムコールを使用して、現在のプロセスのPIDと親プロセスのPPIDを取得できます。

#include <stdio.h>
#include <unistd.h>

int main() {
    while(1) {
        printf("現在のPID: %d\n", getpid());
        printf("親プロセスPID: %d\n", getppid());
        sleep(1);
    }
    return 0;
}

このプログラムを実行すると、PIDは毎回変わりますが、PPIDは変わらないことがわかります。これは、プロセスがbashによって作成されるためです。bashはコマンドラインインタープリタであり、タスクを受け取って識別し、子プロセスを作成して対応するタスクを実行させます。

4. forkによる子プロセスの作成

fork()はシステムコールレベルの関数で、子プロセスを作成する機能を持ちます。fork関数は、呼び出し元のプロセス(親プロセス)に対して子プロセスのPIDを返し、子プロセスに対しては0を返します。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main() {
    pid_t pid = fork();
    
    if (pid < 0) {
        fprintf(stderr, "fork failed\n");
        return 1;
    } else if (pid == 0) {
        // 子プロセス
        printf("子プロセス: PID=%d, PPID=%d\n", getpid(), getppid());
    } else {
        // 親プロセス
        printf("親プロセス: PID=%d, 子プロセスPID=%d\n", getpid(), pid);
    }
    
    return 0;
}

fork関数は、呼び出し前のコードを親プロセスが実行し、呼び出し後のコードはデフォルトで親子プロセス両方が実行します。しかし、forkの戻り値が異なるため、if文を使用して親子プロセスを分岐させ、異なる機能を実現できます。

5. プロセスの状態

プロセスを監視・制御するためには、現在のプロセスの実行状態を理解する必要があります。Linuxカーネルでは以下の状態が定義されています:

  • R (running): 実行可能状態
  • S (sleeping): 中断可能なスリープ状態
  • D (disk sleep): 中断不可能なスリープ状態
  • T/t (stopped/tracing): 停止状態またはトレース状態
  • X (dead): 死亡状態
  • Z (zombie): ゾンビ状態

各状態の意味を以下に説明します:

R - 実行可能状態

この状態のプロセスのみがCPU上で実行可能です。プロセスのPCB(プロセス制御ブロック)は対応するCPUの実行キューに配置されます。プロセススケジューラは、各CPUの実行キューからプロセスを選択して実行します。この状態にあるプロセスは必ずしも実行中であるとは限りませんが、実行中または実行キューで待機中であることを示します。

S - 中断可能なスリープ状態

この状態のプロセスは、ソケット接続の待機やセマフォの待機など、何らかのイベントの発生を待って保留状態になります。これらのプロセスのPCBは対応するイベントの待機キューに配置されます。イベントが発生すると(外部割り込みまたは他のプロセスによってトリガー)、待機キューのプロセスが1つ以上喚起されます。

D - 中断不可能なスリープ状態

この状態のプロセスは、OSでさえもkillできません。プロセス自身が自動的に喚起しない限り、この状態から復帰しません。通常、I/O操作の完了を待っている間にこの状態になります。

T/t - 停止状態とトレース状態

SIGSTOPシグナルを送信することでプロセスを停止(T)できます。この停止されたプロセスはSIGCONTシグナルで再開できます。t状態はgdbデバッグ時に発生します。

X - 死亡状態

死亡状態は単なる戻り状態であり、プロセスの終了情報が読み取られると、プロセスが要求したリソースがすぐに解放され、プロセスは存在しなくなります。

Z - ゾンビ状態

プロセスが終了し、親プロセスがその終了ステータスを読み取っていない場合に発生します。ゾンビプロセスは、終了情報を親プロセスが読み取るまでプロセステーブルに残り続けます。

6. プロセスの終了

プロセスを終了させるにはkillコマンドを使用します。デフォルトではSIGTERM(シグナル15)が送信されますが、-9オプションでSIGKILLを送信することもできます。

# シグナル15(デフォルト)で終了
kill PID

# 強制終了
kill -9 PID

シグナル15は「優雅な終了」であり、プロセスは終了前にリソース解放などの準備を行うことができます。シグナル9は強制的な終了であり、プロセスは準備を行う時間が与えられません。そのため、データ損失などの副作用が発生する可能性があります。

7. ゾンビプロセスの問題

ゾンビプロセスは、終了したが親プロセスがその終了ステータスを読み取っていないプロセスです。ゾンビプロセスはPCBが保持され続け、メモリリソースが解放されないため、多くのゾンビプロセスが存在するとメモリリークが発生します。

ゾンビプロセスを回避するには、親プロセスが子プロセスの終了ステータスを適切に読み取るか、wait()waitpid()システムコールを使用して子プロセスを待機する必要があります。

8. 孤児プロセス

親プロセスが終了したが、子プロセスがまだ実行中の場合、子プロセスはinitプロセス(PID 1)の子供になります。このようなプロセスを孤児プロセスと呼びます。initプロセスは孤児プロセスの終了ステータスを適切に処理するため、ゾンビプロセスの問題は発生しません。

タグ: linux プロセス管理 fork システムコール プロセス状態

5月17日 04:54 投稿