1001001

73。CTFのWrite-upや技術的な備忘録を書きとめたいです。

Linuxでコマンド実行する時の処理モデルの話(setsidとか&とかexec)

はじめに

bashシェルスクリプトでコマンドを実行する時、以下のような実行パターンがそれぞれどう違うのか気になって調べたので備忘録です。特にプロセスIDやプロセスグループの違いを確認してます。

command
setsid command
setsid command &
exec command 
exec setsid command

背景

AKSPASSを使って、シェルでsshパスワードログインを自動化する方法を調べていて、以下の記事を読んだ。

expectやsshpassを使わずにシェルでSSHパスワード認証を自動化する #Linux - Qiita

そこでは exec setsid ssh ~~~ のようにしてsshを実行している。

また、以下の記事では、ASKPASS自体がexecute the programしてくれるのでexec不要と説明し、setsid ssh ~~~ としている。

SSH_ASKPASSの使い方について

これらを見て、setsidやexecをつけて実行した時やそれを組み合わせた時の挙動が気になったので検証した。コマンドは全てbashシェルスクリプトで実行している。

各コマンドのざっくりとした動作の違い

chatgptに聞きつつ、動作の違いを整理した。ここでは、プロセスツリー(pid,ppid,pgid)と処理モデル(非同期・同期実行)の観点で整理している。

  1. command
    • 通常の方法でコマンドを実行する。
    • コマンドはシェルスクリプト子プロセスとして実行される。
    • その子プロセスが完了するまで次の処理は実行されない。(同期実行
  2. setsid command
    • setsidコマンドは、新しいセッションとプロセスグループを作成してから指定されたコマンドを実行する。
    • コマンドはシェルスクリプト子プロセスとして実行されるが、プロセスグループは別物となる。
    • 同期実行となり、そのコマンドが終了するまで次の処理には進まない。
  3. setsid command &
    • setsid commandをバックグラウンドで実行する。(&はコマンドをバックグラウンドで実行するためのシェルの制御構造)
    • コマンドはシェルスクリプトとは別プロセスとして起動する。具体的にはinitプロセス(systemdプロセス)を親プロセスとしたプロセスになる。
    • バックグラウンド実行にしているので非同期実行となり、コマンドの終了を待たずに次の処理が実行される。
  4. exec command
    • execコマンドは、現在のプロセスを指定されたコマンドに置き換える。
    • コマンドのプロセスIDはシェルスクリプトのプロセスIDと同一となる。
    • シェルスクリプト以降の処理が書いてあったとしても実行されない
  5. exec setsid command
    • execを使ってsetsid commandを実行した場合、シェルスクリプト自体はsetsid commandに置き換わり、新しいセッションおよびプロセスグループでcommandが実行される。
    • コマンドのプロセスIDはシェルスクリプトのプロセスIDとなるが、その後setsidが実行されるので最終的には別のプロセスとして実行される。
    • シェルスクリプト以降の処理が書いてあったとしても実行されない

それぞれのコマンド実行時のプロセスのPIDを確認する

実際に確認してみた。
実行するコマンドのみ異なる以下のようなシェルスクリプトを5つ用意する。

#!/bin/bash
echo "My PID is $$"
echo "My PPID is $PPID"
PGID=$(ps -o pgid= -p $$)
echo "My PGID is $PGID"

echo "sleep 10"
sleep 10 # ここだけ可変
echo "done."

これを実行しつつ、sleepしている間に別ターミナルでsleepコマンドのpid,ppid,pgidを取得する。

./command.sh
My PID is 19071
My PPID is 17043
My PGID is   19071
sleep 10
done.

ps -o pid,ppid,pgid,cmd -p $(pgrep -o -x "sleep")
    PID    PPID    PGID CMD
  19073   19071   19071 sleep 10

全てのコマンドについてこの方法でシェルスクリプトとコマンドのpidを確認し、関係を整理する。

command

pid ppid pgid
シェルスクリプト 3485 1287 3485
コマンド 3486 3485 3485

コマンドのppidはシェルスクリプトのpidとなり、pgidもシェルスクリプトのpidで共通である。

setsid command

pid ppid pgid
シェルスクリプト 2862 1287 2862
コマンド 2863 2862 2863

コマンドのppidがシェルスクリプトのpidとなっているのは同じだが、pgidは別となっている。
シェルスクリプトでsetsidをつけて実行した場合、子プロセスにはなるがプロセスグループは別となる。

setsid command &

pid ppid pgid
シェルスクリプト 9583 1287 9583
コマンド 9585 1 9585

コマンドのppidは1となり、pgidもシェルスクリプトとは別となっている。
つまり、別プロセスとして実行されている。

exec command

pid ppid pgid
シェルスクリプト 16030 1287 16030
コマンド 16030 1287 16030

元プロセスを置き換えているので、pid,ppid,pgidは全てシェルスクリプトのプロセスと同じになる。

exec setsid command

pid ppid pgid
シェルスクリプト 17484 1287 17484
コマンド 17486 1 17486

おそらくexec直後は一度シェルスクリプトのプロセスを置き換えて実行されるのだが、その後setsidで別プロセスとして実行されるので、pid,pgidは新しくなり、ppidは1となる。

以上、実際にsetsidやexecでプロセスIDやプロセスグループが変化することが確認できた。

余談

setsid command & とexec setsid commandってほぼ一緒じゃない?

どちらも、親プロセスIDは1となるし、プロセスグループもシェルスクリプトのプロセスとは別物になる。
setsid command &はプロセスを置き換えないのでその後に続く処理があれば、commandの実行を待たずその処理に移るという違いはあるが、
setsid command &の後に何も処理がなければexec setsid commandとほぼ同じ動きになる。

chatgptに聞いても概ね同じ回答だった。置き換えるか明示的に終了するかの違いだと。

macにはsetsidがない?

macだとsetsidはなく、nohup使うらしい?(Linux環境でもnohupは使えるだろうけど。)
こっちも同じ動きになるかは未検証。

まとめ

setsid, execをつけたり、組み合わせた時にコマンドのpid,ppid,pgidがどのように変化するのかを確かめた。
プロセスIDとか気にしないのであれば、同期になるか非同期になるかくらいを気にすれば良さそう。