RTX64開発の洗礼「グリーンスクリーン」とは?INtimeとの決定的な違い

コーディング

日本のリアルタイムOSの採用は、INTimeが多いと聞きます。(Windowsの場合ね)  読者もINtimeを使っているけど、RTXのSDKどうなの?と疑問を持たれているか方へ向けて記事を書きました。

INtimeでメモリアクセス違反が起きても、エラーコード E_BAD_ADDR が返ってくるだけでシステムは動き続けます。ところがRTX64では同じバグを書いた瞬間、画面が止まります。マウスやキーボードで救済などは出来ません。これが「グリーンスクリーン」です。INtimeから移行したエンジニアが最初に直面するこの洗礼、その正体と読み方を解説します。

INtimeのメモリエラー処理:「エラーコードを返す」モデル

INtimeはオブジェクト指向カーネルをベースにしており、プロセス間のメモリ空間が厳密に分離されています。あるプロセスが不正なアドレスにアクセスしようとすると、カーネルはそのアクセスをブロックし、呼び出し元にエラーコードを返します。

  • E_BAD_ADDR:不正なアドレスへのアクセス
  • E_MEM:メモリ不足

このモデルの特徴はプロセス隔離が徹底されている点です。あるプロセスでメモリエラーが起きても、他のプロセスやOSそのものは継続して動作します。開発中は「エラーコードをチェックし忘れた」というバグが多くなりますが、システム全体が止まることは基本的にありません。

rtssで実行エラーを出してみた

以下を実行します。

// 【危険】整数型のゼロ割り算
int a = 10;
int b = 0;

int c = a / b; // ここでプログラムが強制終了する!

エラーが起こっても、対象のリアルタイムスレッドのみ停止します。ゼロ割り算(Divide by Zero Exception)を行うと以下の画面が出ますね! ほんと、この表示が出るのはすごくありがたい。

Delete Process ボタンで消して、もう一度INtimeの実行ファイルを動かすことが出来ます。

RTX64のグリーンスクリーンとは

RTX64はWindowsのHAL(ハードウェア抽象化層)を拡張してリアルタイムサブシステム(RTSS)を実現しています。RTSSプロセス内でメモリアクセス違反が発生すると、INtimeのように「エラーコードを返す」のではなく、RTX64独自のクラッシュ画面「グリーンスクリーン」が表示され、RTSSが停止します。

グリーンスクリーンはWindowsのブルースクリーン(BSOD)と似ていますが、別物です。

画面発生主体表示色影響範囲
グリーンスクリーンRTX64(RTSS)RTSSが停止。Windowsは継続。
ブルースクリーン(BSOD)WindowsWindowsが停止。RTSSは動き続ける

この逆転関係がRTX64の大きな特徴です。WindowsがBSODで死んでも、RTSSは動作を継続できます。これはリアルタイム制御システムにとって非常に重要な設計上のメリットです。

グリーンスクリーンを引き起こす主な原因

グリーンスクリーンにはRTX64独自のエラーコードが対応しています。INtimeユーザーが特に注意すべき代表的なものを示します。

コード内容INtimeでの相当
RTSS_2_000EMemory Access Fault(メモリアクセス違反)E_BAD_ADDR 返却(システム継続)
RTSS_2_000CStack Fault(スタック破壊)警告 → プロセス停止
RTSS_2_000DGeneral Protection Fault(GPF)プロセス例外
RTSS_3_0005Access ViolationE_BAD_ADDR 返却

特にStack Fault(RTSS_2_000C)は即死です。スタックが破壊されるとRTSSは回復不可能と判断し、グリーンスクリーンを即座に発生させます。INtimeではスタックオーバーフローでも比較的穏やかに処理されるケースがありましたが、RTX64では容赦なく停止します。スタックサイズの設計は移行時に特に注意が必要です。

例外コードの違いに注意

INtimeとRTX64では、同じハードウェア例外でも例外コードの名前が異なります。これが移行時の混乱を生みやすいポイントです。

状況Windowsの例外コードRTX64(RTSS)の例外コード
不正メモリアクセスEXCEPTION_ACCESS_VIOLATIONEXCEPTION_PAGE_FAULT
不正命令EXCEPTION_ILLEGAL_INSTRUCTIONEXCEPTION_INVALID_OPCODE

RTX64の例外コードはIntel CPUの割り込みベクタ番号に直接対応しているため、Windowsとは異なる名称になっています。SEH(構造化例外処理)でキャッチする際は、この違いを意識してコードを書く必要があります。

構造化例外処理(SEH)でグリーンスクリーンを防ぐ

RTX64はWindowsのSEH(Structured Exception Handling)をRTSSでも使用できます。__try / __except を使ってメモリアクセス例外をキャッチし、グリーンスクリーンを回避する実装例を示します。

EXCEPTION_Test.cpp

// SEH exception handling example for RTX64 (C++)
#include <windows.h>
#include <RtAPI.h>

void SafeMemoryAccess(volatile int* pAddr)
{
	__try
	{
		int value = *pAddr;  // potentially invalid address
		RtPrintf("Read value: %d", value);
	}
	__except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
		? EXCEPTION_EXECUTE_HANDLER
		: EXCEPTION_CONTINUE_SEARCH)
	{
		RtPrintf("Memory access violation detected (RTX64)");
	}
}

注意:INtimeで EXCEPTION_ACCESS_VIOLATION を使ってキャッチしていたコードは、RTX64では EXCEPTION_PAGE_FAULT に書き換える必要があります。そのままではキャッチできずグリーンスクリーンになります。

注意事項

