Linux には、学ぶ価値のあるプログラミングのアイデアがたくさんあります。多くの技術専門家が、これらのアイデアとメカニズムをマイクロコントローラー プログラミング、特に STM32 での Linux カーネルの自動初期化プロセスのシミュレーションに適用してきました。
一般的に、プログラムを作成するときは特定のルーチンに従います。シーケンシャルロジックに従って関数を次々と実行していきます。
ロジックが非常に複雑で、多くのモジュールが含まれる場合、順次実行されるコードが肥大化し、モジュールが非常に緊密に結合されます。 Linux カーネルにはさまざまな周辺ドライバーがあり、それらを論理的に順番に実行することはほとんど不可能です。
Kenrel コードには非常に大量のコードを含めることができ、サイズは大きくても乱雑ではなく、各レベルと各モジュールを効果的に分離し、大量のコードが論理的にまとめられており、この initcall が重要な役割を果たします。
この方法を真似して、最終的に図のメイン関数のコードをクリアし、このロジックを分離して同じ機能を実現します。
このような関数を実装するには、いくつかの背景知識が必要です:
1、プログラムコードの構成
###2、リンクスクリプトに関する知識。###3、関数ポインタの適用。
画像などのコードの構成は、変数 a、b、関数ポインタ f、f2 がプログラムのどのセクションに格納されているかを知る必要があります。この stm32 の起動を見てください。コード実装 | C 言語、上記の a 、 f は bss セグメントに格納され、 b 、 f2 はデータ セグメントに格納されます。これは、初期値が与えられており、この intcall を実装すると、自動的に初期化する必要があるデータが配置されるためです。 .initcall などのカスタム セグメントにコピーします。特定のセクションに配置するには、attribute((section)) キーワードを使用してデータ格納セクションを変更する必要があります。
現在のプログラムはこれらのセグメントを使用してコンパイルされています。同じく追加される .isr_vector を除き、その他はコンパイラのデフォルトのセグメントです。
最初にコードを追加します: もちろん、これだけでは十分ではありません。リンカー (LD) に .initcall セクションをプログラムにリンクするように指示する必要があるため、このセクションも変更する必要があります。 このセクションは 8 バイトで整列され、2 つのグローバル変数を定義し、これらのデータを 0 ~ 5 の順序でリンクします。これら 2 つの変更後、プログラムの各セクションを見てみましょう。写真に示すように:
.initcalls セクションには余分な赤枠があり、このセクションは 0x80005a8 から始まる合計 8 バイトです。readelf ツールを使用して、このセクションの具体的な状況を見てみましょう。
は上記のサイズ ツールと一致し、緑色のボックスのアドレスは SystemInit (0x08000231、リトル エンディアン モード) です。 したがって、属性を使用してリンク スクリプトを変更すると、関数ポインタ変数が .initcall セクションに配置されます。ということで、この関数の呼び出し方は先ほどの初期化データのセグメントデータと同様で、このセグメントを走査して関数のアドレスを取り出し、セグメント内のアドレスを強制的に関数ポインタに変換して直接呼び出しています。 。
実装された図は、.initcall セクションから関数のアドレスを取得し、それを直接呼び出すことですが、関数のアドレスと関数ポインター変数のアドレスを混同するのは非常に簡単です。このようにコードを変更すると、確かに自動初期化機能を調整できますが、そのたびに static initcall_t __attribute__(( __ used__,__ section__(”.initcall.0.init) の長いセクションを記述する必要があります。 ”))) は不快なだけですが、Linux カーネルのマクロを使用して変更できます。
これも同様です。
プログラムの論理順序に従って実行されるいくつかのマクロを追加します0, low_level_init たとえば、システム基本クロックを初期化します。
1、arch_init たとえば、NVIC の初期化など、CPU アーキテクチャの初期化を行います。2、dev_init は、i2c、フラッシュ、spi などの周辺モジュールを初期化します。
3、board_init は特定のハードウェア ボードに対していくつかの設定を行います。
4、os_init ファイル システム、ネットワーク プロトコル スタックなどのオペレーティング システムの一部の設定。
##5、app_init は最終的にユーザープログラムを実行します。
独自のプログラムを変更し、代わりにマクロを使用します。このように、do_initcallsの呼び出しは0、1、5の順に実行されます。
最後に、initcall セクションを見てみましょう:
このように、自動初期化関数に dev_init()、app_init() などを追加するだけで、main 関数を必要とせずに自動的に呼び出されます。実行のシーケンス。
例: i2c コントロールの初期化は dev_init に置かれます。下には i2c スレーブデバイスがたくさんぶら下がっています。app_init でそれぞれのスレーブデバイスを初期化するだけです。新しいものが来ても、この app_init を使って初期化するだけです必要ありません。元の高度に分離されたモジュール間の結合度を変更します。
これは Linux kenerl の初期化をシミュレートし、検証が成功し、最終的にライブラリにアップロードされます。
以上がSTM32 で Linux の自動初期化プロセスをシミュレートするの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。