222

Linuxプログラマーズマニュアルによると:

brk() および sbrk() は、プロセスのデータ セグメントの終了を定義するプログラム ブレークの位置を変更します。

ここのデータ セグメントとはどういう意味ですか? データ セグメントだけですか、それともデータ、BSS、およびヒープを組み合わせたものですか?

ウィキデータセグメントによると:

データ、BSS、およびヒープ領域をまとめて「データ セグメント」と呼ぶことがあります。

データ セグメントのサイズだけを変更する理由はありません。それがデータ、BSS、およびヒープをまとめたものである場合、ヒープがより多くのスペースを取得するので理にかなっています。

それが私の2番目の質問につながります。これまで読んだすべての記事で、著者はヒープが上向きに成長し、スタックが下向きに成長すると述べています。しかし、彼らが説明していないのは、ヒープがヒープとスタックの間のすべてのスペースを占有するとどうなるかということです?

ここに画像の説明を入力

4

8 に答える 8

278

あなたが投稿した図では、「ブレーク」( and によって操作されるアドレスbrksbrkは、ヒープの上部にある点線です。

仮想メモリ レイアウトの簡略図

あなたが読んだドキュメントでは、これを「データセグメント」の終わりとして説明しています。これは、従来の (事前共有ライブラリ、事前mmap) Unix ではデータセグメントがヒープと連続していたためです。プログラムの開始前に、カーネルは「テキスト」および「データ」ブロックをアドレス 0 から RAM にロードし (実際にはアドレス 0 の少し上にあるため、NULL ポインターは実際には何も指していません)、ブレーク アドレスを次のように設定します。データセグメントの終わり。図に示すように、の最初の呼び出しmallocは、分割を移動し、データ セグメントの先頭と新しいより高いブレーク アドレスのsbrkにヒープを作成するために使用し、その後の の使用は、それを使用してヒープを大きくします。必要に応じて。malloc

その間、スタックはメモリの一番上から始まり、下に向かって成長します。スタックを大きくするために明示的なシステム コールは必要ありません。可能な限り多くのRAMが割り当てられた状態で開始するか(これは従来のアプローチでした)、スタックの下に予約済みアドレスの領域があり、そこに書き込みの試みに気付いたときにカーネルが自動的にRAMを割り当てます。 (これは現代のアプローチです)。いずれにせよ、スタックに使用できるアドレス空間の下部に「ガード」領域がある場合とない場合があります。この領域が存在する場合 (最新のシステムはすべてこれを行います)、永久にマップ解除されます。いずれかの場合スタックまたはヒープがそれに成長しようとすると、セグメンテーション違反が発生します。ただし、伝統的に、カーネルは境界を強制しようとはしませんでした。スタックがヒープに成長したり、ヒープがスタックに成長したりする可能性があり、どちらの方法でも互いのデータを走り書きし、プログラムがクラッシュします。運が良ければ、すぐにクラッシュします。

この図の 512GB という数字がどこから来たのかわかりません。これは 64 ビットの仮想アドレス空間を意味し、そこにある非常に単純なメモリ マップとは矛盾します。実際の 64 ビット アドレス空間は、次のようになります。

簡略化されていないアドレス空間

              Legend:  t: text, d: data, b: BSS

This is not remotely to scale, and it shouldn't be interpreted as exactly how any given OS does stuff (after I drew it I discovered that Linux actually puts the executable much closer to address zero than I thought it did, and the shared libraries at surprisingly high addresses). The black regions of this diagram are unmapped -- any access causes an immediate segfault -- and they are gigantic relative to the gray areas. The light-gray regions are the program and its shared libraries (there can be dozens of shared libraries); each has an independent text and data segment (and "bss" segment, which also contains global data but is initialized to all-bits-zero rather than taking up space in the executable or library on disk). The heap is no longer necessarily continous with the executable's data segment -- I drew it that way, but it looks like Linux, at least, doesn't do that. The stack is no longer pegged to the top of the virtual address space, and the distance between the heap and the stack is so enormous that you don't have to worry about crossing it.

ブレークはまだヒープの上限です。しかし、私が示していなかったのは、.mmapの代わりにbrk. brk(OS は、衝突しないように、これらを領域から遠ざけようとします。)

于 2011-08-09T01:19:37.913 に答える
10

brkandを使用しsbrkて、誰もが常に不平を言っている「malloc オーバーヘッド」を回避できます。ただし、このメソッドを組み合わせて簡単に使用することはできないmallocため、何もする必要がない場合にのみ適切ですfree. できないからです。mallocまた、内部で使用する可能性のあるライブラリ呼び出しは避ける必要があります。すなわち。strlenおそらく安全ですが、fopenおそらくそうではありません。

を呼び出すsbrkのと同じように呼び出しますmalloc。現在のブレークへのポインターを返し、その量だけブレークをインクリメントします。

void *myallocate(int n){
    return sbrk(n);
}

個々の割り当てを解放することはできませんが ( malloc-overheadがないため、覚えておいてください)、への最初の呼び出しによって返された値を使用して呼び出し、brkを巻き戻すことで、領域全体を解放できます。brksbrk

void *memorypool;
void initmemorypool(void){
    memorypool = sbrk(0);
}
void resetmemorypool(void){
    brk(memorypool);
}

これらのリージョンを積み重ねて、ブレークをリージョンの先頭に巻き戻すことで、最新のリージョンを破棄することもできます。


もう一つ ...

sbrkはよりも 2 文字短いため、コード ゴルフにも役立ちますmalloc

于 2011-08-09T06:10:49.570 に答える
4

特別に指定された匿名のプライベート メモリ マッピングがあります (従来は data/bss のすぐ後ろに配置されていましたが、最新の Linux では実際には ASLR で場所を調整します)。原則として、 で作成できる他のどのマッピングよりも優れているわけではありませんがmmap、Linux にはいくつかの最適化があり、( brksyscall を使用して) このマッピングの最後を上方に拡張し、発生するロック コストを削減することがmmapできmremapます。mallocこれにより、実装がメイン ヒープを実装するときに使用することが魅力的になります。

于 2011-08-08T22:32:16.330 に答える
1

mallocは、brkシステムコールを使用してメモリを割り当てます。

含む

int main(void){

char *a = malloc(10); 
return 0;
}

この単純なプログラムをstraceで実行すると、brkシステムが呼び出されます。

于 2013-03-12T16:44:12.937 に答える
0

2 番目の質問にお答えできます。Malloc は失敗し、null ポインターを返します。そのため、メモリを動的に割り当てるときは常に null ポインターをチェックします。

于 2011-08-08T20:59:57.910 に答える
0

ヒープは、プログラムのデータ セグメントの最後に配置されます。brk()ヒープのサイズを変更 (拡張) するために使用されます。ヒープがそれ以上大きくならない場合、malloc呼び出しは失敗します。

于 2011-08-08T21:00:19.227 に答える
0

データ セグメントは、すべての静的データを保持するメモリの一部であり、起動時に実行可能ファイルから読み込まれ、通常はゼロで埋められます。

于 2011-08-08T21:02:13.993 に答える