高度な検索
Developer Connection
Member Login ログイン | ご入会 ADC連絡先

Technote 1137

Disabling Interrupts on the Traditional Mac OS

目次

はじめに

論拠

背景となる素材

InterruptDisableLibの使用

できれば避ける

まとめ

のテクノートでは、伝統的Mac OSの割り込みをディゼーブル(Disable,割り込み不能にすること)する方法を説明します。また割り込みをディゼーブルを避けなければいけない理由について詳しく説明し、割り込みのディゼーブルを避けるための他のシステムサービスについて概説します。

このノートは、デバイスドライバとか、Mac OSの割り込み時間をかなり利用するアプリケーションなどの、カーネルレベルのソフトウェアを作る開発者を対象としています。一般的には、アプリケーションの開発者は割り込みをディゼーブルする必要はありません。

 

はじめに

DTSはサードパーティの開発者に対して、Mac OSの割り込みはディゼーブル(禁止)しないようにお願いしていますが、割り込みをディゼーブルすることが随一の解決法である状況もあることは認識しています。このノートの目的は、割り込みのディゼーブルに対する代替案を示すことと、このような代替案が要求に合わない場合、この危険な作業を行う時の失敗を、最小にすることを目的としています。このノートは、安易に割り込みのディセーブルをすることを勧めるものでは決してありません

このノートは以下の4つのセクションに分かれます:

  • 第1セクション−「論拠」は、割り込みのディゼーブルに対するわれわれの立場の根拠を説明します。
  • 第2セクション−「背景となる素材」は、伝統的なMac OSの割り込みアーキテクチャがどのように動作するか説明し、またPowerPCのコンピュータのエミュレーションでどのように割り込みが扱われるかも説明します。680x0の割り込みアーキテクチャを良く知っている方は、飛ばしてもかまいません。
  • 第3セクション−「InterruptDisableLibの使用」は、このノートに付属する割り込みマスク処理ライブラリを説明します。
  • 最終セクション−「代替案」はMac OSの割り込みのディゼーブルに替わる方法を説明します。代替案を受け入れられるかどうか調べるため、開発者は是非ともここを読んで下さい。

 

論拠

DTSが、それほどまでに割り込みのディゼーブルに反対するの理由は何でしょうか。なぜならば:

  • システム全体としては有害です
  • あなたのコードの速度を低下させます
  • 既存のシステム関数により、使わずにすませることが多くあります

この章では、それぞれの項目を順に説明して行きます。

遅延

割り込みをディゼーブルすることで、割り込みによるシステムの遅延が増加します。これは、システムの機能としては悪いことで、例えばサウンドマネージャ(Sound Manager)は正しく動作するために、かなりの割り込みの遅延を必要としてしまいます。奥歯に何かが挟まった感じがすると思いますが、DTSテクノートHW16"I Was a Teenage DMA Junkie"にこの問題の背景が説明しています。

性能への悪影響

以下のセクションで説明するように、Mac OSでは、素早く割り込みをディゼーブルする方法がありません。PowerPCコードで動作するプログラムの場合、割り込みをディゼーブルするためにミックストモードマネージャ(Mixed Mode Manager)でのモードスイッチが必要です。680x0ソフトを作成している場合、実680x0プロセッサでの仮想メモリーで動作する可能性があり、その場合SRレジスタの変更は特権操作で、実はシステムがエミュレートしています。どちらの場合でも性能へ悪影響をあたえます。

避ける可能性

Mac OSは、割り込みをディゼーブルしないですむ、低レベル基本操作を数多く提供しています。それらのルーチンの多くは、最終的には割り込みをディゼーブルするというのは事実ですが、それらを使うことで時間が節約でき、またアップルが「あなたの背後で」システムの改良をする余地がでます。

 

背景となる素材

このセクションでは伝統的Mac OSの割り込みアーキテクチャについて説明しますが、それは680x0の割り込みアーキテクチャを直接引き継いでいるものです。もし、680x0の割り込みアーキテクチャを良く知っているのであれば、この章は飛ばしてください。

680x0の割り込みアーキテクチャについて

680x0のSRレジスタには、0から7までの値の、現在の割り込みマスクを決定する3ビットのフィールドが含まれています。入ってくる割り込みの優先順位(割り込みレベル)が現在の割り込みマスクより大きい場合、680x0(あるいはPowerPCコンピュータで動作している場合、エミュレートされている680x0)は割り込みレベルをそのレベルに上げ、割り込みを処理します。

次の表は、さまざまな割り込みレベルの通常の使用法をまとめています。

