日本-日本語

製品  >  ソフトウェア  >  OpenVMS  >  マニュアル

OpenVMS マニュアル


≫ 

OpenVMS V8.3
ライブラリ

タイトルページ
目次
まえがき
第 1 部:デバッガ概要
第 1 章:デバッガ概要
第 2 部:コマンド・インタフェース
第 2 章:デバッガの起動
第 3 章:プログラム実行の制御とモニタ
第 4 章:プログラム・データの検査と操作
第 5 章:プログラム内シンボルへのアクセス制御
第 6 章:ソース・コードの表示の制御
第 7 章:画面モード
第 3 部:DECwindows インタフェース
第 8 章:DECwindows Motifインタフェースの概要
第 9 章:デバッグ・セッションの開始と終了
第 10 章:デバッガの使用方法
第 4 部:PC クライアント・インタフェース
第 11 章:デバッガの PC クライアント/サーバ・インタフェースの概要
第 5 部:高度なトピック
第 12 章:ヒープ・アナライザの使用
第 13 章:その他の便利な機能
第 14 章:特殊なデバッグ
第 15 章:マルチプロセス・プログラムのデバッグ
第 16 章:タスキング・プログラムのデバッグ
第 6 部:付録
付録 A :定義済みのキー機能
付録 B :組み込みシンボルと論理名
付録 C :各言語に対するデバッガ・サポートの要約
付録 D :EIGHTQUEENS.C
索引
PDF
OpenVMS ホーム

HP OpenVMS
デバッガ説明書


目次 索引

第 16 章
タスキング・プログラムのデバッグ

本章では,タスキング・プログラム ( マルチスレッド・プログラムとも呼ぶ ) に固有のデバッガ機能について説明します。タスキング・プログラムは 1 つのプロセス内に複数のタスクまたは実行スレッドを持っています。デバッガで使用する タスク という用語は制御の流れのことであり,言語や実現方法とは関係ありません。デバッガのタスキング・サポートは,それらのプログラムすべてに適用されます。次のものを含んでいます。

  • POSIX Threads か POSIX 1003.1b サービスを使用する言語で作成されたプログラム。これらのプログラムをデバッグするとき,デバッガの省略時のイベント機能は THREADS です。 Alpha と I64 は POSIX Threads サービスを使用します。

  • 言語固有のタスキング・サービス ( 言語が独自に用意しているサービス ) を使用するプログラム。現在のところ,デバッガがサポートする組み込みタスキング・サービスを用意している言語は Ada だけです。 Ada プログラムをデバッグする場合は,デバッガの省略時のイベント機能は, ADA です。

注意

デバッガの中では,タスクとスレッドという用語は同義語として使われます。

PTHREAD$RTL バージョン 7.1 またはそれ以降のバージョンがリンクされたプログラムをデバッグするときには,PTHREAD コマンドを使って Compaq POSIX Threads デバッガに直接アクセスすることができます。

本章では, POSIX Threads 固有か言語固有の情報にはそのことを明記します。 第 16.1 節 に POSIX Threads 用語と Ada のタスキング用語の対応表を示します。

本章の機能を使用すれば,次のような処理を行うことができます。

  • タスク情報を表示する。

  • タスクの実行,優先順位,状態の遷移などを制御するためにタスク特性を変更する。

  • タスク依存イベントと状態の遷移をモニタする。

これらの機能を使用するときには,同じタスキング・プログラムを実行してもそのときの状況によっては動作がデバッガにより変更されることがあるので注意してください。たとえば,現在アクティブなタスクの実行をあるブレークポイントで中断しているとき,入出力 (I/O) の終了による POSIX 信号か非同期システム・トラップ (AST) が届いた場合は,ユーザの続行指示後ただちにその他のタスクが適格になることがあります。

POSIX Threads についての詳しい説明は,『Guide to the POSIX Threads Library』を参照してください。 Ada タスクについての詳しい説明は Compaq Ada のマニュアルを参照してください。

マルチプロセス・プログラム(2つ以上のプロセスに分けて実行されるプログラム)のデバッグについては 第 15 章 を参照してください。

