Bashでのコマンドタイムアウトの設定
1. 序章
最高のコンピューターでさえ、処理能力は限られています。 これは、プロセスを永久に実行できないことを意味します。 彼らがしようとする状況を避けるために、タイムアウトを使用することができます。
このチュートリアルでは、タイムアウトとは何かについて説明することから始めます。 次に、いくつかのタイムアウト条件について説明します。 最後に、Bashでタイムアウトを使用するさまざまな方法について説明します。 特に、1つの標準ソリューションと1つのカスタムソリューションについて説明します。
GNUBash5.1.4を使用してDebian11Bullseyeでコードをテストしました。 POSIXに準拠しており、そのような環境で機能するはずです。
2. タイムアウト
タイムアウトは、コンピューティングの多くのレベルに存在します。 すべての場合において、タイムアウトは同じ目的を果たします:プロセスの実行時間を制限します。 タイムアウトを強制する方法は正確には異なりますが、基本的な考え方は同じです。
まず、タイムアウト値を決定します。 次に、プロセスの開始時刻を確認します。
大まかに言えば、どのアクションも時間の観点から制約を受ける可能性がありますが、他のアクションよりも理にかなっているアクションもあります。 多くの場合、選択はタイムアウト条件の確率に依存します。
3. タイムアウト条件
タイムアウトには多くの理由が考えられます。 一般的に、多くはいくつかのカテゴリの1つに分類されます。 それらを調べてみましょう。
3.1. ハードウェア障害
ハードウェアコンポーネントの問題により、プロセスが完了しない場合があります。 例えば、
3.2. ハードウェア仕様
エラーがなくても、ハードウェアは妥当な時間内にプロセスを完了するのに十分なほど強力ではない可能性があります。 たとえば、メモリ不足が速度低下やスラッシングの原因になる可能性があります。 さらに、ネットワーク接続が遅いと、プロセスが不当に遅れる可能性があります。
3.3. 発達
これは多くの場合、タイムアウト条件の最も広いカテゴリです。 実際、環境の外では、プロセスをどのように実装するかが、その実行時間と制限に最も大きな影響を及ぼします。
重要なことに、ランタイムを間違えると、予期しないタイムアウトが発生する可能性があります。 部分的には、これは異なるハードウェアを検討する必要があることを意味します。
もう1つの一般的な問題は、デッドロックです。 これらは、2つのプロセスが互いに完了するのを待つときに発生します。 その結果、それらのどれも完了せず、無期限に待機します。
もちろん、最も単純なプログラムでも問題が発生する可能性があります。 実際、単一スレッド内の単純な無限ループにより、プロセスの完了が妨げられる可能性があります。
他にも複数の可能性がありますが、これらは一般的なものです。 次に、Bashでそれらを処理する方法について説明します。
4. Bashタイムアウト
Bashでタイムアウトを使用するにはさまざまな方法があります。 標準とカスタムの2つの大きなカテゴリでソリューションを確認してみましょう。
4.1. タイムアウトコマンド
明らかに、私たちは自分たちのニーズに合わせて特定のツールを使用することができます。 たとえば、[X25X] GNUcoreutilsパッケージの一部であるtimeoutコマンドがあります。
タイムアウトコマンドは、タイムアウト期間後に実行されたプロセスを停止します:
$ timeout 1s bash -c 'for((;;)); do :; done'
$ echo $?
124
ここでは、エンドレスループを実行します。 timeoutがプロセスを強制終了する前に1秒のタイムアウトを設定しました。
重要なのは、終了コードに注意してください。 これは、プロセスがデフォルトのSIGTERM信号で停止されたことを意味します。 –signalオプションで別の信号を選択できます。
さらに、 –preserve-status スイッチは、timeoutに元のプロセスのステータスコードを返すように強制することができます。
$ timeout --preserve-status 1s bash -c 'exit 66'
$ echo $?
66
このオプションを使用すると、問題の原因となったコードを確認できます。
timeoutコマンドは外部パッケージの一部です。 これは、すべてのシステムで使用できるとは限らないことを意味します。
4.2. カスタムタイムアウト
カスタムコマンドにアクセスできない場合や、単に使用したくない場合があります。 このような状況では、独自のバージョンのタイムアウトコマンドを作成できます。
function xtimeout {
timeout=$1
shift
command=("$@")
(
"${command[@]}" &
runnerpid=$!
trap -- '' SIGTERM
( # killer job
sleep $timeout
if ps -p $runnerpid > /dev/null; then
kill -SIGKILL $runnerpid 2>/dev/null
fi
) &
killerpid=$!
wait $runnerpid
kill -SIGKILL $killerpid
)
}
この関数は、最初の引数として秒単位のタイムアウトを想定しています。 次に、その最初の引数を削除し、残りを実行するコマンドラインとして使用します。
まず、コマンドを実行します。 実際には、サブシェルのバックグラウンドで実行されます。 これは、バックグラウンドジョブメッセージをミュートするためです。 次に、この関数は sleep を使用して、指定された秒数だけ待機します。 最後に、開始されたコマンドのPIDを確認します。 それが実行されている場合、殺人者の仕事はそれを止めます。 タイムアウトが切れる前にコマンドが完了した場合、キラージョブを停止します。
もちろん、この機能はtimeoutコマンドほど柔軟でも堅牢でもありません。 でも、そのすべてのパーツは、標準のLinuxマシンで使用できます。
5. 結論
このチュートリアルでは、Bashでのタイムアウトについて説明しました。 まず、タイムアウトといくつかの一般的なタイムアウト条件を定義しました。 その後、2つのタイムアウトソリューションを確認しました。
- GNU coreutils timeoutコマンド
- Bashのカスタムタイムアウト関数
結論として、タイムアウトはBashで簡単に記述できますが、 timeout コマンドがあるため、これは必要ありません。