# ジョブ制御ってなに? - シェルもどきをgoで自作する#18 ## おさらい これまで - [シェルってなに?コマンドラインインタプリタってなに? - シェルもどきをgoで自作する#1](https://hackmd.io/@jyami/HJzohRn2D) - [コマンドと引数の分解、環境変数PATHから探索、外部コマンドと内部コマンド - シェルもどきをgoで自作する #2](https://hackmd.io/@jyami/HyeSkkThP) - [字句解析その1 - シェルもどきをgoで自作する #3](https://hackmd.io/@jyami/Hk3bWSMQO) - [字句解析その2 - シェルもどきをgoで自作する #4](https://hackmd.io/@jyami/S1BkltxQu) - [リダイレクションってなに? - シェルもどきをgoで自作する #5](https://hackmd.io/@jyami/S113NQx8u) - [リダイレクションの種類 - シェルもどきをgoで自作する #6](https://hackmd.io/@jyami/rJ3XmClqd) - [リダイレクションの構文 - シェルもどきをgoで自作する #7](https://hackmd.io/@jyami/BJ04J2Upd) - [コマンドプロセスを作成する際のファイルディスクリプタの操作 - シェルもどきをgoで自作する #8](https://hackmd.io/@jyami/Hy7nSciMt) - [構文解析と抽象構文木 - シェルもどきをgoで自作する #9](https://hackmd.io/@jyami/ByrK1ajIK) - [パイプってなに? - シェルもどきをgoで自作する #10](https://hackmd.io/@jyami/SkXR3iltK) - [ワイルドカードってなに? - シェルもどきをgoで自作する #11](https://hackmd.io/@jyami/SJghKSl3F) - [環境変数ってなに? - シェルもどきをgoで自作する #12](https://hackmd.io/@jyami/H12wYg4L9) - [環境変数の設定と環境変数の展開 - シェルもどきをgoで自作する #13](https://hackmd.io/@jyami/Hy3EefRgj) - [シェル変数との違いからみた環境変数 - シェルもどきをgoで自作する #14](https://hackmd.io/@jyami/ByxFrm6Si) - [シェル変数を実装 - シェルもどきをgoで自作する #15](https://hackmd.io/@jyami/rJf5GFRhs) - [プロセスグループ/セッションってなに? - シェルもどきをgoで自作する #16](https://hackmd.io/@jyami/Hk0J0E7vn) - [ジョブってなに? - シェルもどきをgoで自作する#17](https://hackmd.io/@jyami/Hyej-Ncoh) シェルもどき「[oreshell](https://github.com/jyami/oreshell)」を自作している。 前回は、Unix(とその眷属)のシェルにおけるジョブとは何かについて調べた。 今回はジョブ制御とその仕組みについて調べる。 ## ジョブ制御とは ジョブは3つの状態を持つ。 - フォアグラウンド実行 - バックグラウンド実行 - 停止 これら3つの状態への切り替え、現在の状態の確認をジョブ制御で行う。 bashではジョブの現在の状態の確認は内部コマンド「jobs」で行う。 以下はその例。 ```bash $ ping yahoo.co.jp > /dev/null & [1] 2573 $ jobs [1]+ 実行中 ping yahoo.co.jp > /dev/null & ``` bashではジョブの実行状態の切り替えを内部コマンド「fg」「bg」で行う。 また、端末からは「Ctrl+Z」「Ctrl+C」の入力によりジョブの実行状態を切り替える。 状態遷移図を以下に示す。 ```plantuml state "フォアグラウンド" as fg state "バックグラウンド" as bg state "停止" as stop [*] --> fg : a)任意の外部コマンド\nを実行 fg -> stop : b)端末から\n「Ctrl+Z」を入力 stop -> fg : c)bash内部コマンド\nfg %{ジョブ番号}\nを実行 fg --> [*] : d)コマンド実行終了、\nまたは端末から\n「Ctrl+C」を入力 [*] --> bg : e)任意の外部コマンドを\n末尾に「&」をつけて実行 bg -> fg : f)bash内部コマンド\nfg %{ジョブ番号}\nを実行 stop -> bg : g)bash内部コマンド \nbg %{ジョブ番号}\nを実行 bg --> [*] : h)コマンド実行終了 ``` (他にも状態を切り替える方法(killコマンドなど)はあるがここでは割愛) ## 実行例 状態遷移図のイベントa~gを実行してみる。 まずbash内部コマンドjobsを実行してジョブの状態を確認する。 ``` $ jobs $ ``` 現時点では、このbash上にジョブは一つも存在しない。 ### a)任意の外部コマンドを実行 ``` $ ping yahoo.co.jp PING yahoo.co.jp (182.22.25.124) 56(84) バイトのデータ 64 バイト応答 送信元 182.22.25.124 (182.22.25.124): icmp_seq=1 ttl=56 時間=11.3ミリ秒 64 バイト応答 送信元 182.22.25.124 (182.22.25.124): icmp_seq=2 ttl=56 時間=11.4ミリ秒 ... ``` ここではpingコマンドを実行する。実行するとpingが繰り返し実行し、結果を永久に標準出力する。 ### b)端末から「Ctrl+Z」を入力 ``` ... 64 バイト応答 送信元 182.22.25.124 (182.22.25.124): icmp_seq=1 ttl=56 時間=11.3ミリ秒 64 バイト応答 送信元 182.22.25.124 (182.22.25.124): icmp_seq=2 ttl=56 時間=11.4ミリ秒 (Ctrl+Zを入力) ^Z [1]+ 停止 ping yahoo.co.jp $ ``` 端末に「Ctrl+Z」を入力すると、先に実行したpingコマンドが停止する。 ここでbash内部コマンドjobsを実行してジョブの状態を確認する。 ``` $ jobs [1]+ 停止 ping yahoo.co.jp ``` ジョブが1つ存在する。ジョブ番号は1番、内容は「ping yahoo.co.jp」、状態は「停止」。 ### c)bash内部コマンドfg %{ジョブ番号}を実行 ``` $ fg %1 ping yahoo.co.jp 64 バイト応答 送信元 182.22.25.124 (182.22.25.124): icmp_seq=5 ttl=56 時間=11.5ミリ秒 64 バイト応答 送信元 182.22.25.124 (182.22.25.124): icmp_seq=6 ttl=56 時間=12.8ミリ秒 ``` ジョブ番号1番を指定してbash内部コマンド「fg」を実行すると、停止していたジョブ番号1番の「ping yahoo.co.jp」が実行を再開する。 ### d)コマンド実行終了、または端末から「Ctrl+C」を入力 ``` ... 64 バイト応答 送信元 182.22.25.124 (182.22.25.124): icmp_seq=10 ttl=56 時間=10.9ミリ秒 64 バイト応答 送信元 182.22.25.124 (182.22.25.124): icmp_seq=11 ttl=56 時間=11.7ミリ秒 (Ctrl+Cを入力) ^C --- yahoo.co.jp ping 統計 --- 送信パケット数 11, 受信パケット数 11, パケット損失 0%, 時間 496064ミリ秒 rtt 最小/平均/最大/mdev = 10.944/11.471/12.822/0.593ミリ秒 ``` 端末に「Ctrl+C」を入力すると、pingコマンドが強制終了した。 ### e)任意の外部コマンドを末尾に「&」をつけて実行 ``` $ ping yahoo.co.jp & [1] 4842 $ PING yahoo.co.jp (183.79.248.252) 56(84) バイトのデータ 64 バイト応答 送信元 183.79.248.252 (183.79.248.252): icmp_seq=1 ttl=56 時間=4.36ミリ秒 64 バイト応答 送信元 183.79.248.252 (183.79.248.252): icmp_seq=2 ttl=56 時間=4.56ミリ秒 ... ``` pingコマンドを末尾に「&」をつけて実行する。 実行するとこのジョブが1番であることを表示する。 その後pingが繰り返し実行し、結果を永久に標準出力する。 a)の場合と違って、ここで端末から「Ctrl+Z」や「Ctrl+C」を入力してもこのpingを停止したり強制終了することはできない。これはこのpingがバックグラウンドジョブであるため。 ### f)bash内部コマンドfg %{ジョブ番号}を実行 ``` ... 64 バイト応答 送信元 183.79.248.252 (183.79.248.252): icmp_seq=2 ttl=56 時間=4.56ミリ秒 $ fg %1 ping yahoo.co.jp 64 バイト応答 送信元 182.22.16.123 (182.22.16.123): icmp_seq=8 ttl=53 時間=11.4ミリ秒 64 バイト応答 送信元 182.22.16.123 (182.22.16.123): icmp_seq=9 ttl=53 時間=11.4ミリ秒 ``` ジョブ番号1番を指定してbash内部コマンド「fg」を実行すると、バックグラウンドジョブのpingがフォアグラウンドジョブに切り替わる。 相変わらずpingの結果を標準出力し続ける。 ### g)bash内部コマンドbg %{ジョブ番号}を実行 ``` ^Z [1]+ 停止 ping yahoo.co.jp ``` 一旦、「Ctrl+Z」で停止する。 その後、 ``` $ bg %1 bg %1 [1]+ ping yahoo.co.jp & $ 64 バイト応答 送信元 183.79.249.252 (183.79.249.252): icmp_seq=16 ttl=56 時間=3.02ミリ秒 64 バイト応答 送信元 183.79.249.252 (183.79.249.252): icmp_seq=17 ttl=56 時間=3.06ミリ秒 ... ``` ジョブ番号1番を指定してbash内部コマンド「bg」を実行する。実行するとこのジョブが1番であることを表示する。 その後pingがバックグラウンドジョブとして実行を再開する。 このpingで確認したいことは終わったのでf),d)で強制終了する。(f,d) ``` $ fg %1 (Ctrl+C) ``` ### h)コマンド実行終了 ``` $ ping -c 3 yahoo.co.jp & [1] 16268 $ PING yahoo.co.jp (183.79.219.252) 56(84) バイトのデータ 64 バイト応答 送信元 183.79.219.252 (183.79.219.252): icmp_seq=1 ttl=53 時間=5.28ミリ秒 64 バイト応答 送信元 183.79.219.252 (183.79.219.252): icmp_seq=2 ttl=53 時間=3.47ミリ秒 64 バイト応答 送信元 183.79.219.252 (183.79.219.252): icmp_seq=3 ttl=53 時間=3.15ミリ秒 --- yahoo.co.jp ping 統計 --- 送信パケット数 3, 受信パケット数 3, パケット損失 0%, 時間 2111ミリ秒 rtt 最小/平均/最大/mdev = 3.153/3.967/5.281/0.937ミリ秒 ``` 今度はpingを回数を指定する。コマンド末尾に「&」をつけて実行する。 pingが指定回数だけ処理を実行すると終了する。 この状態でエンターキーを押すとジョブの終了を表示する。 ``` [1]+ 終了 ping -c 3 yahoo.co.jp ``` ## ジョブに対して「何か」を送るとジョブの実行状態が切り替わる 今度は状態遷移図ではなくロバストネス図で考えてみる。 ここではキーボードからはじまり、実行中または停止中のジョブまでの間のリクエストの伝播に絞って考える。 ### ジョブ実行中 ```plantuml usecase "フォアグラウンドジョブ実行中に\n端末からCtrl+ZまたはCtrl+Cを入力する" boundary "キーボード" as kb control "端末" as tty control "実行中のジョブ\nping yahoo.co.jp" as a1 kb -> tty : "Ctrl+ZまたはCtrl+C" tty -> a1 : 「何か」 ``` 端末からCtrl+Zを入力するとジョブは停止状態になる。(状態遷移図のb) 端末からCtrl+Cを入力するとジョブは終了する。(状態遷移図のd) ### ジョブ停止中 ```plantuml usecase "ジョブ停止中にbash内部コマンド\n「fg %1」または「bg %1」を実行する" boundary "キーボード" as kb control "端末" as tty control "bash" as bash control "停止中のジョブ\nping yahoo.co.jp" as a kb -> tty : "「fg %1」または「bg %1」" tty --> bash : "「fg %1」または「bg %1」" bash -> a : 「何か」 ``` bash内部コマンド「fg %1」を実行するとジョブはフォアグラウンドで実行を再開する。(状態遷移図のc) bash内部コマンド「bg %1」を実行するとジョブはバックグラウンドで実行を再開する。(状態遷移図のg) ### 「何か」の正体 端末、またはbashからジョブに向けて「何か」が送られている。 この「何か」の正体は「[シグナル](https://ja.wikipedia.org/wiki/%E3%82%B7%E3%82%B0%E3%83%8A%E3%83%AB_(Unix))」である。 次回はシグナルの話。