16.1 POSIX Threads 用語とAda用語の対応表

表 16-1 に POSIX Threads と Ada の用語とその意味の対応を示します。

表 16-1 POSIX Threads用語と Ada 用語の対応
POSIX Threads 用語 Ada用語 意味
スレッド タスク 同じプロセス内の制御の流れ
スレッド・オブジェクト タスク・オブジェクト 制御の流れを表すデータ項目
オブジェクト名または式 タスク名または式 制御の流れを表すデータ項目
開始ルーチン タスク本体 制御の流れに従って実行されるコード
該当なし 親タスク 親タスクの制御の流れ
該当なし 依存タスク なんらかの親タスクに制御される子タスクの制御の流れ
同期化オブジェクト(ミューテクス,条件変数) ランデブ構造 ( エントリ呼び出しやaccept文など ) 制御の流れを同期化する方法
スケジューリング方針およびスケジューリング優先順位 タスク優先順位 実行のスケジューリング方法
警告処理 abort文 制御の流れの取り消し方法
スレッド状態 タスク状態 実行の状態 ( 待ち,レディ,実行中,終了)
スレッド作成属性 ( 優先順位,スケジューリング方針など ) プラグマ パラレル・エンティティの属性



16.2 タスキング・プログラムの例

次の各項では,タスキング・プログラムのデバッグ時によく起こるエラーを含んでいるタスキング・プログラムの例を示します。

  • 第 16.2.1 項 では, POSIX Threads サービスを使用するCプログラムについて説明する。

  • 第 16.2.2 項 では,組み込みの Ada タスキング・サービスを使用するAdaプログラムについて説明する。

本章のその他の例は,これらのプログラムを引用したものです。

16.2.1 Cのマルチスレッド・プログラムの例

例 16-1 はマルチスレッドの C のプログラムです。条件変数の使用法が間違っているのでブロッキングを起こします。

例のあとに説明が続いています。その説明のあとに,デバッガを使用してスレッドの相対的な実行を制御することによってブロッキングを診断する方法を示しています。

例 16-1 では,初期スレッドにより,計算作業を行う 2 つのワーカ・スレッドが作成されます。これらのワーカ・スレッドの作成後に SHOW TASK/ALL コマンドを実行すれば,それぞれが 1 つのスレッドに対応する 4 つのタスクが表示されます。 第 16.4 節 に SHOW TASK コマンドの使用法が説明されています。

  • %TASK 1 が初期スレッドであり,main() から実行される。 第 16.3.3 項 では,%TASK 1 などのタスク ID が定義されている。

  • %TASK 2 と %TASK 3 は,ワーカ・スレッドである。

例 16-1 では,ワーカ・スレッドのパスの行 3893 に同期化点 ( 条件待ち ) が設けられています。行 3877 から始まるコメントは,このような直接的な呼び出しは間違ったプログラミング方法であることを示したうえで,正しいコーディング方法を示しています。

このプログラムを実行すると,ワーカ・スレッドが大量の計算を行っているときに初期スレッドが条件変数をブロードキャストします。条件変数をモニタしている最初のスレッドは初期スレッドのブロードキャストを検出してクリアし,残りのスレッドを放置します。実行が妨げられ,プログラムは終了できなくなります。

例 16-1 Cのマルチスレッド・プログラムの例