項目内容
C++例外との混在__try ブロック内でC++例外(throw)を使うと動作が不定になる。RTSS内ではC++例外より SEH を優先する
デストラクタの呼び出し/EHa なしビルドでは __try スコープ内のC++自動変数のデストラクタが呼ばれない。RTSSビルドの例外フラグを vcxproj で確認すること
ループ内の多用は避ける制御周期の __try/__except は毎サイクル入る。フィルター関数内を軽量に保つこと(RtPrintf 等のI/Oは __except ハンドラ側のみに置く)
スタック破壊には無力スタックオーバーフローなど、スタック自体が壊れた場合はSEHでも回収できない

BSODが起きてもRTSSを動かし続ける — ShutdownHandler

RTX64の重要な機能として、Windows BSODが発生してもRTSSプロセスを継続動作させる RtAttachShutdownHandler があります。実機制御ではWindowsが落ちても安全にモーターを停止させるなど、緊急処理が必要なシーンで活用します。

// RTX64 SDK 4.3 ShutdownHandler 最小実装例
#include <windows.h>
#include <rtapi.h>
#ifdef UNDER_RTSS
#include <rtssapi.h>
#endif

static volatile LONG g_bShutdown      = 0;
static HANDLE        g_hShutdownEvent = NULL;
static HANDLE        g_hHandler       = NULL;


static VOID RTFCNDCL ShutdownHandler(PVOID pContext, LONG reason)
{
    (void)pContext;

    if (reason != SHTDN_REASON_WINDOWS_STOP)
        return;  // BSOD以外は通常終了経路に任せる

    InterlockedExchange(&g_bShutdown, 1);

    // ここでモーター停止・バルブクローズ等の緊急処理
    // ※ new/delete・ファイルI/O・Win32サービスは使用不可

    if (g_hShutdownEvent != NULL)
        RtSetEvent(g_hShutdownEvent);  // ※ RtPulseEvent は使わない
}

int main()
{
    // ★ RtCreateEvent/RtCloseHandle を使う (Win32のCreateEvent/CloseHandleは不可)
    g_hShutdownEvent = RtCreateEvent(NULL, TRUE, FALSE, NULL);
    if (g_hShutdownEvent == NULL)
        return -1;

    // ★ 引数は5つ、戻り値のHANDLEを保存する
    g_hHandler = RtAttachShutdownHandler(
        NULL,            // pThreadAttributes
        32 * 1024,       // StackSize
        ShutdownHandler,
        NULL,            // Context
        RT_PRIORITY_MAX  // Priority
    );
    if (g_hHandler == NULL)
    {
        RtCloseHandle(g_hShutdownEvent);
        return -1;
    }

    // 制御ループ
    while (InterlockedCompareExchange(&g_bShutdown, 0, 0) == 0)
    {
        // 通常の制御処理
        RtSleepFt(10000);  // 1ms (10000 × 100ns)
    }

    RtWaitForSingleObject(g_hShutdownEvent, 5000);  // 緊急停止完了待ち

    // ★ RtReleaseShutdownHandler でハンドラを解除してからイベントを閉じる
    RtReleaseShutdownHandler(g_hHandler);
    RtCloseHandle(g_hShutdownEvent);
    return 0;
}

重要:ShutdownHandler内ではWindowsのサービスや非リアルタイム関数は使えません。RTSS固有のリアルタイム関数のみ使用可能です。

デバッグ技術の集大成として、WindowsがBSODで死んでもRTSSを動かし続ける設計パターンを整理します。

  • ShutdownHandler内でWindowsのAPIを呼ばないprintfmallocCreateFile 等はBSOD後にハングの原因になる。
  • main終了前にShutdownHandlerの処理を待つ:mainが先に終了するとShutdownHandlerが返せずプロセスがハングする。
  • ShutdownHandler内でスレッドを新規作成しない:BSOD後は新規スレッド作成が不可。事前に作成して待機させておく設計にする。

RTXにおけるBSODテストの確認ポイント

BSODが発生した瞬間、Windowsの画面は青くなりますが、RTX側の ShutdownHandler は裏でひっそりと動いています。画面に RtPrintf の文字は出ないため、ハンドラが本当に動いたかどうかは外部機器の挙動で確認する必要があります。

動作確認:Microsoft公式ツール「NotMyFault」を使用する

MicrosoftのSysinternalsチームが提供している、意図的にシステムをクラッシュさせるための公式テストツールです。レジストリをいじる必要がないため、最も手軽で確実です。

手順:

  1. Microsoftの公式サイト(Sysinternals)から NotMyFault をダウンロードします。
  2. ZIPファイルを解凍します。
  3. 64bitOSであれば NotMyFault64.exe管理者権限で実行します。
  4. 小さなダイアログが表示されます。クラッシュの要因(High IRQL fault など、どれでも構いません)を選択します。
  5. 「Crash」ボタンをクリックします。
  6. 即座にBSODが発生します。

まとめ

  • INtimeはエラーコード返却モデルE_BAD_ADDR などが返りシステムは継続。RTX64はグリーンスクリーンでRTSSが停止する。
  • グリーンスクリーン ≠ ブルースクリーン:グリーンはRTSS停止・Windows継続。ブルーはWindows停止・RTSS継続という逆転関係がある。
  • Stack Faultは即死RTSS_2_000C は回復不可。スタックサイズの設計を丁寧に行う。
  • RtAttachShutdownHandler を必ず実装する:BSOD後もRTSSを安全に動かし続けるための必須パターン。

RTXで新しい機能の開発を行っていると、グリーンスクリーン見るようになります。Windows再起動しか選択肢がないのはつらいですね。必要に迫られてメモリーダンプを読んだりもします。開発かんばていきましょう!

コメント

タイトルとURLをコピーしました