システム情報とプロセス管理

LinuxBeginner
オンラインで実践に進む

はじめに

稼働中の Linux システムは、単なるファイルの集合ではありません。カーネルはハードウェアとプロセスを管理し、各コマンドは識別子を持つプロセスとして実行されます。また、シェルはフォアグラウンドジョブとバックグラウンドジョブを追跡します。管理者はまずシステムの状態を確認し、その後、必要最小限の対象プロセスに対して操作を行います。

この実験では、オペレーティングシステムとカーネルの識別、稼働時間とロードアベレージの解釈、プロセス間の関係の調査、名前によるプロセス検索、終了シグナルの送信、対話的なジョブ制御、終了ステータスの解釈、nohup を使った切り離しコマンドの実行、最近のカーネルメッセージの確認を行います。

システムを識別し、負荷を解釈する

このステップでは、カーネルとディストリビューションを識別し、システムの稼働時間を確認して、ロードアベレージが何を表すのかを学びます。

作業用ディレクトリを作成して移動します。

mkdir -p /home/labex/project/process-lab
cd /home/labex/project/process-lab

uname コマンドはカーネル情報を表示します。-s-r-m オプションは、それぞれカーネル名、カーネルリリース、マシンアーキテクチャを指定します。

uname -srm

uname -a は、利用可能なすべてのフィールドを 1 行で表示します。

uname -a

カーネルと Linux ディストリビューションは同じものではありません。ディストリビューションのメタデータを確認します。

cat /etc/os-release

NAMEVERSIONID などのフィールドを確認してください。次に、稼働時間と負荷を調べます。

uptime

出力には、現在時刻、システムの稼働時間、ログイン中のユーザー数、3 種類のロードアベレージが含まれます。これらの平均値は、およそ 1 分、5 分、15 分の間に実行可能だった処理、または割り込み不能な状態にあった処理の量を示します。パーセンテージではなく、その意味は CPU コア数にも左右されます。

カーネルが持つ簡潔な負荷情報を表示します。

cat /proc/loadavg

最初の 3 つの値は、ロードアベレージに対応します。後続のフィールドには、実行可能なタスク数と、直近に割り当てられたプロセス ID が示されます。

後で参照できるよう、簡単なシステム概要を保存します。

uname -srm > system-summary.txt
uptime >> system-summary.txt
cat system-summary.txt

ファイルには、カーネル情報の行と稼働時間の行が 1 行ずつ含まれているはずです。

プロセスとリソース使用状況を調査する

このステップでは、プロセス識別子、親子関係、状態、およびシステムリソース使用状況のスナップショットを確認します。

プロセスとは、実行中のプログラムです。すべてのプロセスにはプロセス ID(PID)があります。ほとんどのプロセスには親プロセス ID(PPID)もあり、そのプロセスを起動した親プロセスを識別します。

ps コマンドは、プロセスのスナップショットを表示します。必要なフィールドを選び、PID 順に並べます。

cd /home/labex/project/process-lab
ps -eo pid,ppid,user,stat,comm --sort=pid | head -n 15

-e オプションはすべてのプロセスを対象にし、-o オプションは表示する列を定義します。

  • PID はプロセス識別子です。
  • PPID は親プロセスの識別子です。
  • USER はプロセスの所有者です。
  • STAT はプロセスの状態と、付加的なフラグです。
  • COMMAND は実行ファイル名です。

一般的な状態文字には、実行中を示す R、割り込み可能なスリープを示す S、割り込み不能なスリープを示す D、停止中を示す T、ゾンビプロセスを示す Z があります。スリープ中のプロセスは、単に処理すべき作業を待っていることが多いです。

BSD 形式の ps aux は、CPU 使用率やメモリ使用量の列に加えて、完全なコマンドラインを表示します。

ps aux | head -n 10

top は対話的に使用すると、継続的に表示を更新します。バッチモードでは安定したスナップショットを 1 回取得できます。-b はバッチ出力を指定し、-n 1 は 1 回だけ更新するよう指定します。

top -b -n 1 | head -n 12

ヘッダーには、稼働時間、負荷、タスクの状態、CPU 使用率、メモリ使用量の概要が表示されます。その下のプロセス一覧から、リソースを大量に消費しているプロセスを確認できます。

次に、対話形式の表示を開きます。

top

値が更新される様子を確認し、%CPU 列と %MEM 列を見つけたら、q を押してシェルに戻ります。時間の経過に伴う変化を観察する場合は対話形式の top が便利です。出力を保存したり別のコマンドで処理したりする場合は、バッチモードが適しています。

確認結果として、対象を絞ったプロセススナップショットを保存します。

ps -eo pid,ppid,user,stat,comm --sort=pid > process-snapshot.txt
head -n 5 process-snapshot.txt

