はじめに
稼働中の Linux システムは、ファイルだけで構成されているわけではありません。カーネルはハードウェアとプロセスを管理し、各コマンドは識別子を持つプロセスとして実行されます。また、シェルはフォアグラウンドジョブとバックグラウンドジョブを追跡します。管理者はまずこの状態を確認し、その後、必要最小限のプロセスに対して操作を行います。
この実験では、オペレーティングシステムとカーネルの識別、uptime とロードアベレージの解釈、プロセス間の関係の調査、名前によるプロセス検索、終了シグナルの送信、対話型ジョブ制御、終了ステータスの解釈、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
NAME、VERSION、ID などのフィールドを確認してください。次に、稼働時間と負荷を確認します。
uptime
出力には、現在時刻、システムの稼働時間、ログイン中のユーザー数、3 つのロードアベレージが含まれます。これらの平均値は、およそ 1 分、5 分、15 分の間に実行可能だった処理、または割り込み不可能な状態にあった処理の量を表します。パーセンテージではなく、その意味は CPU コア数にも左右されます。
カーネルが保持する簡潔な負荷情報を表示します。
cat /proc/loadavg
最初の 3 つの値は、ロードアベレージの考え方に対応します。後続のフィールドには、実行可能なタスク数と、直近に割り当てられたプロセス ID が表示されます。
後で参照できるように、簡単なシステム概要を保存します。
uname -srm > system-summary.txt
uptime >> system-summary.txt
cat system-summary.txt
ファイルには、カーネル情報の行と uptime 情報の行が 1 行ずつ含まれているはずです。
プロセスとリソースの活動を調査する
このステップでは、プロセス ID、親子関係、状態、システムリソースの活動状況をスナップショットで確認します。
プロセスとは、実行中のプログラムです。すべてのプロセスには、プロセス 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 を取得して、ps と pgrep の両方で検索します。
末尾の & は、コマンドをバックグラウンドで実行し、すぐにプロンプトを返すようシェルに指示します。表示名を 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 オプションを付けると、プロセス ID も表示されます。
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 を使用します。
終了ステータスを読み取り、切り離した処理を実行する
このステップでは、コマンドの終了ステータスを解釈し、ターミナル画面から独立して出力を保持する短いバックグラウンドタスクを実行します。
すべてのコマンドは整数のステータスを返します。0 は成功を表し、ゼロ以外の値は何らかの失敗を表します。シェル変数 $? には、直前に終了したコマンドのステータスが格納されます。
成功するコマンドを実行し、直後にそのステータスを表示します。
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 システムを識別し、uptime とロードアベレージを解釈しました。また、スナップショットツールと対話型ツールの両方を使って、プロセス ID、親プロセス、所有者、状態、リソース活動を調査しました。名前付きプロセスを起動して検索し、kill と pkill で正確に対象を指定し、SIGKILL より先に SIGTERM を使用しました。さらに、フォアグラウンドジョブとバックグラウンドジョブの制御も実践しました。
終了ステータスを解釈し、nohup と明示的なログ出力を使って切り離した処理を実行し、dmesg でカーネルメッセージを調査しました。まず状態を観察するこれらの習慣は、安全なプロセストラブルシューティングの基礎となり、このコース後半で扱うサービス、ログ、ネットワーク診断への準備になります。