割り込みレベル

典型的な使用法

0

普通のアプリケーションレベルのコード

1

ADB

2

SCC, NuBus, PCI

3

Built-in Ethernet

4

ロジックボードのDMAチャネル割り込み

5

未使用

6

未使用

7

マスク不可割り込み(Non-Maskable Interrupts,NMI)−通常プログラマスイッチ

重要:
上の表は解説のためだけのものです。実行しているハードウェアに関する深い知識がなければ、割り込みレベル7以外の割り込みマスクを陽に使わないでください。ここで割り込みレベル7のマスクは、マスク可能割り込みの全てをディゼーブルすることを意味します。

注意:
一般的にいって、割り込みマスクXは、レベルXとそれ以下のレベルの割り込みを全てディゼーブルします。しかしNMIは割り込みレベル7でもマスクされません。

680x0のSRレジスタは特権レジスタで、680x0のスーパバイザモードで動作するコードでのみ操作できます。ユーザモードからSRレジスタをアクセスすると、680x0は特権破壊例外を受け取ります。伝統的なMac OSではこの例外を補足し、当該の違反命令をエミュレートします。それで、例外処理による性能の低下はありますが、680x0ソフトウェアから、制限を無視してSRレジスタを操作することができます。

SRレジスタに関する、さらなる情報に関してはDTSテクノート1094"Virtual Memory Application Compatibility"を参考にしてください。

PowerPCの割り込みレベル

生のPowerPCプロセッサは、ただ一つの割り込み状態ビットしか持ちません。つまり割り込みはマスクされるかされないかの二つに一つです。割り込みマスクビットは特権ビットで、それにアクセスするためにはネーティブPowerPCプロセッサのネーティブスーパバイザモードで動作しなければなりません。しかしながら、MacOSは(次のセクションで説明するナノカーネルを除いて)全てのPowerPCコードをユーザモードで実行し、従ってPowerPCコードから割り込みビットはアクセスできません。

PowerPCでのMac OSでの割り込みアーキテクチャ

上で説明したように、680x0とPowerPCマイクロプロセッサの割り込みアーキテクチャは全く異なっています。PowerPCベースのコンピュータでは、680x0のアーキテクチャの割り込みアーキテクチャをエミュレートし、それがPowerPCの割り込みアーキテクチャの機能の鍵となります。これは、かなりの量のコード(伝統的Mac OSのコードとサードパーティのコード)が、特定のレベルをマスクした割り込みのディゼーブルをしているからです。

例えば、伝統的Mac OSのシリアルドライバを考えてみます。シリアル割り込みは、レベル2で起こり、シリアルドライバの割り込みハンドラがそれを検知するので、割り込みをレベル2以上に維持する限り、それ以外のシリアル割り込みは起こりません。多くのドライバはこのような仮定に基づいて、グローバルデータ構造をアクセスするときの並行アクセス制御(concurrency control)を行います。

 

注意:
上はあくまでも例として説明したものです。現在のMac OSコンピュータでは、シリアルドライバは必ずしもレベル2で割り込みを発生する必要はありません。詳細は「680x0の割り込みレベル」を見て下さい。

互換性を保つためPowerPCコンピュータでの割り込みは、全て680x0ベースのコンピュータであるかのように優先順位が付けられます。外部ハードウェアがPowerPCプロセッサに割り込みをかけたとき、割り込みは最初にナノカーネル(nanokernel)で処理され、ナノカーネルはマシンの状態にしたがい、以下の2つのうち一つの動作をします。

  1. マシンが現在(エミュレータにより)680x0ソフトウェアを実行している場合、ナノカーネル(nanokernel)はエミュレータに特定の優先順位の割り込みが発生したことを知らせ、外部割り込みハンドラから制御が戻ります。次にエミュレータが680x0の命令(動的再コンパイルエミュレータの場合、基本ブロック)を終了した後、ナノカーネルは割り込みを通知します。割り込みレベルがマスクされていない場合、エミュレータは680x0例外フレームを作り、680x0ベクタベースレジスタ(Vector Base Regiser,VBR)で指され680x0割り込みハンドラを呼ぶことで、伝統的な手続きと同様な処理をします。
  2. 一方、現在PowerPCコードを実行している場合、ナノカーネル(nanokernel)はエミュレータコンテキストをスィッチし、割り込みを処理する特殊な例外を発生させます。