練習用プロセスを起動して検索する

このステップでは、安全なバックグラウンドプロセスを起動し、その PID を取得して、pspgrep の両方で検索します。

末尾の & は、シェルにコマンドをバックグラウンドで実行させ、すぐにプロンプトを返すための記号です。表示名を labex-worker にした 5 分間の sleep プロセスを起動します。

cd /home/labex/project/process-lab
bash -c 'exec -a labex-worker sleep 300' &

特殊なシェル変数 $! には、直前に起動したバックグラウンドプロセスの PID が格納されます。別のバックグラウンドコマンドを起動する前に保存します。

worker_pid=$!
echo "$worker_pid" > worker.pid

そのプロセスだけを詳しく調べます。-p オプションは PID を指定し、-o オプションは表示するフィールドを選択します。

ps -o pid,ppid,user,stat,etime,args -p "$worker_pid"

ETIME は経過時間を示し、ARGS には表示上のプロセス名が含まれます。完全なコマンドラインを検索するには pgrep -f を使用します。-a を付けると、コマンドラインも表示されます。

pgrep -af labex-worker

pgrep で表示された PID は、worker.pid の内容と一致するはずです。広すぎる名前ですべてのプロセスを対象にするよりも、正確なパターンで検索するほうが安全です。

シグナルでプロセスを停止する

このステップでは、SIGTERM による正常終了を要求し、その要求を無視するよう設計したプロセスに対してのみ SIGKILL を使用します。

kill コマンドは PID にシグナルを送信します。シグナルを明示しない場合は、シグナル 15 である SIGTERM が送信されます。SIGTERM は正常な終了を要求し、プロセスが後処理を行う機会を与えます。

ワーカープロセスの PID を読み込み、SIGTERM を送信します。

cd /home/labex/project/process-lab
worker_pid=$(cat worker.pid)
kill "$worker_pid"
sleep 1

次の ps の失敗によって、プロセスが終了したことを確認できます。

ps -p "$worker_pid" || echo "labex-worker stopped after SIGTERM"

次に、SIGTERM を意図的に無視する制御されたプロセスを起動します。

bash -c 'trap "" TERM; exec -a labex-stubborn sleep 300' &
stubborn_pid=$!
echo "$stubborn_pid" > stubborn.pid

新しいシェルがシグナルハンドラーを設定するまで少し待ちます。

sleep 1

SIGTERM を送信し、少し待ってからプロセスを確認します。

kill -TERM "$stubborn_pid"
sleep 1
ps -o pid,stat,args -p "$stubborn_pid"

この練習用プロセスは SIGTERM を無視するため、まだ存在しているはずです。シグナル 9 である SIGKILL は、捕捉することも無視することもできません。正常終了用のシグナルが効かなかった場合にのみ使用してください。

kill -KILL "$stubborn_pid"

終了したバックグラウンドジョブを回収します。ゼロ以外のステータスが返るのは想定内なので、|| true により練習を続行できます。

wait "$stubborn_pid" 2>/dev/null || true
ps -p "$stubborn_pid" || echo "labex-stubborn required SIGKILL"

SIGKILL を受けたプロセスには、状態を保存したり、アプリケーションのリソースを適切に解放したりする機会がありません。そのため、SIGKILL は最後の手段として使用します。

kill は既知の PID を対象にします。名前や完全なコマンドラインでプロセスを選択する必要がある場合、pkill を使うと照合とシグナル送信をまとめて行えます。特徴的なコマンドラインを持つ制御されたプロセスをもう 1 つ起動します。

bash -c 'exec -a labex-helper sleep 300' &
helper_pid=$!
echo "$helper_pid" > helper.pid

操作する前に、完全なコマンドラインが正確に一致することを確認します。

pgrep -af '^labex-helper 300$'

pkill -f を使って、その正確な一致に SIGTERM を送信します。アンカー ^$ により、練習用パターンが無関係なコマンドラインに一致するのを防ぎます。

pkill -TERM -f '^labex-helper 300$'
wait "$helper_pid" 2>/dev/null || true
ps -p "$helper_pid" || echo "labex-helper stopped by pkill"

pkill では正確なパターンを使用し、最初に pgrep で一致を確認してください。広すぎるパターンは、意図しないプロセスまで停止する可能性があります。

フォアグラウンドジョブとバックグラウンドジョブを制御する

このステップでは、フォアグラウンドコマンドを一時停止し、バックグラウンドで再開し、フォアグラウンドに戻してから中断します。

ジョブ制御は、現在使用している対話型シェルに属します。識別しやすい名前を付けた sleep プロセスをフォアグラウンドで起動します。

