Go to main content
Oracle® Solaris 11.3 デバイスドライバの記述

印刷ビューの終了

更新: 2016 年 11 月
 
 

タスクのキュー

このセクションでは、タスクキューを使用して一部のタスクの処理を延期し、その実行を別のカーネルスレッドに委任する方法について説明します。

タスクキューの概要

カーネルプログラミングの一般的な操作は、タスクがあとで別のスレッドによって実行されるようにスケジュールすることです。次の例は、タスクを別のスレッドにあとで実行する理由をいくつか列挙したものです。

  • 現在のコードパスはタイムクリティカルである。実行する追加タスクはタイムクリティカルでない。

  • 追加タスクで、別のスレッドが現在保持しているロックを獲得しなければならない可能性がある。

  • 現在のコンテキストではブロックできない。追加タスクは、メモリー待機などの理由でブロックしなければならない可能性がある。

  • ある条件のためにコードパスが完了できない状態になっているが、現在のコードパスをスリープさせたり失敗させたりできない。条件の解消後に実行できるよう、現在のタスクをキューに入れる必要がある。

  • 複数のタスクを並列して起動する必要がある。

これらの各ケースでは、別のコンテキスト内でタスクが実行されます。別のコンテキストとは通常、異なる一連のロックを保持し、優先順位もおそらく異なる別のカーネルスレッドのことです。タスクキューは、非同期タスクをスケジュールするための汎用カーネル API を提供します。

タスクキューとは、タスクリストを処理するためのスレッドを 1 つ以上備えたタスクリストのことです。タスクキューのサービススレッドが 1 つの場合、リストに追加された順番ですべてのタスクが実行されることが保証されます。タスクキューのサービススレッドが複数存在する場合、タスクの実行順番は不明になります。


注 - タスクキューのサービススレッドが複数存在する場合には、あるタスクの実行がほかのどのタスクの実行にも依存しないことを確認してください。タスク間に依存関係があると、デッドロックが発生する可能性があります。

タスクキューのインタフェース

次の DDI インタフェースはタスクキューを管理します。これらのインタフェースは sys/sunddi.h ヘッダーファイル内で定義されています。これらのインタフェースの詳細については、taskq(9F) のマニュアルページを参照してください。

ddi_taskq_t
不透明なハンドル
TASKQ_DEFAULTPRI
システムのデフォルト優先順位
DDI_SLEEP
メモリーのためにブロックできる
DDI_NOSLEEP
メモリーのためにブロックできない
ddi_taskq_create()
タスクキューを作成する
ddi_taskq_destroy()
タスクキューを破棄する
ddi_taskq_dispatch()
タスクキューにタスクを追加する
ddi_taskq_wait()
保留中のタスクが完了するまで待つ
ddi_taskq_suspend()
タスクキューを一時停止する
ddi_taskq_suspended()
タスクキューが一時停止されているかどうかをチェックする
ddi_taskq_resume()
一時停止されたタスクキューを再開する

タスクキューの監視

ドライバでの典型的な使用方法は、attach(9E) でタスクキューを作成することです。taskq_dispatch() 呼び出しの大部分は、割り込みコンテキストから行われます。

このセクションでは、タスクキューで消費されるシステムリソースをモニターするために使用可能な 2 つの手法について説明します。タスクキューは、タスクキュースレッドによるシステム時間の使用量に関する統計情報をエクスポートします。さらにタスクキューは、DTrace SDT プローブを使用してタスクキューがタスクの実行を開始した時刻と終了した時刻を判定します。

タスクキューのカーネル統計カウンタ

すべてのタスクキューに一連の kstat カウンタが関連付けられます。次のkstat(1M) コマンドの出力を確認してください。

$ kstat -c taskq
module: unix                            instance: 0     
name:   ata_nexus_enum_tq               class:    taskq
        crtime                          53.877907833
        executed                        0
        maxtasks                        0
        nactive                         1
        nalloc                          0
        priority                        60
        snaptime                        258059.249256749
        tasks                           0
        threads                         1
        totaltime                       0

module: unix                            instance: 0     
name:   callout_taskq                   class:    taskq
        crtime                          0
        executed                        13956358
        maxtasks                        4
        nactive                         4
        nalloc                          0
        priority                        99
        snaptime                        258059.24981709
        tasks                           13956358
        threads                         2
        totaltime                       120247890619

上で示した kstat 出力には、次の情報が含まれています。

  • タスクキューの名前とそのインスタンス番号

  • スケジュールされたタスクの数 (tasks) と実行されたタスクの数 (executed)

  • タスクキューを処理するカーネルスレッドの数 (threads) とその優先順位 (priority)

  • すべてのタスクの処理に費やされた合計時間 (ナノ秒) (totaltime)

次の例は、kstat コマンドを使用してあるカウンタ (スケジュールされたタスクの数) が時間の経過とともに増加する様子を監視する方法について示したものです。

$ kstat -p unix:0:callout_taskq:tasks 1 5
unix:0:callout_taskq:tasks      13994642

unix:0:callout_taskq:tasks      13994711

unix:0:callout_taskq:tasks      13994784

unix:0:callout_taskq:tasks      13994855

unix:0:callout_taskq:tasks      13994926

タスクキューの DTrace SDT プローブ

タスクキューは便利な SDT プローブをいくつか提供します。このセクションで説明するプローブはすべて、次の 2 つの引数を持ちます。

  • ddi_taskq_create() から返されるタスクキューポインタ

  • taskq_ent_t 構造体へのポインタ。このポインタを D スクリプト内で使用することで関数や引数を抽出します。

これらのプローブを使用すると、個々のタスクキューやそれらを通じて実行される個々のタスクに関する高精度のタイミング情報を収集できます。たとえば、次のスクリプトは、タスクキュー経由でスケジュールされた関数を 10 秒に 1 度出力します。

# !/usr/sbin/dtrace -qs

sdt:genunix::taskq-enqueue
{
  this->tq  = (taskq_t *)arg0;
  this->tqe = (taskq_ent_t *) arg1;
  @[this->tq->tq_name,
    this->tq->tq_instance,
    this->tqe->tqent_func] = count();
}

tick-10s
{
  printa ("%s(%d): %a called %@d times\n", @);
  trunc(@);
}

特定のシステム上で上の D スクリプトを実行すると、次のような出力が生成されます。

callout_taskq(1): genunix`callout_execute called 51 times
callout_taskq(0): genunix`callout_execute called 701 times
kmem_taskq(0): genunix`kmem_update_timeout called 1 times
kmem_taskq(0): genunix`kmem_hash_rescale called 4 times
callout_taskq(1): genunix`callout_execute called 40 times
USB_hid_81_pipehndl_tq_1(14): usba`hcdi_cb_thread called 256 times
callout_taskq(0): genunix`callout_execute called 702 times
kmem_taskq(0): genunix`kmem_update_timeout called 1 times
kmem_taskq(0): genunix`kmem_hash_rescale called 4 times
callout_taskq(1): genunix`callout_execute called 28 times
USB_hid_81_pipehndl_tq_1(14): usba`hcdi_cb_thread called 228 times
callout_taskq(0): genunix`callout_execute called 706 times
callout_taskq(1): genunix`callout_execute called 24 times
USB_hid_81_pipehndl_tq_1(14): usba`hcdi_cb_thread called 141 times
callout_taskq(0): genunix`callout_execute called 708 times