簡単なキュー登録による複数タスクのバッチ処理を可能にするTask Spooler

 Task Spoolerは、バッチ処理させたいタスク群をシェルからキュー(待ち行列)に直接登録するためのユーティリティだ。その使用法は簡単で、特別な設定も必要とせず、またキューに登録したコマンドの確認と編集も行える他、こうしたコマンド群からの出力も随時確認できるようになっている。

 Task Spoolerの機能に関しては、atといったバッチ処理の遅延実行ツールと似ている部分も存在する。つまりTask Spoolerもatも、複数のキューが扱え、そこに登録しておいたコマンド群を後から実行させるという点は共通しているのだが、キューに登録したコマンドの実行結果については、atの場合はユーザにメール送信されるのに対して、Task Spoolerの場合はコマンドラインにて直接確認できるのだ。またTask Spoolerによる処理そのものも、個々のコマンドを指定時刻に実行させることではなく、ごく単純に、キューに登録したコマンドを順番に実行させることを主目的としている。

 Fedora、openSUSE、UbuntuのメインリポジトリにTask Spoolerのパッケージは収録されていないが、Debian、Ubuntu、openSUSE 10.xの一部バージョン用パッケージは作成されており、ソースコードもプロジェクトのホームページから取得できる。本稿の執筆にあたってもTask Spoolerのバージョン0.6のソースを入手した上で、64ビット版Fedora 9マシンを用いたソースからのインストールを行った。なおTask Spoolerをビルドする際にautotoolsは使用できないが、そのインストールは通常の「make; sudo make install」にて実行すればよく、これによりTask Spoolerのメインコマンドであるtsおよびmanページが/usr/localにインストールされる。

 下記に掲載したサンプルは、Task Spoolerを用いた簡単な実行例である。ここでは最初に新規のジョブを1つキューに登録した上で、その処理状況のチェックを行っている。なお2番目の実行例のように引数指定なしでtsを実行すると、処理の完了したタスクを含めたキューの登録ジョブが一覧されるが、この例の場合は非常に単純なコマンドを登録したため、即座に処理されてしまったようだ。その次に実行した「ts -c」は、stdoutを介して当該コマンドの実行結果を確認するための指定である。つまり-cオプションは、各タスクの実行出力を書き出したファイルをcatにて表示させるという指定なのだ。同じくその次に実行した「ts -i」は、ジョブ情報を表示させるための指定である。なおこの実行例では用いていないが、処理の終了したジョブをキューから削除するには「ts -C」コマンドを使用すればいい。

$ ts echo "hello world"
6

$ ts
ID   State      Output               E-Level  Times(r/u/s)   Command [run=0/1]
6    finished   /tmp/ts-out.QoKfo9   0        0.00/0.00/0.00 echo hello world

$ ts -c 6
hello world

$ ts -i 6
Command: echo hello world
Enqueue time: Tue Jul 22 14:42:22 2008
Start time: Tue Jul 22 14:42:22 2008
End time: Tue Jul 22 14:42:22 2008
Time run: 0.003336s

 実行出力の末尾数行を表示させる「tail -f」と同様の処理を行わせるには-tオプションを使用すればよく、その実行中はタスクからの追加出力に応じて表示行が継続的に更新されていく。特定タスクの終了を確認したい場合は-mオプションによりメール通知をさせることもできるが、そうした目的に関しては通知用のコマンドをキューに登録しておいてもいい。例えば下記の実行例では、キューに入れておいたtarコマンドの実行終了を確認する目的で、tarボール作成コマンドに続けて、デスクトップに簡易ポップアップウィンドウを表示させる通知コマンドを実行させている。なお、このポップアップウィンドウはタイムアウト後に自動消去されるので、放っておいても構わない。

$ ts tar czvf /tmp/mytarball.tar.gz liberror-2.1.80011
11
$ ts notify-send "tarball creation" "the long running tar creation process is complete."
12
$ ts
ID   State      Output               E-Level  Times(r/u/s)   Command [run=0/1]
11   finished   /tmp/ts-out.O6epsS   0        4.64/4.31/0.29 tar czvf /tmp/mytarball.tar.gz liberror-2.1.80011
12   finished   /tmp/ts-out.4KbPSE   0        0.05/0.00/0.02 notify-send tarball creation the long... is complete.

 この出力例については、ヘッダ行の右端にあるrun=0/1という表示に注目して頂きたい。これは、Task Spoolerが現在処理中のタスクは0個であり、必要であれば1個のタスクを新規に実行可能であるという情報を示している。つまりTask Spoolerでは、マルチコアCPU環境を有効活用するべく、キューに登録されたタスクを同時に複数実行できるよう作られているのだ。このようにキューに登録されたタスクを同時にいくつ実行させるかは、下記のように-Sオプションにて指定できる。

