RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

【Linux】今振り返りたい、プロセスって何?

f:id:sts-250rr:20200329202258p:plain

はじめに

花粉がつらくなってきました... sts-250rrです。
開発エンジニアとして、チームにアサインされて1年が経とうとしています。(速い)

私が担当している商材は主にJavaで書かれているため、普段はJVM上で動くアプリケーションに意識が行ってしまいがちで、そもそもアプリケーションがどうやって動いているなど
Tomcat上でアプリケーションを動かしているということは漠然と理解していますが...) OSレベルまで掘り下げして理解できていないように感じています。

今回は基礎に振り返るという意味でLinuxのプロセスについてまとめていきます。 (本記事のコマンドはUnixOS系であるMacOSXで実行しています。)

プロセスって何?

プロセスとは、「OS上で実行中のプログラム」を指します。
プロセスが動いているということは、コンピュータリソース(CPUやメモリなど)を消費している状態です。

例えば、Linuxコマンドは1つのプログラムであるため、コマンド実行時にプロセスを1つ作成します。 この間、プロセスはCPUやメモリを消費してプログラムを実行しています。

プロセスの特徴には以下のようなものがあります。

  • 1つのCPU上で同時に処理するプロセスは1つだけ
  • 複数のプロセスが実行可能な場合、個々のプロセスを適当な長さの時間ごとにCPU上で順番に処理する。

psコマンドを叩いてみると複数のプロセスが動いているように見えます。

(ターミナルを2枚開いた状態で1枚目はPostgresに接続、2枚目でpsコマンドを実行 することで2つのプログラムが動いている状態を用意しています。)

$ ps -a
  PID TTY           TIME CMD
17239 ttys001    0:00.02 login -pf sts
17240 ttys001    0:00.03 -bash
17642 ttys001    0:00.00 ps -a
15351 ttys017    0:00.03 login -pf sts
15352 ttys017    0:00.12 -bash
17237 ttys017    0:00.01 psql -U postgres -p 5432

PC上の操作でも複数の処理を同時に動かすことがあるように、
実際のWEBアプリケーションにおいても、ApatchTomcat、データベースが同時に動いて1つのサービスとして機能しています。 これを実現しているものが次項のマルチプロセスになります。

マルチプロセス

マルチプロセスは複数のタスクを並行して実行する機能のことを指しますが、Linuxは厳密には同時並行で処理を行うことを実現してはいません。 Linuxがやっていることは人が認識できない時間でプロセスを区切り、順番に処理することで複数のプロセスが同時に処理をしているように振る舞っています。

プロセススケジューラ

複数のプロセスが同時に処理をしているように振る舞うようにするため、Linuxはプロセススケジューラという機能を持っています。 例えば3つのプロセスを同時に処理する場合、プロセススケジューラによって以下のようにプロセス0 → 1 → 2 と処理を行っていきます。

f:id:sts-250rr:20200329200912j:plain

また、プロセスの処理は以下のように並列で進んでいきます。

f:id:sts-250rr:20200329200939j:plain

プロセスを短い間隔(タイムスライス)に区切り、1つのCPU上で動くプロセスを切り替えることで複数のプロセスが同時に処理しているように振る舞っています。 タイムスライスはそれぞれ等しいくらいの長さで区切られ、プロセスが増えるとタイムスライスの数が増えるため1つのプロセスが完了するまでに時間がかかります。

コンテキストスイッチ

1つのCPU上で動くプロセスがタイムスライスで切り替わることをコンテキストスイッチと呼びます。
コンテキストスイッチは、タイムスライスの時間が切れると容赦なく発生します。
例として、プロセス1 → 2 と順に実行する1つのプログラムがあったとします。プログラムの実行中にプロセス1が起動した場合、プロセス1 → 3 → 2 という順序で実行される可能性があります。 1プログラム以外の要因(他のプロセスが間に起動したなど)によって完了時間が伸びる可能性があるという点がコンテキストスイッチによるプロセス特性の1つです。

プロセスの状態

ここまでで記載したように、1つのCPU上で動くプロセスは切り替わるため、プロセスは実行状態・スリープ状態などの複数の状態を持っています。 以下にプロセス状態の一例をあげます。

状態 意味
実行状態 CPU使用状態
実行待ち状態 CPUが割り当てられるのを待っている
スリープ状態 イベントの発生を待っている(CPUは使っていない状態)
ゾンビ状態 終了状態の受け取りを待っている

プロセスの状態は以下のように遷移します。

f:id:sts-250rr:20200329200958j:plain

プロセスはプロセススケジューラに基づき、CPUの実行権を得ると実行待ち状態から実行状態に遷移し、処理をすべて完了するまで、タイムスライス区切りで状態を遷移し続けます。 プロセスは終了状態を得ることで終了となります。

コマンドで確認

Mac上でtopコマンドを実行してみると以下のような出力になりました。(上部10件まで)

Processes: 418 total, 5 running, 413 sleeping, 1787 threads                                                                                                                                               19:16:35
Load Avg: 1.97, 3.00, 4.61  CPU usage: 7.7% user, 26.88% sys, 66.3% idle     SharedLibs: 295M resident, 33M data, 29M linkedit. MemRegions: 109880 total, 3310M resident, 119M private, 1551M shared.
PhysMem: 8606M used (2803M wired), 7777M unused. VM: 1959G vsize, 1882M framework vsize, 2689796(0) swapins, 2881396(0) swapouts. Networks: packets: 58205899/34G in, 48094735/17G out.
Disks: 5133929/94G read, 2715156/54G written.