3777  /* 定義  */ 
3778  #define NUM_WORKERS 2           /* ワーカ・スレッドの数    */ 
3779 
3780  /* マクロ                                                  */ 
3781  #define check(status,string) \
3782      if (status == -1) perror (string); \
3783 
3784  /* グローバル変数                                          */ 
3785  int              cv_pred1;     /* 条件変数の述語           */ 
3786  pthread_mutex_t  cv_mutex;     /* 条件変数のミューテクス   */ 
3787  pthread_cond_t   cv;           /* 条件変数                 */ 
3788  pthread_mutex_t  print_mutex;  /* プリント・ミューテクス   */ 
 
 
3789 
3790  /* ルーチン                                                */ 
3791  static pthread_startroutine_t 
3792  worker_routine (pthread_addr_t  arg); 
3793 
3794  main () 
3795     { 
3796     pthread_t  threads[NUM_WORKERS];  /* ワーカ・スレッド   */ 
3797     int        status;                /* 戻り状態値         */ 
3798     int        exit;                  /* Join終了状態値     */ 
3799     int        result;                /* Join結果値         */ 
3800     int        i;                     /* ループ索引         */ 
3801 
3802     /* ミューテクスの初期化                                 */ 
3803     status = pthread_mutex_init (&cv_mutex, pthread_mutexattr_default); 
3804     check (status, "cv_mutex initilization bad status"); 
3805     status = pthread_mutex_init (&print_mutex, pthread_mutexattr_default); 
3806     check (status, "print_mutex intialization bad status"); 
3807 
3808     /* 条件変数の初期化                                     */ 
3809     status = pthread_cond_init (&cv, pthread_condattr_default); 
3810     check (status, "cv condition init bad status"); 
3811 
3812     /* 条件変数の述語の初期化                               */ 
3813     cv_pred1 = 1;                                            (1)
3814 
3815     /* ワーカ・スレッドの作成                               */ 
3816     for (i = 0; i < num_workers; i++) {                      (2)
3817         status = pthread_create ( 
3818                         &threads[i], 
3819                         pthread_attr_default, 
3820                         worker_routine, 
3821                         0); 
3822         check (status, "threads create bad status"); 
3823         } 
3824 
3825     /* cv_pred1を偽に設定。可視性を保つためにロック内で行う。*/ 
3826 
3827     status = pthread_mutex_lock (&cv_mutex); 
3828     check (status, "cv_mutex lock bad status"); 
3829 
3830     cv_pred1 = 0;                                            (3)
3831 
3832     status = pthread_mutex_unlock (&cv_mutex); 
3833     check (status, "cv_mutex unlock bad status"); 
3834 
3835     /* ブロードキャストの実施 */ 
3836     status = pthread_cond_broadcast (&cv);                   (4)
3837     check (status, "cv broadcast bad status"); 
3838 
3839     /* 両方のワーカ・スレッドの結合を試行                      */ 
3840     for (i = 0; i < num_workers; i++) {                      (5)
3841         exit = pthread_join (threads[i], (pthread_addr_t*)&result); 
3842         check (exit, "threads join bad status"); 
3843         } 
3844     } 
3845 
3846  static pthread_startroutine_t 
3847  worker_routine(arg) 
3848     pthread_addr_t   arg;                                    (6)
3849     { 
3850     int   sum; 
3851     int   iterations; 
3852     int   count; 
3853     int   status; 
3854 
3855     /* 大量の計算を実施                                        */ 
3856     for (iterations = 1; iterations < 10001; iterations++) { 
3857         sum = 1; 
3858         for (count = 1; count < 10001; count++) { 
3859             sum = sum + count; 
3860             } 
3861         } 
3862 
3863     /* Printfはリエントラントとは限らないので,一度に1スレッドを実行 */ 
3864 
3865     status = pthread_mutex_lock (&print_mutex); 
3866     check (status, "print_mutex lock bad status"); 
3867     printf (" The sum is %d \n", sum); 
3868     status = pthread_mutex_unlock (&print_mutex); 
3869     check (status, "print_mutex unlock bad status"); 
3870 
3871     /* この条件変数のミューテクスをロックする。スレッドにより条件変数がブ*/ 
3872     /* ロックされるとpthread_condによりそのミューテクスがアンロックされる*/ 
3873 
3874     status = pthread_mutex_lock (&cv_mutex); 
3875     check (status, "cv_mutex lock bad status"); 
3876 
3877     /* 次の文では,条件待ち呼び出しのまわりをループし,その条件変数の述 */ 
3878     /* 語をチェックするのが正しい条件待ちの構文ということになります。   */ 
3879     /* そうすれば,ブロードキャスト済みの可能性がある条件変数を待った   */ 
3880     /* り,間違ったウェイクアップによって起動されるのを回避できます。そ */ 
3881     /* のスレッドがウェイクアップされ,しかもその述語が偽であれば,実  */ 
3882     /* 行が再開されます。正しい呼び出しは,たとえば次のようになります。 */ 
3883     /*                                                                  */ 
3884     /*    while (cv_pred1) {                                             */ 
3885     /*      status = pthread_cond_wait (&cv, &cv_mutex);                */ 
3886     /*      check (status, "cv condition wait bad status");             */ 
3887     /*    }                                                             */ 
3888     /*                                                                  */ 
3889     /* 次のコーディングで使用されているような直接的な呼び出しでは,     */ 
3890     /* スレッドが間違ってウェイクアップされたり,この例のワーカ・       */ 
3891     /* スレッドの1つと同様に永続的にブロックされることがあります。      */ 
3892 
3893     status = pthread_cond_wait (&cv, &cv_mutex);             (7)
3894     check (status, "cv condition wait bad status"); 
3895 
3896     /* 条件待ちでブロックされている間,そのルーチンはミューテクスを手放 */ 
3897     /* しますが,制御が戻ったらミューテクスを取り出します。             */ 
3898 
3899     status = pthread_mutex_unlock (&cv_mutex); 
3900     check (status, "cv_mutex unlock bad status"); 
3901 
3902     return (int)arg; 
3903     } 

