9

The question is NOT about the Linux kernel. It is NOT a C vs. C++ debate either.

I did a research and it seems to me that C++ lacks tool support when it comes to exception handling and memory allocation for embedded systems:

Why is the linux kernel not implemented in C++? Beside the accepted answer see also Ben Collins' answer.

Linus Torvalds on C++:

"[...] anybody who designs his kernel modules for C++ is [...]
(b) a C++ bigot that can't see what he is writing is really just C anyway"

" - the whole C++ exception handling thing is fundamentally broken. It's especially broken for kernels.
- any compiler or language that likes to hide things like memory allocations behind your back just isn't a good choice for a kernel."

JOINT STRIKE FIGHTER AIR VEHICLE C++ CODING STANDARDS:

"AV Rule 208 C++ exceptions shall not be used"


  1. Are the exception handling and the memory allocation the only points where C++ apparently lacks tool support (in this context)?

  2. To fix the exception handling issue, one has to provide bound on the time till the exception is caught after it is thrown?

  3. Could you please explain me why the memory allocation is an issue? How can one overcome this issue, what has to be done?

As I see this, in both cases one has to provide an upper bound at compile time on something nontrivial that happens and depends on things at run-time.


Answer:

  1. No, dynamic casts were also an issue but it has been solved.

  2. Basically yes. The time needed to handle exceptions has to be bounded by analyzing all the throw paths.

  3. See solution on slides "How to live without new" in Embedded systems programming. In short: pre-allocate (global objects, stacks, pools).

4

3 に答える 3

14

さて、いくつかのことがあります。まず、STLはOSルーチン、C標準ライブラリ、および動的割り当てに完全に基づいて構築されていることを覚えておく必要があります。カーネルを作成するとき、動的メモリ割り当てはありません(提供します)。C標準ライブラリはありません(カーネル上に構築されたライブラリを提供する必要があります)。また、システムコールを提供します。次に、Cはアセンブリと非常にうまく相互運用しやすいのに対し、C ++は、ABIが必ずしも一定ではなく、名前も一定ではないため、アセンブリとのインターフェイスが非常に難しいという事実があります。名前のマングリングにより、まったく新しいレベルの複雑さが発生します。

次に、OSを構築するときは、カーネルが使用するメモリのあらゆる側面を把握して制御する必要があることを覚えておく必要があります。C ++には、作業を大幅に妨げる可能性のある、制御できない隠れた構造(vtables、RTTI、例外)がかなりあります。

言い換えれば、Linusが言っているのは、Cを使用すると、生成されているアセンブリを簡単に理解でき、マシン上で直接実行するのに十分簡単であるということです。C ++でも可能ですが、常にかなりのコンテキストを設定し、アセンブリとCをインターフェイスするためにCを実行する必要があります。別の理由は、システムプログラミングでは、メソッドがどのように呼び出されているかを正確に知る必要があることです。Cには非常によく文書化されたC呼び出し規約がありますが、C ++ではthis、名前のマングリングなどに対処する必要があります。

要するに、それはC++があなたに尋ねることなく物事を行うからです。

以下の@Joshのコメントによると、C ++が背後で行うもう1つのことは、コンストラクタとデストラクタです。スタックフレームに出入りするためのオーバーヘッドが追加され、何よりも、C ++スタックフレームを破棄するときに、その中のすべてのオブジェクトのデストラクタを呼び出す必要があるため、アセンブリの相互運用がさらに困難になります。これはすぐに醜くなります。

于 2012-08-18T17:06:47.017 に答える
9

特定のカーネルがコードベースでC++コードを拒否するのはなぜですか?政治と好み、しかし私は逸脱します。

最新のOSカーネルの一部は、C++の特定のサブセットで記述されています。これらのサブセットでは、主に例外とRTTIが無効になっています(多重継承とテンプレートも許可されていない場合があります)。

これはCにも当てはまります。特定の機能はカーネル環境(VLAなど)では使用しないでください。

例外とRTTIを除いて、カーネルコード(または組み込みコード)について話しているとき、C++の特定の機能は非常に批判されています。これらはvtablesとコンストラクタ/デストラクタです。それらは内部に少しのコードをもたらします、そしてそれは「悪い」とみなされるようです。コンストラクターが必要ない場合は、コンストラクターを実装しないでください。コンストラクターでクラスを使用することを心配している場合は、構造体を初期化するために使用しなければならない関数についても心配してください。C ++の利点は、メモリの割り当てを解除することを忘れる以外に、dtorを使用することを本当に忘れることができないことです。