PID    COMMAND      %CPU  TIME     #TH   #WQ  #PORT MEM    PURG   CMPRS  PGRP  PPID  STATE    BOOSTS          %CPU_ME %CPU_OTHRS UID  FAULTS   COW    MSGSENT    MSGRECV   SYSBSD     SYSMACH   CSW        PAGEINS
3075   VBoxHeadless 59.5  01:52:54 39/2  5    996   908M   0B     47M    3075  3060  running  *0[2]           0.00000 0.00000    501  272297   390    4904505+   110938+   80020677+  12095394+ 56986420+  760
0      kernel_task  28.9  01:47:26 172/4 0    0     105M   0B     0B     0     0     running   0[0]           0.00000 0.00000    0    173910+  0      100809447+ 97799283+ 0          0         429621385+ 0
14655  firefox      15.0  04:38.25 61    4    775+  443M+  9384K  81M-   14655 1     sleeping *0[1192+]       0.00000 0.06446    501  1835132+ 15875  940954+    304755+   2763928+   3000384+  1945509+   33761+
15349  Terminal     6.4   00:11.47 9     4    261-  72M    33M+   0B     15349 1     sleeping *0-[368+]       1.14998 0.42402    501  149598+  491    55408+     13334+    134496+    142201+   63838+     2824
2303   com.docker.h 5.6   02:01:48 15    0    38    2355M  0B     304M   2288  2300  sleeping *0[1]           0.00000 0.00000    501  725271   446    785        317       207705774+ 465       126169989+ 68
15698  top          4.3   00:01.43 1/1   0    25    5284K  0B     0B     15698 15352 running  *0[1]           0.00000 0.00000    0    11303+   109    489446+    244672+   39445+     314731+   1968+      0
226    WindowServer 3.1   15:54.83 10    5    1377- 447M   19M    47M    226   1     sleeping *0[1]           0.38155 0.00856    88   1809748+ 8936   13705840+  6222509+  5598080+   19197483+ 7974577+   9094
188    hidd         1.9   02:39.48 5     3    241   3492K  0B     664K   188   1     sleeping *0[1]           0.00688 0.00000    261  83167+   171    629366+    489469+   5409421+   1849976+  2190289+   5218
265    airportd     1.1   03:49.45 12    10   1385+ 13M+   0B     5144K- 265   1     sleeping *14180[212]     0.00000 0.93819    0    243908+  268    5776050+   2282663+  11177993+  5077848+  4985570+   2748
171    locationd    0.6   01:34.95 7     5    231+  8600K+ 192K   1420K  171   1     sleeping  0[22641]       1.15398 0.00000    205  207357+  338    357612+    97377+    3218875+   737171+   876386+    12727

まだまだ情報量が多く見づらいので、Terminalとtopコマンドだけ抜粋します。

PID    COMMAND      %CPU  TIME     #TH   #WQ  #PORT MEM    PURG   CMPRS  PGRP  PPID  STATE    BOOSTS          %CPU_ME %CPU_OTHRS UID  FAULTS   COW    MSGSENT    MSGRECV   SYSBSD     SYSMACH   CSW        PAGEINS
15349  Terminal     6.4   00:11.47 9     4    261-  72M    33M+   0B     15349 1     sleeping *0-[368+]       1.14998 0.42402    501  149598+  491    55408+     13334+    134496+    142201+   63838+     2824
15698  top          4.3   00:01.43 1/1   0    25    5284K  0B     0B     15698 15352 running  *0[1]           0.00000 0.00000    0    11303+   109    489446+    244672+   39445+     314731+   1968+      0

topコマンドは今正しく動いているプログラムであるため状態は実行中(running)となっています。 対して、Terminalはtopコマンドの実行後、入力待ち状態となったためスリープ状態(sleeping)となっています。 手元で実行していただくとわかるかと思いますが、topコマンド実行中にコマンドを終了させてみたり、新しく実行してみたりすると
プロセスの生成や状態がみるみる変わっていく様子を見ることができます。

ゾンビプロセス

IDEなどで開発をしている中で、「既に8080ポートが使われている」などの理由でアプリ起動に失敗した経験が皆さんにもあるのではないでしょうか?

そんな悪さをしているのがゾンビプロセスです。

ゾンビプロセスが生まれてしまう原因は様々ですが、
一定時間子プロセスからの返答がなかったことから、タイムアウトとして親プロセスを終了してしまい子プロセスが宙に浮いてしまう。
というものがよくある1つの例かと思います。

「親プロセスが終了したからといって子プロセスもあわせて勝手に終了するわけではない」と覚えておきましょう。(その逆もしかりです。)

ゾンビプロセスができてしまったときは対象のプロセスを調べ、Killして対処しましょう。

まとめ

雑多になりましたが、Linuxのプロセスについてまとめていきました。 書籍やブログ記事などで知識を得ることはできますが、なにぶんOSの裏の動きは実際に手を動かしてみないとピンとこないものです。 コマンドは知っていても見方や期待される状態は知識のみでは補えない点も多いかと思います。 コーディングに疲れて一息つくときなどにpsコマンドやtopコマンドをボーっと眺めて、変化が起きる瞬間をウォッチしてみると面白い発見や知識がつながる瞬間があるかもしれません。

参考資料

Copyright © RAKUS Co., Ltd. All rights reserved.