エミュレータは、割り込みの観点からはあたかも680x0プロセッサであるかのように、680x0命令をアトミックな命令(訳注:命令が中断して他の命令が実行されないこと)として実行します。このことで680x0での割り込みハンドラのコードが、暗に仮定しているアトミック性を保存します。動的再コンパイルエミュレータの場合は、それが作り出すネーティブコードはより大きいため、割り込みをチェックする間隔もより粗くなります。

まとめると、Power Macintoshでの割り込みは、全てナノカーネル(nanokernel)によりエミュレータへ導かれ、680x0の割り込みレベルと680x0の命令の個別性(訳注:それぞれの命令が中断して他の命令が実行されないこと)を保つような忠実なエミュレーションが行われます。

InterruptDisableLibの理論的背景

上の議論により2つの重要な結論が導かれます。最初にネーティブPowerPCコードは特権レジスタにアクセスできないため、ネーティブPowerPCの割り込みをマスクできません。第2に680x0ソフトウェアから割り込みをディゼーブルすると、実際にはマシンの全ての割り込みをディゼーブルしてしまうことで、その理由は全ての割り込みをエミュレータが取り扱うからです。この2つの事実を組み合わせると以下の結論が得られます。

Mac OSの割り込みをディゼーブルする随一の方法は、680x0のSRレジスタの割り込みマスクを変更することです。これはプログラムがPowerPCコードにコンパイルされていても同じです。

このため、割り込みをディゼーブルしたいコードには、2つのコードパスがあるということになります。680x0コードパスでは、SRレジスタを直接変更します。PowerPCコードパスでは"MixedMode.h"ルーチンのCallUniversalProcを使いSRレジスタを変更する680x0コードを呼ばなければならないということです。

 

注意:
通常の環境で、CFM-68KコードはCFM-PPCコードと全く同じに振る舞います。つまり古典的68Kコードを呼ぶときにはミックスト・モード・マネージャ(Mixed Mode Manager)を呼ばなければなりません。しかし、割り込みをディゼーブルするときに限り、CFM-68Kコードは古典的680x0コードと同じパスを取ることができます。CFM-PPCコードとは異なり、CFM-68Kコードは680x0のSRレジスタを直接アクセスすることができ、ミックスト・モード・マネージャ(Mixed Mode Manager)によるモードスィッチを避けることができます。

 

InterruptDisableLibの使用

このテクノートに付属してInterruptDiableLibというサンプルコードを付けました。コードは、これまでのセクションで説明した技法を使い、古典的68Kコード、PowerPCコードそしてCFM-68Kコードから簡単に680x0割り込みマスクを制御するためのものです。コードは、あなたのプロジェクトとして簡単に使えるように完全なライブラリとして構成されていて、CおよびPascalにインターフェースがついていて、Cでインプリメントされています。

ライブラリはMetrowerks CodeWarrior ProのCとPascalでコンパイルされていますが、このソースを別のC,C++あるいはPascalコンパイラで使うことができると思います。プロジェクトでこのライブラリを使用する場合、次のステップに従ってください。

  1. "InterruptDisableLib.c"ファイルをプログラムに付け加えてください。もし、あなたが統合開発環境がCコードをサポートする場合、直接プロジェクトに付加することができると思います。それ以外の場合、ファイルを別にコンパイルしてオブジェクトファイルとして付け加える必要があります。
  2. 適切なインターフェースファイルをインクルードしてください。C/C++プログラムの場合、"InterruptDisableLib.h"をインクルードしてください。Pascalプログラムの場合、"InterruptDisableLib.p"を使ってください。
  3. 割り込みをディゼーブルする場合、以下のようにして下さい:
    oldMask = SetInterruptMask(7);

    // 割り込みはディゼーブルされました。個別の処理をいれてください。

    (void) SetInterruptMask(oldMask);

ライブラリ・リファレンス

ライブラリは以下の2つのエントリーポイントを持ちます:

extern pascal UInt16 GetInterruptMask(void);

extern pascal UInt16 SetInterruptMask(UInt16 newMask);

GetInterruptMaskは0から7までの現在の割り込みマスクを返します。SetInterruptMaskは、現在の割り込みマスクとして0から7までの値を設定します。さらにSetInterruptMaskは、直前の割り込みマスクを返します。

要注意事項