次の番号は, 例 16-1 の番号に対応しています。

  1. main() の最初のいくつかの文では,スレッドが使用する同期化オブジェクトと条件変数に対応する述語が初期化される。それらの同期化オブジェクトは省略時の属性により初期化される。条件変数の述語は,述語のまわりをループしているスレッドがループし続けるように初期化される。プログラムのこの箇所で SHOW TASK/ALL を実行すれば, %TASK 1 が表示される。

  2. ワーカ・スレッド %TASK 2 と %TASK 3 が作成される。ここで作成された各スレッドは同じ起動ルーチン (worker_routine) を実行するので,pthread_create に対する同じ呼び出しを再使用できる。ただし,異なるスレッド ID を格納するためにわずかな違いがある。それらのスレッドは省略時の属性を使用して作成され,この例では使用されない引数を引き渡される。

  3. 条件変数に対応する述語がブロードキャストの準備のためクリアされる。この結果,条件変数によってウェイクアップされるスレッドは正しくウェイクアップされ,間違ってウェイクアップされることはない。述語をクリアすると,条件変数がブロードキャスト済みまたはシグナル通知済みとなっているので,新しいスレッドが条件変数を待つこともなくなる。期待通りの効果が得られるかどうかは,行 3893 の条件待ち呼び出しのコーディングが正しいかどうかによるが,この例のコーディングは間違っている。

  4. 初期スレッドはほとんどすぐにブロードキャスト呼び出しを実行するので,どのワーカ・スレッドもまだ条件待ちをしていない。ブロードキャストにより,その時点でその条件変数を待っているすべてのスレッドがウェイクアップされる。
    プログラマは,ブロードキャスト時にすべてのスレッドが条件変数を待っているようにするか,またはブロードキャストがすでに起こったことを対応する述語で明らかにすることによって,そのブロードキャストが確実に認識されるようにしなければならない。このような方法は,この例では意図的に省いている。

  5. 初期スレッドは,ワーカ・スレッドがどちらも正しく終了したことを確かめるために,両者を結合しようとする。

  6. ワーカ・スレッドが worker_routine を実行すると,大量の計算に時間がかかる。そのため初期スレッドは,どちらのワーカ・スレッドも条件変数を待つ準備ができていないときにその条件変数をブロードキャストする。

  7. 次にワーカ・スレッドは pthread_cond_wait 呼び出しを実行し,必要に応じて呼び出しのまわりでロックを行う。両方のワーカ・スレッドがブロードキャストを検出できなくてブロックするのはこの箇所である。そのときに SHOW TASK/ALL コマンドを入力すれば,両方のワーカ・スレッドが条件変数を待っていることが分かる。このようにプログラムがデッドロック状態になったときに制御をデバッガに戻すには,Ctrl/C を押さなければならない。