しかし、vtablesはどうですか?

拡張ポイントを含むオブジェクト(Linuxファイルシステムドライバーなど)を実装する場合、仮想メソッドを使用してクラスのようなものを実装します。では、vtableを使用するのはなぜとても悪いのでしょうか?vtableが存在するページに特定の要件がある場合は、このvtableの配置を制御する必要があります。私が覚えている限り、これはLinuxには関係ありませんが、Windowsでは、コードページがページアウトされる可能性があり、高すぎるirqlからページアウト関数を呼び出すとクラッシュします。ただし、高いirqlを使用している場合は、どの関数を呼び出すかに注意する必要があります。また、このコンテキストで仮想呼び出しを使用しない場合は、心配する必要はありません。組み込みソフトウェアでは、これはさらに悪化する可能性があります。これは、(めったに)コードがどのコードページに入るのかを直接制御する必要があるためです。

では、なぜこれほど多くの人が「カーネルでCを使用する」ことに固執しているのでしょうか。

ツールチェーンの問題でやけどを負ったか、カーネルモードで最新のものを使用している熱狂的な開発者によってやけどを負ったためです。

たぶん、カーネルモードの開発者はかなり保守的であり、C++はあまりにも新しいものです...


カーネルモードコードで例外が使用されないのはなぜですか?

関数ごとにいくつかのコードを生成する必要があるため、コードパスに複雑さを導入し、例外を処理しないことは、システムを強制終了するため、カーネルモードコンポーネントにとっては悪いことです。

C ++では、例外がスローされた場合、スタックをアンワインドし、それに応じたデストラクタを呼び出す必要があります。これには、少なくとも少しのオーバーヘッドが含まれます。これはほとんど無視できますが、コストがかかり、あなたが望むものではないかもしれません。(テーブルベースのアンワインドが実際にいくらかかるかはわかりません。例外が実行されていない場合はコストがかからないと読んだと思いますが、...調べなければならないと思います)。

例外をスローできないコードパスは、それよりもはるかに簡単に推論できます。それで :

int f( int a )
{
   if( a == 0 )
      return -1;

   if( g() < 0 )
      return -2;
   f3(); 

   return h();
}

この関数では、すべての戻り値を簡単に確認できるため、すべての出口パスについて推論できますが、例外が有効になっている場合、関数がスローされる可能性があり、関数が取る実際のパスが何であるかを保証できません。これは、コードの正確なポイントであり、一度に見ることができないことを行う可能性があります。(例外が有効になっている場合、これは不正なC ++コードです)。

3番目のポイントは、ユーザーモードアプリケーションをクラッシュさせたいということです。予期しないことが発生した場合(メモリが不足した場合など)、ユーザーモードアプリケーションは(リソースを解放した後)クラッシュして、開発者が問題をデバッグできるようにするか、少なくとも問題を解決できるようにする必要があります。エラーメッセージ。カーネルモードモジュールでキャッチされない例外が発生することはありません。

これはすべて克服できることに注意してください。WindowsカーネルにはSEH例外があるため、ポイント2+3はNTカーネルでは実際には良いポイントではありません。


カーネルのC++にはメモリ管理の問題はありません。たとえば、NTカーネルヘッダーは、newおよびdeleteのオーバーロードを提供します。これにより、割り当てのプールタイプを指定できますが、それ以外は、ユーザーモードアプリケーションのnewおよびdeleteとまったく同じです。

于 2012-08-18T20:14:00.777 に答える
3

私は言語戦争があまり好きではなく、これを再び終わらせることに投票しました。とにかく...

さて、いくつかのことがあります。まず、STLはOSルーチン、C標準ライブラリ、および動的割り当てに完全に基づいて構築されていることを覚えておく必要があります。カーネルを作成するとき、動的メモリ割り当てはありません(提供します)。C標準ライブラリはありません(カーネル上に構築されたライブラリを提供する必要があります)。また、システムコールを提供します。次に、Cはアセンブリと非常にうまく相互運用しやすいのに対し、C ++は、ABIが必ずしも一定ではなく、名前も一定ではないため、アセンブリとのインターフェイスが非常に難しいという事実があります。名前のマングリングにより、まったく新しいレベルの複雑さが発生します。