cd /home/labex/project/process-lab
bash -c 'exec -a labex-job sleep 300'

これで端末はフォアグラウンドプロセスに占有されます。Ctrl+Z を押してください。シェルが停止シグナルを送信し、プロンプトが戻ります。

このシェルが認識しているジョブを一覧表示します。-l オプションを付けると PID も表示されます。

jobs -l

Stopped 状態のジョブが表示されるはずです。ジョブ番号 1 をバックグラウンドで再開します。

bg %1
jobs -l

状態が Running になり、プロンプトを引き続き使用できるはずです。ジョブを再びフォアグラウンドに戻します。

fg %1

Ctrl+C を押して、フォアグラウンドジョブに割り込みシグナルを送信します。プロンプトが戻ります。練習用ジョブが残っていないことを確認します。

pgrep -af labex-job || echo "No labex-job process remains"

対話的な操作が完了したら、完了マーカーを作成します。

touch job-control.done

ジョブ制御は、現在の端末に接続されたコマンドに使用します。後ほど、端末セッションに依存しない処理を実行するために nohup を使用します。

終了ステータスを確認し、切り離した処理を実行する

このステップでは、コマンドの終了ステータスを解釈し、端末画面から独立して出力を保持できる短いバックグラウンド処理を実行します。

すべてのコマンドは整数のステータスを返します。ゼロは成功を示し、ゼロ以外の値は何らかの失敗を示します。シェル変数 $? には、直前に終了したコマンドのステータスが格納されます。

成功するコマンドを実行し、直後にステータスを表示します。

cd /home/labex/project/process-lab
true
echo "true status: $?"

結果は 0 になります。次に、存在しないパスを指定したコマンドを実行します。

ls missing-path

別のコマンドによって $? が上書きされる前に、ステータスを保存します。

missing_status=$?
echo "missing-path status: $missing_status"

値はゼロ以外になります。想定される 2 種類の解釈を保存します。

printf 'success=0\nfailure=%s\n' "$missing_status" > exit-status.txt

nohup コマンドは、プログラムが端末のハングアップシグナルを無視するようにします。最後の & によってバックグラウンドで起動します。< /dev/null は端末入力を切り離し、> nohup.log 2>&1 は標準出力と標準エラー出力の両方をログに送ります。

nohup bash -c 'for item in one two three; do echo "background: $item"; sleep 1; done' < /dev/null > nohup.log 2>&1 &

バックグラウンドプロセスの PID を保存し、短い処理が完了するまで待ちます。

echo $! > nohup.pid
sleep 4
cat nohup.log

background: one から background: three までの 3 行が表示されるはずです。長時間実行する処理では、保存した PID とログを使って進行状況を確認できます。

最近のカーネルメッセージを確認する

このステップでは、カーネルメッセージバッファを確認し、実行中のカーネルリリースを安定した参照情報として保存します。

カーネルは、起動、ハードウェア、ドライバー、実行時イベントに関するメッセージをリングバッファに記録します。dmesg はそのバッファを読み取ります。通常、管理者権限が必要です。

cd /home/labex/project/process-lab
sudo dmesg | tail -n 10

表示されるメッセージは、マシンや時刻によって異なります。タイムスタンプのようなフィールドと、各メッセージを生成したコンポーネントまたはサブシステムに注目してください。

--level オプションを使うと、重要度で絞り込めます。最近の警告とエラーを表示します。

sudo dmesg --level=err,warn | tail -n 10

出力がない場合でも、コマンドが失敗したとは限りません。現在のバッファに該当レベルのメッセージがない可能性があります。一般的なストレージおよびネットワーク関連の用語を検索します。

sudo dmesg | grep -Ei 'disk|filesystem|network|eth' | tail -n 10

ここでも、結果は現在のシステムによって異なります。uname -r が報告するカーネルリリースを保存します。

uname -r > kernel-version.txt
cat kernel-version.txt

カーネルメッセージは証拠であって、自動的に診断結果を示すものではありません。対処を決める前に、プロセスの状態、サービスログ、実際に観測された症状と組み合わせて判断してください。

まとめ

Linux システムを識別し、稼働時間とロードアベレージを解釈し、プロセス ID、親プロセス、所有者、状態、リソース使用状況のスナップショットを確認しました。名前付きプロセスを起動して検索し、SIGKILL より先に SIGTERM を使用し、フォアグラウンドジョブとバックグラウンドジョブの制御も実践しました。

さらに、終了ステータスを解釈し、nohup と明示的なログ出力を使って切り離した処理を実行し、dmesg でカーネルメッセージを確認しました。このように、まず観測してから操作する習慣は、安全なプロセストラブルシューティングの基礎となり、今後のサービス、ログ、ネットワーク診断にも役立ちます。