デバッガを使用すればスレッドの相対的な実行を制御することにより, 例 16-1 のような問題を診断することができます。この例の場合は,初期スレッドの実行を中断してワーカ・スレッドに計算を終了させ,ワーカ・スレッドがブロードキャスト時に条件変数を待っているようにできます。その手順は次のとおりです。

  1. デバッグ・セッションの開始時に,ブロードキャストの直前で初期スレッドの実行が中断するよう,行3836にブレークポイントを設定する。

  2. 初期スレッドを実行しワーカ・スレッドを作成するGOコマンドを入力する。

  3. すべてのスレッドの実行を中断するこのブレークポイントで, SET TASK/HOLD %TASK 1 コマンドによって初期スレッドを保留する。

  4. ワーカ・スレッドが実行を続けるように GO コマンドを入力する。初期スレッドは保留され,実行できない。

  5. ワーカ・スレッドが条件変数をブロックしているときは,その時点で Ctrl/C を押せば制御はデバッガに戻る。 SHOW TASK/ALL コマンドを実行すれば,両方のワーカ・スレッドが条件待ち副次状態で中断していることが示される。示されない場合は,それらのワーカ・スレッドを実行する GO コマンドを入力し,Ctrl/C を押してから SHOW TASK/ALL を入力する。両方のワーカ・スレッドが条件待ち副次状態になるまでこの手順を繰り返す。

  6. 最初に SET TASK/NOHOLD %TASK 1 コマンド,次に初期スレッドが実行を再開してブロードキャストを行うように,GO コマンドを入力する。これで,ワーカ・スレッドは結合し正常終了する。



16.2.2 Adaのタスキング・プログラムの例

例 16-2 はデバッグ中のタスキング・プログラムによくあるエラーを示します。この例のプロシージャ BREAK の呼び出しは,ブレークポイントを設定したり各タスクの状態を観察する候補箇所です。この例をデバッガ制御の下で実行する場合は,プロシージャ BREAK の各呼び出しの箇所で次のコマンドを入力してブレークポイントを設定し,それぞれのブレークポイントで各タスクのそのときの状態を表示できます。


DBG> SET BREAK %LINE  46 DO (SHOW TASK/ALL)
DBG> SET BREAK %LINE  71 DO (SHOW TASK/ALL)
DBG> SET BREAK %LINE  76 DO (SHOW TASK/ALL)
DBG> SET BREAK %LINE  92 DO (SHOW TASK/ALL)
DBG> SET BREAK %LINE 100 DO (SHOW TASK/ALL)
DBG> SET BREAK %LINE 104 DO (SHOW TASK/ALL)
DBG> SET BREAK %LINE 120 DO (SHOW TASK/ALL)

このプログラムでは次の 4 つのタスクが作成されます。

  • メイン・プログラム TASK_EXAMPLE を実行する環境タスク。 このタスクが最初に作成され,そのあとでライブラリ・パッケージ ( この例では,TEXT_IO ) が詳細化される。SHOW TASK を実行すれば,環境タスクのタスク ID は %TASK 1 と表示される。

  • FATHER というタスク・オブジェクト。このタスクはメイン・プログラムによって宣言され, SHOW TASK を実行すれば %TASK 2 と表示される。

  • CHILD というタスク。このタスクはタスク FATHER によって宣言され, SHOW TASK を実行すれば %TASK 3 と表示される。

  • MOTHER というタスク。このタスクはメイン・プログラムによって宣言され, SHOW TASK を実行すれば %TASK 4 と表示される。

