Linux基礎 #7 - プロセス

Last Edited: 1/6/2025

このブログ記事では、Linuxにおけるプロセスについてを紹介します。

DevOps

プロセスとは?

一般にプロセスとプログラムを同じものとして使う傾向がありますが、コンピュータサイエンスでは明確に区別されています。 プログラムは特定の命令のセットで構成されたコンパイル済みのコードであり、プロセスはそのプログラムを実行しているインスタンスを指します。 プロセスには、静的変数などのデータセクション、スタック、ヒープが含まれます。C/C++のようなコンパイル型言語でコードを書く場合、 コンパイルされた実行可能ファイルがプログラムであり、CPUやメモリ、ディスクなどのリソースが割り当てられることでプロセスとなります。

Pythonのようなインタープリタ型言語の場合、プログラムはインタープリタの実行ファイルであり、それが実行されることでプログラマが記述したスクリプト(通常はヒープに格納される)を解釈し、 リソースを割り当ててプロセスになります。このため、C/C++ではgccやg++のようなコンパイラでコードをコンパイルし、実行可能ファイルの場所を指定するだけですが、 PythonやNode.jsではpython <script_name>.pynode <script_name>.jsのように実行ファイルとスクリプト名の両方を指定します。

Linuxにおけるプロセス

Linuxでは、psコマンドを使って現在実行中のプロセスを確認できます。プロセスには一意のID(PID)が付与され、制御端末(TTY)の下で実行されています。 CPU使用時間とコマンドは、それぞれTIME列とCMD列に表示されます。

> ps
PID TTY          TIME CMD
195 pts/1    00:00:00 bash
271 pts/1    00:00:00 ps
 
> ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   4300  3428 pts/0    Ss+  Jan02   0:00 /bin/bash
root         8  0.0  0.0   4296  3652 pts/1    Ss   Jan02   0:00 bash
root       194  0.0  0.0   5380  2432 pts/1    S    Jan02   0:00 su - new
new        195  0.0  0.0   5240  4400 pts/1    S    Jan02   0:00 -bash
new        272  0.0  0.0   8020  3380 pts/1    R+   17:19   0:00 ps aux

ps auxを使用すると、他のユーザによるプロセスも含めて、より多くの情報を確認できます。USER列には実効ユーザが表示され、%CPUと%MEMはCPU時間と物理メモリ使用量の割合、 VSZとRSSは仮想メモリ使用量とプロセスが使用する物理メモリ量(バイト単位)が表示されます。STATはプロセスの状態を示しています。(これについては後述します。)

プロセスの生成

Linuxを含むUnix系オペレーティングシステムでは、プロセスは常にforkシステムコールを使って生成されます。このコールは現在のプロセスを複製し、 異なるPIDを持つ親プロセスと子プロセスを作成します。一見すると冗長に思えますが、親プロセスのリソースがすでに設定されているため、 新しいプロセスをゼロから初期化するよりも簡単で柔軟な方法です。子プロセスはこれらのリソースを利用するか、新しいメモリ空間で新しいプログラムを実行するかを選択できます。

> ps l
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
4  1001   195   194  20   0   5240  4396 do_wai S    pts/1      0:00 -bash
0  1001   270   195  20   0   8052  3644 -      R+   pts/1      0:00 ps l

シェルプロセスに新しいプログラムの実行を指示すると、forkシステムコールがシェルプロセスを親プロセスとして扱い、その親プロセスID(PPID)を持つ新しい子プロセスが生成され、 指定されたプログラムを実行します。その後、親プロセスは子プロセスの終了状態を待つ(waitシステムコール)か、同時に自身のプログラムを実行し続けます。 ps lコマンドを使用して、親プロセスID(PPID)を確認できます。例外はinitプロセスであり、これはPIDが1のプロセスで、カーネルが直接作成する最初のプロセスです。 initプロセスはすべてのプロセスの親となります。

プロセスの終了

通常、親プロセスはwaitシステムコールを実行し、子プロセスがexitシステムコールを実行して使用していたリソースを解放するのを待ちます。 しかし、プロセスが終了に失敗する場合があります。その一例が、親プロセスが子プロセスの終了前に死んでしまった場合です。この場合、 子プロセスは孤児プロセスとなり、initプロセスが代わりにwaitを実行します。

もう一つの例は、親プロセスが子プロセスの終了前にwaitを実行できなかった場合です。この場合、子プロセスはゾンビプロセスとなります。 ゾンビプロセスはプロセステーブルに残り続け、親またはinitプロセスがwaitを実行しない限り削除されません。ゾンビプロセスは他のプロセスの実行を妨げる可能性があるため、 注意が必要です。

プロセスの状態

ps auxps lコマンドのSTAT列はプロセスの状態を示します。たとえば、Rはプロセスが実行中または実行可能状態で、CPUの完了を待っていることを示します。 Sはプロセスがスリープ中で、特定のイベントを待っている状態です。Dは割り込み不可能なスリープ状態、Tは停止中のプロセス、Zは先述のゾンビプロセスを示します。 さらに、s+などのサフィックスが付加されることがあります。これらは、プロセスがセッションリーダーであることや、フォアグラウンドプロセスグループに属していることを示します。