extern "C"いいえ、C ++では、 (またはオプションextern "assembly"で)呼び出し規約を持つ関数を宣言できます。これにより、名前は同じプラットフォーム上の他のすべてと互換性があります。

次に、OSを構築するときは、カーネルが使用するメモリのあらゆる側面を把握して制御する必要があることを覚えておく必要があります。C ++には、作業を大幅に妨げる可能性のある、制御できない隠れた構造(vtables、RTTI、例外)がかなりあります。

カーネル機能をコーディングするときは注意が必要ですが、それはC++に限定されません。もちろん、std::vector<byte>メモリ割り当てのベースとして使用することはできませんが、そのために使用することもできませんmalloc。すべてのC++クラスに対して、仮想関数、多重継承、および動的割り当てを使用する必要はありませんか

言い換えれば、Linusが言っているのは、Cを使用すると、生成されているアセンブリを簡単に理解でき、マシン上で直接実行するのに十分簡単であるということです。C ++でも可能ですが、常にかなりのコンテキストを設定し、アセンブリとCをインターフェイスするためにCを実行する必要があります。別の理由は、システムプログラミングでは、メソッドがどのように呼び出されているかを正確に知る必要があることです。Cには非常によく文書化されたC呼び出し規約がありますが、C ++では、これを処理したり、名前マングリングなどを行う必要があります。

Linusは、へのすべての呼び出しを見つけて、それが、、、および20レベルの深さf(x)で呼び出していることをすぐに確認できると主張している可能性があります。それは彼の後ろにいくつかの未知のコードを呼び出しているかもしれないので、それでも大きな謎です。そこで私を失った。g(x)h(x)q(x)MyClass M(x);

要するに、それはC++があなたに尋ねることなく物事を行うからです。

どのように?クラスのコンストラクタとデストラクタを作成する場合、それはコードの実行を要求しているためです。Cがコードを実行せずにオブジェクトを魔法のようにコピーできると言わないでください!

以下の@Joshのコメントによると、C ++が背後で行うもう1つのことは、コンストラクタとデストラクタです。スタックフレームに出入りするためのオーバーヘッドが追加され、何よりも、C ++スタックフレームを破棄するときに、その中のすべてのオブジェクトのデストラクタを呼び出す必要があるため、アセンブリの相互運用がさらに困難になります。これはすぐに醜くなります。

コンストラクタとデストラクタは、背後にコードを追加するのではなく、必要な場合にのみコードを追加します。デストラクタは、動的メモリの割り当てを解除する必要がある場合など、必要な場合にのみ呼び出されます。これがなくてもCコードが機能するとは言わないでください。


LinuxとWindowsの両方でC++がサポートされていない理由の1つは、C ++が利用可能になるずっと前から、カーネルに取り組んでいる多くの人がこれを行っていることです。C ++で記述されたデバイスドライバーはほとんどないため、C++サポートは実際には必要ないと主張するWindowsカーネル開発者からの投稿を見てきました。キャッチ22!


例外処理とメモリ割り当ては、C ++が明らかにツールサポートを欠いている唯一のポイントですか(このコンテキストでは)?

これが適切に処理されない場所では、使用しないでください。どこでも、多重継承、動的割り当て、および例外のスロー使用する必要はありません。エラーコードを返すことが機能する場合は、問題ありません。それを行う!

例外処理の問題を修正するには、例外がスローされてからキャッチされるまでの時間に制限を設ける必要がありますか?

いいえ。ただし、カーネルでアプリケーションレベルの機能を使用することはできません。を使用して動的メモリを実装することstd::vector<byte>は良い考えではありませんが、誰が実際にそれを試すでしょうか?

メモリ割り当てが問題になる理由を教えてください。どうすればこの問題を克服できますか、何をしなければなりませんか?

メモリ管理を実装する関数の下の層でのメモリ割り当てに依存する標準ライブラリ機能を使用することは問題になります。malloc呼び出しを使用して 実装mallocするのも同様にばかげています。しかし、誰がそれを試みるでしょうか?

于 2012-08-18T19:57:10.243 に答える