このセクションには、いくつかの重要なルールが含まれています。このルールを破らなければならない場合もありますが、ルールを破る前にやよくよく注意して考えて下さい。

  • 決して、決して、決してGetInterruptMask関数を使って自分のコードが"割り込み時"に動作しているかどうか判定しないでください。Mac OSコンピュータでは"割り込み時"に走りながら割り込みをイネーブルにする(割り込みマスク0)(訳注:割り込み処理を可能とする)場合が数多くあります。VBLタスク、遅延タスク(Deffered Task)とPCI2次割り込み(Secondary Interrupt)が含まれています。
  • 決して割り込みハンドラのなかで、割り込みマスクを下げないでください。例えば、割り込みマスクが2に設定して割り込みハンドラに入った場合、決して割り込みマスクを下げないでください。理由ですか。割り込みレベルを1に設定して、再入を防いでいるハードウエアドライバがあるとします。このときあなたの割り込みハンドラがレベル2で、割り込みマスクを0に設定してしまったとしたら、他のドライバにとって不都合な割り込みが発生してしまいます。
  • 必ず古い割り込みマスクを保存してください。決して、特定の割り込みレベルで動作していると仮定して、明示的にそのマスクに設定しないでください。かならず、古い割り込みマスクを保存してハンドラが終了するときにそのマスクを回復してください。

 

できれば避ける

割り込みをディゼーブルする前に、以下のシステムサービスを使えるかどうか研究する必要があります。

OSユーティリティ

Mac OSの全てのアトミック操作の祖先はOSユーティリティルーチンのEngueueとDequeueです。これらのルーチンは全てのMac OSコンピュータで使用することができ、簡単なアトミックなキュー操作を提供しています。しかし、これは680x0コードでインプリメントされていて、使用する時にはミックスト・モード・マネージャ(Mixed Mode Manager)によるモードスィッチが発生します。

遅延タスク(Deffered Task)

クリティカルセクション(訳注:他のプロセスから割り込まれて欲しくない部分)を全て遅延タスクに置けば、遅延タスクマネージャ(Deffered Task Manager)が全ての遅延タスクの直列化を保証してくれます。

Open Transportのユーティリティ

Open Transportは豊富なカーネルサービスを提供していて、以下のような割り込みに対して安全な構成要素が使用できます:

  • LIFOキュー
  • アトミックなビット演算と算術演算
  • アトミックな比較とスワップ
  • Open Transportの遅延タスク
  • OTGate(はクリティカルセクションをサポートします)
  • メモリ割り当て

これらのOTの基本演算は全て完全なPowerPCのネーティブコードでインプリメントされています。

DriverServicesLib

PCIデバイスドライバを作成している場合、DriverServicesLibには数多くの低レベルキュー操作とアトミック操作があります。

DriverServicesLibのキュー操作ルーチンはOSユーティリティルーチンと同様なものですが、空のキューに要素を追加する場合とキューからひとつの要素を削除する場合には、ミックスト・モード・マネージャ(Mixed Mode Manager)のモードスイッチが発生しません。

680x0アトミック命令

上で説明したように680x0命令はPower Macintoshでエミュレーションされた場合でもアトミックです。それ故、addqとbsetなどのごく普通の680x0命令は、680x0コンピュータでもPowerPCベースコンピュータでもアトミック操作となります。

PowerPCアトミック命令

PowerPCプロセッサには2つの特殊な命令、Load Reserved(lwarx)とStore Conditional(stwcx)があり、それによりアトミック操作を実現することができます。ほとんどのコンパイラでは、固有な関数でその命令にアクセスできます。

重要:
DTSではPowerPCのLoad ReservedとStore Conditinal命令の使用は避けて下さいとデベロッパにお願いしています。これには2つの理由があります。最初に、これらの命令は、その本来の性質としてプロセッサ特有のものでコードの移植性を損ないます。次に、これらの命令の動作はPowerPCのタイプにより変化します。これら全てに対応するのは、かなりきわどいことになります。これらの命令はOpen TransportのDriverServicesLibのアトミックルーチンほどに利便性はなく、Open Transportのアトミックルーチンに関しては、全てアップルがうまく動くように更新することを約束しています。

 

まとめ

DTSは割り込みのディゼーブルを避けるようにデベロッパにお願いしています。Mac OSは割り込みのディゼーブルに対する代替案を数多く提供しています。このような代替案がすべてデベロッパの要求を満足しないとき、680x0のSRレジスタの割り込みマスクを設定することで割り込みのディゼーブルが可能です。PowerPCコードから割り込みをディゼーブルするには、ミックスト・モード・マネージャ(Mixed Mode Manager)から680x0ソフトウェアを呼ばなければなりません。この場合InterruptDisableLibのコードを利用して、よくある間違いを繰り返さないようにしてください。またこのテクノートの注意事項を良く読んで下さい。

 

参考文献

更新日: 1998 年 8 月 31 日