2016-02-01 10 views
11

C++のグリーンスレッドでは、ほとんどがboost::coroutine2と同様のPOSIX関数makecontext()/swapcontext()のようなリサーチを行い、boost::coroutine2の上にC++グリーンスレッドライブラリを実装する予定です。両方とも、ユーザーコードが新しい関数/コルーチンごとにスタックを割り当てる必要があります。C++グリーンスレッドのスタック割り当て

ターゲットプラットフォームはx64/Linuxです。私は私の緑色のスレッドライブラリを一般的な使用に適しているようにしたいので、必要に応じてスタックを展開しなければなりません(妥当な上限は、例えば10MBです)、あまりにも多くのメモリが使用されていないとき)。私はスタックを割り当てるための適切なアルゴリズムを理解していません。いくつかのグーグル後

、私はいくつかのオプションを自分で考え出し:

  1. 使用スプリット・スタックは、コンパイラ(gccの-fsplitスタック)で実装されますが、分割スタックは、パフォーマンスのオーバーヘッドがあります。パフォーマンス上の理由から、Goはすでにスプリットスタックから離れています。
  2. mmap()で大きなメモリチャンクを割り当てます。カーネルは、物理メモリを未割り当てのままにしておき、スタックにアクセスするときにだけ割り当てることができます。この場合、私たちはカーネルの慈悲に満ちています。
  3. は、mmap(PROT_NONE)で大きなメモリ領域を予約し、SIGSEGVシグナルハンドラを設定します。シグナルハンドラでは、SIGSEGVがスタックアクセスによって発生した場合(アクセスされたメモリが予約されている大きなメモリ空間内にある場合)、必要なメモリをmmap(PROT_READ | PROT_WRITE)で割り当てます。このアプローチの問題は次のとおりです。mmap()は非同期の安全ではなく、シグナルハンドラ内で呼び出すことはできません。これはまだ実装することができます非常にトリッキー:メモリ割り当てのためのプログラム起動中に別のスレッドを作成し、pipe() + read()/write()を使用して、信号ハンドラからスレッドにメモリ割り当て情報を送信します。オプション3について

さらにいくつかの質問:

  1. が、私はこのアプローチのパフォーマンスオーバーヘッドわからないんだけど、カーネル/ CPUが実行する方法も/悪いメモリ空間は、のために非常に断片化されたとき何千もの電話番号mmap()
  2. 割り当てられていないメモリがカーネル空間でアクセスされる場合、このアプローチは正しいですか?例えばread()と呼ばれると?

グリーンスレッドのスタック割り当てには、他にも(より良い)オプションがありますか?グリーンスレッドスタックは他の実装でどのように割り当てられますか? Go/Java?

+1

'mmap'はPOSIXに従った安全で非同期されていないが、それは実際にそこのLinuxでの安全とほとんどすべての合理的な、使用可能なUNIXバリアントを非同期れます。 –

+0

@ChrisDoddなぜ 'mmap'が緑のスレッドに適しているのか聞いてみることはできますか?私は専門家ではないが、私は知りたかった。 – VermillionAzure

+0

@ChrisDoddこれについてのmanページ/リンクが見つかりませんでした。私にリンクを教えてください。 – user416983

答えて

0

なぜmmapですか?新しい(またはmalloc)で割り当てると、メモリは変更されず、間違いなくマップされます。

const int STACK_SIZE = 10 * 1024*1024; 
char*p = new char[STACK_SIZE*numThreads]; 

pには、必要なスレッド用の十分なメモリがあります。あなたがメモリを必要とする場合には、P + STACK_SIZEへのアクセスを開始*私

+2

マッピングされていないか、特にどのような値にも初期化されていることは間違いありません。 GNU libc mallocを使用すると、割り当ての大部分は最終的にはとにかくmmap()を呼び出します。 – Matt

1

glibcのは、通常のCプログラム用のスタックを割り当てる方法は、まさにこの目的のために設計され、次のmmapフラグと地域をのmmapすることです:

MAP_GROWSDOWN 
      Used for stacks. Indicates to the kernel virtual memory system 
      that the mapping should extend downward in memory. 

互換性のために、おそらくMAP_STACKを使用してください。その後、SIGSEGVハンドラを自分で書く必要はなく、スタックは自動的に成長します。あなたは、彼らがsigaltstack(2)を呼び出したい場合は、通常の人はシグナルハンドラのために何をすべきかです有界スタックサイズを、必要な場合普通のmmapコールを発行し、ここWhat does "ulimit -s unlimited" do?

説明するように境界を設定することができます。

のLinuxカーネルは常に、ページが最初に(おそらくないリアルタイムカーネルではなく、確かに他のすべての構成で)アクセスされたときにページフォルトをキャッチし、仮想ページをバックアップ物理ページをマップします。興味があれば/proc/<pid>/pagemapインターフェイス(または私がhttps://github.com/dwks/pagemapと書いたこのツール)を使って確認することができます。

関連する問題