dorapon2000’s diary

忘備録的な。セキュリティとかネットワークすきです。

シェルの&とwaitについて少し調べてみた

&はシェルにおいてバックグラウンド実行をするコマンド?です。 そして、複数のバックグランドジョブの終了を待つコマンドがwaitです。

つまり、

sleep 20 &
sleep 10 &
wait  # 上2つのコマンド入力直後であれば20秒待機

# sleep 20 & sleep 10 & wait  #1行にもできる

というコマンドは20秒sleepするジョブと10秒sleepするジョブをバックグランドで実行し、それらが終わるまで待つことになります。

今回はmanを見てもよくわからなかった2つの疑問を調べてみます。

疑問

  • 1行に2つ書いた&によるバッググラウンド実行は、ハイパースレッドあるいはマルチコアで実行されるか
  • waitによって完了を待つのは同じ親プロセスに属する&によるプロセスなのか

結論

  • 1行に2つ書いた&によるバッググラウンド実行は、マルチコアで実行される
  • waitによって完了を待つのは、同じ親プロセスに属するすべての&による子孫プロセス

環境

  • WSL2
  • Ubuntu 20.04 LTS
  • bash
  • CPU 4コア4スレッド

調査

疑問に対してそれぞれ調査して確認します。

マルチコアになるか

CPUに負荷をかけるためyesを2つバックグランドで実行します

$ yes > /dev/null & yes > /dev/null &
[1] 2929
[2] 2930

$ jobs
[1]-  Running                 yes > /dev/null &
[2]+  Running                 yes > /dev/null &

$ top
略
%Cpu0  :   0.0/18.2   18[|||||||||||||||||                                                                            ]
%Cpu1  :  62.4/37.6  100[|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||]
%Cpu2  :   0.0/1.1     1[|                                                                                            ]
%Cpu3  :  64.0/36.0  100[|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||]

2つのCPUの利用率が100%になっているため、マルチコアで実行されるということがわかります。

バックグランドジョブの終了

$ kill %1 %2
$ jobs
[1]-  Terminated              yes > /dev/null
[2]+  Terminated              yes > /dev/null

waitが待つプロセス

wait() This function waits for the first child to die. The return value is that of the wait(2) system call.

manの説明ではよくわからなかったので確認します。

叔父プロセス

親プロセスのfishでsleep 100を実行し、その子プロセスのbashでさらにsleep 10sleep 5を実行しました。

sleep 100 &
❯ bash

$ ps
  PID TTY          TIME CMD
   13 pts/1    00:00:06 fish
 3248 pts/1    00:00:00 sleep
 3311 pts/1    00:00:00 bash
 3317 pts/1    00:00:00 ps

$ sleep 10 & sleep 5 & pstree -p 13; wait
[1] 3318
[2] 3319
fish(13)─┬─bash(3311)─┬─pstree(3320)
         │            ├─sleep(3318)
         │            └─sleep(3319)
         └─sleep(3248)
# 10秒後
[1]-  Done                    sleep 10
[2]+  Done                    sleep 5

上の挙動から親のfishで実行されたsleepは待たず、bashで実行された2つのsleepの実行を待っている事がわかります。

ワンライナーではない兄弟プロセス

$ sleep 20 &
[1] 3354

$ sleep 5 & pstree -p 13; wait
[2] 3355
fish(13)───bash(3311)─┬─pstree(3356)
                      ├─sleep(3354)
                      └─sleep(3355)
# 約20秒後
[1]-  Done                    sleep 20
[2]+  Done                    sleep 5

当然ですが、ワンライナーとか関係ないです。同じbashの子プロセスであればすべて待ちます。

甥プロセス

$ sh -c 'sleep 10' & sleep 5 & pstree -p 13; wait
[1] 3358
[2] 3359
fish(13)───bash(3311)─┬─pstree(3360)
                      ├─sh(3358)───sleep(3361)
                      └─sleep(3359)
# 10秒後
[1]-  Done                    sh -c 'sleep 10'
[2]+  Done                    sleep 5

同じ親を持つ子孫すべてのバックグラウンドジョブを待ちました。

まとめ

結論に書いたとおりですが、期待どおりの動作をしてくれました。 package.jsonのscripts内でコマンドを&で並列実行できるか確認したかったのが今回の調査の動機でした。

甥プロセスや叔父プロセスといった名詞があるかは知りません。