PROCESS STATE CODES
       Here are the different values that the s, stat and state output
       specifiers (header "STAT" or "S") will display to describe the state of
       a process:
 
               D    uninterruptible sleep (usually IO)
               R    running or runnable (on run queue)
               S    interruptible sleep (waiting for an event to complete)
               T    stopped by job control signal
               t    stopped by debugger during the tracing
               W    paging (not valid since the 2.6.xx kernel)
               X    dead (should never be seen)
               Z    defunct ("zombie") process, terminated but not reaped by
                    its parent
 
       For BSD formats and when the stat keyword is used, additional
       characters may be displayed:
 
               <    high-priority (not nice to other users)
               N    low-priority (nice to other users)
               L    has pages locked into memory (for real-time and custom IO)
               s    is a session leader
               l    is multi-threaded (using CLONE_THREAD, like NPTL pthreads
                    do)
               +    is in the foreground process group

上記はman psのSTATについての説明です。セッションはプロセスグループやジョブのコレクションであり、一意のセッションID(SID)を持ちます。 このSIDの値は、セッションリーダーのPIDと同じです。セッションは、異なるユーザーやデーモンプロセス(バックグラウンドで実行され、ユーザーの制御を離れるプロセス)のプロセスやジョブを分離するために設定されます。

ジョブ

プロセスグループやジョブは、プロセスをターミナルウィンドウをブロックするフォアグラウンドで実行するか、ターミナルウィンドウをブロックしないバックグラウンドで実行するかを制御するために設定されています。 Linuxでは、コマンドの末尾に&を追加することでプロセスをバックグラウンドジョブとして実行できます。また、Ctrl+Zを使用してプロセスを一時停止し、その後bgコマンドで現在のプロセスをバックグラウンドジョブに移動することも可能です。 バックグラウンドジョブを表示するには、jobsコマンドを使用します。

> sleep 1000 &
> sleep 1001 &
> sleep 1002
^Z
[3]+    Stopped     sleep 1002
 
> bg
[3]+    sleep 1002 &
 
> jobs
[1]   Running                 sleep 1000 &
[2]-  Running                 sleep 1001 &
[3]+  Running                 sleep 1002 &

上は、ジョブに関連するコマンドの使用例を示しています。ジョブリストの+は、バックグラウンドジョブに追加された最新のプロセスを示し、-は2番目に最新のプロセスを示します。 バックグラウンドジョブのプロセスをフォアグラウンドに移動するには、fg %<ジョブ番号>、または最新のジョブの場合は%+、その前のジョブの場合は%-を使用できます。

シグナル

コマンドで何かミスをした場合、Ctrl+Cを押してプロセスを中断することができます。これは、プロセスに割り込みを送るためのSIGINTシグナルのショートカットです。 シグナルは列挙型(enum)のようなもので、それぞれに対応する番号があります。SIGINTの場合、その値は2です。シグナルを使用すると、 プロセスに何らかのイベントが発生したことを通知できます。

  • SIGHUP or HUP or 1: Hangup
  • SIGINT or INT or 2: Interrupt
  • SIGKILL or KILL or 9: Kill
  • SIGSEGV or SEGV or 11: Segmentation fault
  • SIGTERM or TERM or 15: Software termination
  • SIGSTOP or STOP: Stop

上は、ユーザーやプロセスが使用できるシグナルの一覧です。プロセスはほとんどのシグナルに対してどのように対処するかを決定することができるため、 どのシグナルを使用するかは慣例に依存する場合が多いです。SIGHUPは通常、プロセスをバックグラウンドプロセスグループに送るために使用され、 ユーザーが現在のターミナルから切断するたびに送信されます。また、手動で送信することも可能です。一方、SIGINTはプロセスを中断しますが、 バックグラウンドには送信されません。この動作は、jobsコマンドを使用して確認できます。

> kill -<SIGNAL> <PID>

上は、killコマンドを使用してプロセスにシグナルを送信する方法を示しています。シグナルを指定しない場合、デフォルトでSIGTERMシグナルが送信されます。 ただし、送信したいシグナルに対応する番号を指定することで、特定のシグナルを指定されたPIDのプロセスに送信することが可能です。

プロセスの詳細

プロセスの詳細を確認および追跡する方法は他にもあります。例えば、ps mコマンドを使用すると、プロセス内のスレッドを確認できます。 スレッドは軽量で、同じメモリを共有し、プロセス内での通信が容易であり、切り替えが高速であるという点でプロセスとは異なります。 また、Linuxではすべてがファイルとして扱われるため、cat /proc/<PID>/statusを使用して、プロセスの詳細が保存されているファイルを確認することもできます。

topコマンドは、10秒ごとにプロセスとリソースの使用状況を追跡します。このツールは非常に便利で、psで確認できるすべての情報に加え、 ロードアベレージ(1分、5分、15分間でCPU時間を待機しているプロセスの数)、CPU使用率、メモリ使用状況、優先度(nicenessレベル)などの統計情報を確認できます。 nicenessレベルは、nice -n 5 <コマンド>で初期設定したり、renice 10 -p <PID>で再設定したりできます。

結論

この記事では、プロセスの定義、生成方法、終了方法、追跡および設定方法について説明しました。さらに深く理解するために、下記のリンクやリソースをぜひ参照してください。 また、sarのような便利なプロセス継続監視ツールも試してみることをお勧めします。

リソース