例 16-2 Adaのタスキング・プログラムの例

 1 -- Tasking program that demonstrates various tasking conditions. 
 2 
 3 package TASK_EXAMPLE_PKG is 
 4    procedure BREAK; 
 5 end; 
 6 
 7 package body TASK_EXAMPLE_PKG is 
 8    procedure BREAK is 
 9    begin 
 10      null; 
 11   end; 
 12 end; 
 13 
 14 
 15 with TEXT_IO; use TEXT_IO; 
 16 with TASK_EXAMPLE_PKG; use TASK_EXAMPLE_PKG; 
 17 procedure TASK_EXAMPLE is (1)  
 18 
 19    pragma TIME_SLICE(0.0); -- Disable time slicing. (2)
 20 
 21    task type FATHER_TYPE is 
 22       entry START; 
 23       entry RENDEZVOUS; 
 24       entry BOGUS; -- Never accepted, caller deadlocks. 
 25    end FATHER_TYPE; 
 26 
 27    FATHER : FATHER_TYPE; (3)
 28 
 29    task body FATHER_TYPE is 
 30       SOME_ERROR : exception; 
 31 
 32       task CHILD is  (4)
 33          entry E; 
 34       end CHILD; 
 35 
 36       task body CHILD is 
 37       begin 
 38          FATHER_TYPE.BOGUS;   -- Deadlocks on call to its parent 
 39       end CHILD;              -- (parent does not have an accept 
 40                               -- statement for entry BOGUS). Whenever 
 41                               -- a task-type name (here, FATHER_TYPE) 
 42                               -- is used within a task body, the 
 43                               -- name designates the task currently 
 44                               -- executing the body. 
 45    begin -- (of FATHER_TYPE body)  
 46 
 47       accept START do    
 48          BREAK;   -- Main program is waiting for this rendezvous to 
 49                   -- complete; CHILD is suspended when it calls the 
 50                   -- entry BOGUS. 
 51          null; 
 52       end START; 
 53 
 54       PUT_LINE("FATHER is now active and"); (5)  
 55       PUT_LINE("is going to rendezvous with main program."); 
 56 
 57       for I in 1..2 loop 
 58          select 
 59             accept RENDEZVOUS do 
 60                PUT_LINE("FATHER now in rendezvous with main program"); 
 61             end RENDEZVOUS; 
 62          or 
 63             terminate; 
 64          end select; 
 65 
 66          if I = 2 then 
 67             raise SOME_ERROR; 
 68          end if; 
 69       end loop; 
 70 
 71    exception   
 72       when OTHERS => 
 73          BREAK;   -- CHILD is suspended on entry call to BOGUS. 
 74        -- Main program is going to delay while FATHER 
 75                   -- terminates. 
 76                   -- MOTHER is ready to begin executing. 
 77          abort CHILD; 
 78          BREAK;   -- CHILD is now abnormal due to the abort statement. 
 79 
 80          raise; -- SOME_ERROR exception terminates FATHER. 
 81    end FATHER_TYPE; 
 82 
 83 begin    -- (of TASK_EXAMPLE)  (6)  
 84 
 85    declare 
 86       task MOTHER is  (7)
 87          entry START; 
 88          pragma PRIORITY (6); 
 89       end MOTHER; 
 90 
 91       task body MOTHER is 
 92       begin 
 93          accept START; 
 94          BREAK;   -- At this point, the main program is waiting for 
 95                   -- its dependents (FATHER and MOTHER) to terminate. 
 96                   -- FATHER is terminated. 
 97          null; 
 98       end MOTHER; 
 99    begin  (8)
100 
101 
102       BREAK;   -- FATHER is suspended at accept start. 
103                -- CHILD is suspended in its deadlock. 
104                -- MOTHER has activated and ready to begin executing. 
105       FATHER.START;  (9)        
106       BREAK;   -- FATHER is suspended at its 'select or terminate' 
107                -- statement. 
108 
109 
110       FATHER.RENDEZVOUS;  
111       FATHER.RENDEZVOUS;  (10)
112       loop  (11)               
113          -- This loop causes the main program to busy wait 
114          -- for the termination of FATHER, so that FATHER 
115          -- can be observed in its terminated state. 
116          if FATHER'TERMINATED then 
117             exit; 
118          end if;       
119          delay 1.0; 
120       end loop; 
121 
122       BREAK;   -- FATHER has terminated by now with the unhandled 
123                -- exception SOME_ERROR. CHILD no longer exists 
124                -- because its master (FATHER) has terminated. Task 
125                -- MOTHER is ready. 
126       MOTHER.START; (12)     
127          -- The main program enters a wait-for-dependents state, 
128          -- so that MOTHER can finish executing. 
129    end; 
130 end TASK_EXAMPLE; (13)


目次 索引

印刷用画面へ
プライバシー 本サイト利用時の合意事項