$ ts -S 2
$ ts
ID   State      Output               E-Level  Times(r/u/s)   Command [run=0/2]
6    finished   /tmp/ts-out.QoKfo9   0        0.00/0.00/0.00 echo hello world

 ただし、Task Spoolerを用いて2つのタスクを同時実行させるとしても、片方のタスクが処理終了してからでないと他方のタスクは実行できないという関係になるケースもあるはずで(あるいは1つ目の処理の正常終了を前提とするなど)、そうした場合は、一方のタスクが終了するまで待機させてから次のタスクを実行させるという指定ができる。こうした要件が特に重要となるのは、クアッドコアCPUを搭載したマシンにて、Task Spoolerに3つのタスクを並列処理させるような場合だ。例えば下記の実行例はこうしたタスク間の依存関係を意図的に持たせたものであり、複数タスクの同時実行を許可したキューであったとしても、1つ目のコマンドが正常に終了しない限り2つ目のコマンドを実行できないようにしてある。1つ目のコマンドで行っているのはtsを用いた通常のキュー登録だが、コマンド群をサブシェルにて実行させるため、このtsに対しては新規bashシェルの使用を明示的に指定しておいた。2つ目のtsコマンドにて用いた-dオプションは、現在のキューに登録されている最終コマンドの処理が成功した場合にのみ当該コマンドを実行させるという指定である。そしてその後行った最初のキューの進捗状況の確認では、1つ目のコマンド(28)が実行中であると表示されている。この段階では2つ目のコマンドもキューに登録はされているが、先のタスク28が処理終了するまで手を付けないようTask Spoolerに指定しておいたので、実行中タスクのリストには追加されていない。その次にキューの進捗状況の確認を行った段階では、これら2つのタスクの処理は共に終了している。

$ ts bash -c "sleep 10; echo hi"
28
$ ts -d echo there
29
$ ts
ID   State      Output               E-Level  Times(r/u/s)   Command [run=1/2]
28   running    /tmp/ts-out.hKqDva                           bash -c sleep 10; echo hi
29   queued     (file)                                       && echo there
$ ts
ID   State      Output               E-Level  Times(r/u/s)   Command [run=0/2]
28   finished   /tmp/ts-out.hKqDva   0        10.01/0.00/0.01 bash -c sleep 10; echo hi
29   finished   /tmp/ts-out.VDtVp7   0        0.00/0.00/0.00 && echo there
$ cat /tmp/ts-out.hKqDva
hi
$ cat /tmp/ts-out.VDtVp7
there

 こうしたタスク間の依存関係は、次の実行例のようにより明示的な指定をすることもできる。つまりこの1つ目のコマンドでは、tsコマンドを実行すると新規タスクのIDがコンソールに出力されることを利用して、そうしたID値をシェル変数に格納する形で2つ目のコマンドに渡すようにしているのだ。そして2つ目のコマンドでは受け取ったタスクIDを用いて、当該IDのタスクの処理終了を待ってからエコーを返すようtsコマンドに指定している。なおこの指定部は&&でつなげてあるので、2つ目のコマンドにおけるエコー処理部は、1つ目のコマンドの単なる実行終了ではなくその正常な終了をもって実施されるはずだ。

 その後行った最初のキューの進捗状況の確認では、先の例とは異なり、両方のタスクが実行中であると表示されている。そのうち1つ目のタスクに関しては、全体の処理に遅延をかけるために指定しておいたsleepコマンドの実行途中という段階だ。そして2番目のコマンドも実行中なのだが、そこで動作しているtsは1つ目のタスク終了までの待機が命じられている、というのがここでの状況なのである。ただしこのように、1つ目のタスクが完了しない限り次の処理に進めない処理を2番目のコマンドとしてキューに登録するのは、依存関係の確認法としては問題がある方式とも言えるだろう。

$ FIRST_TASKID=`ts bash -c "sleep 10; echo hi"`
$ ts sh -c "ts -w $FIRST_TASKID && echo there"
25
$ ts
ID   State      Output               E-Level  Times(r/u/s)   Command [run=2/2]
24   running    /tmp/ts-out.La9Gmz                           bash -c sleep 10; echo hi
25   running    /tmp/ts-out.Zr2n5u                           sh -c ts -w 24 && echo there
$ ts
ID   State      Output               E-Level  Times(r/u/s)   Command [run=0/2]
24   finished   /tmp/ts-out.La9Gmz   0        10.01/0.00/0.00 bash -c sleep 10; echo hi
25   finished   /tmp/ts-out.Zr2n5u   0        9.47/0.00/0.01 sh -c ts -w 24 && echo there
$ ts -c 24
hi
$ ts -c 25
there

まとめ

 Task Spoolerの特長は、コマンドライン上で実行するシェルコマンドの前にtsを追加するだけで、これらを1つのキューに登録できることだ。確かに同様の処理はatコマンドでも実行可能だが、「tail -f」的なタスクの進捗確認を効率的に処理できることおよび、実行の完了した各タスクごとの出力結果をコマンドラインから直接アクセスできることは、Task Spoolerの有す大きなアドバンテージである。またマルチコアCPU搭載マシンの場合、複数のタスクを並列処理する機能は特に役立つことになるだろう。またこのユーティリティでは、各タスクの実行に対して明示的な遅延をかけることができるので、非常に複雑な依存関係にあるタスク群を複数組み合わせて、特定ジョブの正常終了を待ってから次のジョブを実行させるという指定を、多段階的に施すことも可能なのだ。

 このように現在実行中のキューに対して、依存関係にある複数のタスクを明示的に登録できることは、キューの実行において任意指定の遅延をかけられることを意味する。つまりこうした遅延用のタスクを1つ用意した上で、それが正常に処理終了した場合にのみ実行される一群のタスクを登録しておくことで、当該キューにおけるその他タスクの実行タイミングが1つ目のタスクの遅延時間によって制御可能となるのだ。

Ben Martinは10年以上にわたってファイルシステムに取り組んでおり、博士課程の修了後、現在はlibferris、ファイルシステム、検索ソリューションを中心としたコンサルティング業に従事している。

Linux.com 原文(2008年8月12日)