初めに #
Linuxのシステム運用では、「あるプロセスが終了せずに長時間実行し続けてしまった」「バックアップ処理が予定時間を超えても終わらない」といった問題に直面することがあります。
これらの原因は多岐にわたり、ネットワークの遅延、リソースの競合、プログラム自体のバグによる無限ループなど、予測不可能な要因で発生します。
このような場合、親プロセスなどの実行側の目線では「正常に処理が続いている」ように見えてしまうため、一般的なエラーハンドリングだけでは検知や制御が困難となります。
こうしたプロセスの長時間実行を防ぐのに便利なのが、timeout コマンドによる実行時間の制限です。
本記事では、AlmaLinuxを例に、timeout コマンドの基本的な使い方について実際の使用例を交えながら初心者向けにわかりやすく解説します。
timeoutコマンドとは #
timeout コマンドは、指定した時間が経過すると、実行中のコマンドやプロセスを自動的に終了させることが出来る Linux コマンドとなります。
主な利用目的は以下の通りとなります。
- 無限ループやハングアップ(フリーズ)への対策
- 外部ネットワーク連携時の遅延・切断対策
- スクリプトやバッチ処理における最大実行時間の保証
- 長時間動作プロセスの放置によるリソース枯渇の防止
- デッドロック発生時の強制解除
例えば、rsync コマンド等を使用して外部サーバーへバックアップを行う際、ネットワーク回線の不調によりプロセスが応答しなくなり、処理が永遠に終わらない(ハングアップする)ケースがあります。
このような場合に timeout コマンドを使用することで、想定以上の時間がかかった際に強制的に処理を打ち切ることが可能になります。
利用確認 #
AlmaLinux をはじめとする多くの RHEL 系ディストリビューションでは、timeout コマンドは標準パッケージである coreutils に含まれております。
そのため一般的な構成を取っているサーバーではインストール作業を行う事なくコマンドを利用する事ができます。
環境での利用可否について確認したい場合は、以下のように which コマンドを実行しパス(実行ファイルの場所)を確認してください。
$ which timeout
/usr/bin/timeout
上記のようにコマンドパスが表示されれば、既にシステムに導入されており利用可能な状態となっております。
万が一コマンドが見つからない(パスが表示されない)場合は、以下のコマンドにてインストールを行ってください。
dnf install coreutils
基本的な利用方法 #
timeout コマンドの基本書式は以下の通りとなります。
timeout [オプション] <時間> <実行するコマンド>
以下は各項目の説明となっております。
| 項目 | 説明 |
|---|---|
| 時間 | コマンドを実行する最大時間を指定します。 単位は秒( s)、分(m)、時間(h)、日(d)が使用可能。省略時は秒として解釈されます。 |
| 実行するコマンド | タイムアウトを設定したいLinuxコマンドやスクリプトを指定します。 |
| オプション | timeout コマンドの動作を制御するための各種オプションを指定します。 |
例えば、サーバーのログをリアルタイムで表示し続ける tail -f コマンドを、10秒間だけ実行して終了させる場合は以下のように実行します。
timeout 10s tail -f /var/log/messages
コマンドのオプション #
timeout コマンドには動作を調整するいくつかのオプションが用意されています。
主要なオプションとその説明は次の通りです。
| オプション | 説明 |
|---|---|
--preserve-status |
タイムアウト時でもコマンドの終了ステータスを維持する |
--foreground |
シェルプロンプト外で実行する際、TTY入力やTTYシグナルを許可する(子プロセスにはタイムアウトが適用されない) |
-k, --kill-after=DURATION |
最初の終了シグナル送信後、指定時間経過後に KILL シグナルも送信する |
-s, --signal=SIGNAL |
タイムアウト時に送信するシグナルを指定(例:HUP や 9 など) |
-v, --verbose |
タイムアウト時に送信されたシグナルを標準エラー出力に表示する |
--help |
ヘルプメッセージを表示して終了 |
--version |
バージョン情報を表示して終了 |
実際の使用例 #
timeout コマンドの実用的な活用シーンとして、今回は「Cronジョブの多重起動防止」と「スクリプト内での実行制御」の2つの活用例をご紹介します。
1. Cronジョブでの利用 #
定期的に自動実行される Cron ジョブでは、「前の処理がまだ終わっていないのに、次の実行時間が来てしまう」というトラブルがよく起こります。
例えば、夜間に実施するバックアップ処理が通信エラーなどの理由で応答しなくなり、そのプロセスが終了せずに残り続けてしまう場合があります。
このような状態であっても、翌日の実行時刻になれば Cron は新たな処理を開始してしまうため、結果として古いプロセスと新しいプロセスが重複して動作することになります。
これらが蓄積していくと、ファイルアクセスの競合によってデータの不整合が発生したり、サーバーリソースの枯渇によってシステムダウンを引き起こす原因となります。
timeout コマンドを組み込む事でこのような状況を未然に防ぐ事が可能となっています。
以下は、バックアップスクリプトの実行時間を「最大1時間」に制限する場合の設定例となります。
0 2 * * * /usr/bin/timeout 1h /usr/local/bin/backup.sh
2. スクリプト内での実行制御 #
timeout コマンドは、シェルスクリプトの中から外部コマンドを呼び出す際のエラーハンドリングとしても非常に有効な手段となっています。
例えば、ウイルススキャン(ClamAV)のような負荷の高い処理や、外部通信のような応答時間が読めない処理を実行する場合、一つの処理が滞ることでスクリプト全体の進行が止まってしまうリスクがあります。
そういった場合 timeout コマンドを組み込むことで、「一定時間待っても終わらなければ、その処理を打ち切って次へ進む」という制御を簡単に実装する事が出来ます。
以下は、ウイルススキャンの処理に「2時間」という制限時間を設ける記述例となります。
#!/bin/bash
# ウイルススキャンを実行(2時間を超えたら強制終了)
timeout 2h clamscan -r /var/www/
# その他の処理
このように記述しておくことで、万が一スキャン処理がハングアップしてしまった場合でも強制的に終了させ、スクリプトを止める事なく次の処理に移行させることが可能となります。