4

C を使用して、ARM コア上にシステム (つまり、組み込みシステム) を構築しています。問題は、再突入の問題を正式な方法でどのように回避し、すべての再突入バグが削除されたと確信できるかです。これは実際的な希望ではないかもしれませんが、どのシステムにとっても確かに重要だと思います。

議論のために、UMLダイアグラムを描くか、完全なステートマシンを用意するのが良い出発点だと思います(しかし、システム全体が開発された後にそれを生成する方法は?)。ステート マシン/UML ダイアグラムを使用して分析を行う方法について何か提案はありますか?

4

4 に答える 4

4

Quick fix, if you suspect something:

int some_func(int x, int y)
{
    static volatile int do_not_enter_twice = 0;
    assert(!(do_not_enter_twice++));

    /* some_func continued */

    do_not_enter_twice--;
    return whatever;
}

Longer answer:
Use some tool to make a call graph and continue manually from there.

于 2010-01-25T13:21:27.133 に答える
4

解決したい問題について正確にはわかりませんが、知識に基づいた推測をさせてください。

最初のポイントは、問題になる可能性のある機能を特定することです。再入は、いくつかのネストされた呼び出しを通過し、コールバック/依存性注入によって隠されることさえある再帰呼び出しによって、または複数のスレッド内で使用される関数によって発生します。

ダイレクト コール グラフを描画できます。関数 A は B と C を呼び出し、関数 B は D、E と F を呼び出し、関数 C は何も呼び出さない、などとします。マルチスレッド時はスレッドごとに描画します。グラフにサイクルがある場合、このサイクルを作成するすべての関数は再入セーフである必要があります。この場合、サブブランチは無視できます。複数のスレッドで使用される関数も安全である必要がありますが、各スレッドが現在どこにあるかを正確に把握できないため、すべてのサブブランチが含まれるようになりました。ロックが使用されると事態は複雑になりますので、ここでは無視しておきましょう。

このステップは、コード分析ツールによって確実に自動化できます。

機能が特定されたので、

  • 関数ローカル データのみに依存する関数は通常安全です。これは、関数型プログラミングの優れた特性の 1 つです。
  • 外部データに依存する関数 (関数の範囲外) は綿密に調査する必要があります。外部データがいつ、どこで変更されたかを知ることは特に重要です。
  • 外部データを変更する関数は、特にマルチスレッドの場合、赤旗を立てて大きな警報サイレンを作動させる必要があります。
于 2010-01-25T13:26:09.447 に答える
2

膨大なコール グラフを計算できるツールは、 DMS Software Reengineering Toolkitとその C フロント エンドです。C フロント エンドは、C コードの解析に使用されます。DMS には、制御とデータ フローの分析、ポイント ツー分析を計算し、call-direct および call-indirect-thru-pointer ファクトを抽出するための組み込みの機械があります。

DMS は、3,500 万行の C コード (= 250,000 関数) の C ソース コード システムのコール グラフを構築し、そのコール グラフから情報を抽出するために使用されてきました。このような大規模なグラフを作成する際の重要な問題は、間接的な関数呼び出しが最小限の数の偽陽性のターゲットを保守的に対象とするように、ポイントツー情報を実際に可能な限り正確に計算することです (これを完全に実行するには、理論上の厳しい制限があります)。

あなたの場合、抽出する情報は、他の著者が示すように、「サイクルはありますか?」です。このコールグラフで。

この規模では、これを手動で行うことは望ましくなく、製品ビルドの準備が整うたびにやり直す必要があります。したがって、チェックを機械化することは非常に理にかなっています。

于 2010-01-30T04:37:37.387 に答える
0

コードをチェックするために 2 つのことを行います。徹底的なグループ コード レビュー (スタイルやその他のエラーではなく、再入エラーのみを見つけることを目的としています)。第二に、問題に対する実際的な攻撃。

例えば:

int myfunction(int x, int y) {
    REENTRANCE_CHECK;
    ... body of function
}

REENTRANCE_CHECK を #define して空にする (本番用) か、関数が再入力されないようにチェックするコードを指定できるようになりました。これらのチェックを有効にしてテストを実行し (テストがない場合は、デバッガーが接続されたデバイスで実行します)、問題が発生するかどうかを確認します。

同様に、デバッグ ロジックを追加して、グローバル状態への不適切な更新を検出できます。ロックを使用するコードを記述します (ロックが既に保持されているときに取得された場合にアサートします。

このようなもの:

int my_global;
DEFINE_GLOBAL_LOCK(my_global);

void my_dangerous_function() {
    ...
    LOCK_GLOBAL(my_global);
    .. some critical section of code that uses my_global.
    UNLOCK_GLOBAL(my_global);
    ...
}

繰り返しになりますが、DECLARE_GLOBAL_LOCK、LOCK_GLOBAL、および UNLOCK_GLOBAL は、テスト用に実際のロック コード (もちろん、これを作成する必要があります) として #defined にすることも、本番環境では何も #defined にすることもできます。

このアプローチは、グローバル状態へのすべてのアクセスを見つけてラップする場合にのみ機能しますが、検索を使用すると簡単です。

于 2010-01-30T04:23:55.257 に答える