17

プロセスのメモリ割り当てを調べると、通常、次のように概説されます。

ここに画像の説明を入力

ここまでは順調ですね。

しかし、プログラムがデータセクションの上限を変更できるようにするsbrk()システムコールがあり、sbrk(0)でその上限がどこにあるかを簡単に確認するためにも使用できます。その機能を使用して、次のパターンを見つけました。

パターン 1 - 小さな malloc

Linux マシンで次のプログラムを実行します。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int globalVar;

int main(){
        int localVar;
        int *ptr;

        printf("localVar address (i.e., stack) = %p\n",&localVar);
        printf("globalVar address (i.e., data section) = %p\n",&globalVar);
        printf("Limit of data section = %p\n",sbrk(0));

        ptr = malloc(sizeof(int)*1000);

        printf("ptr address (should be on stack)= %p\n",&ptr);
        printf("ptr points to: %p\n",ptr);
        printf("Limit of data section after malloc= %p\n",sbrk(0));

        return 0;
}

出力は次のとおりです。

localVar address (i.e., stack) = 0xbfe34058
globalVar address (i.e., data section) = 0x804a024
Limit of data section = 0x91d9000
ptr address (should be on stack)= 0xbfe3405c
ptr points to: 0x91d9008
Limit of data section after malloc= 0x91fa000

ご覧のとおり、割り当てられたメモリ領域は古いデータ セクションの制限のすぐ上にあり、malloc の後でその制限が押し上げられたので、割り当てられた領域は実際には新しいデータ セクション内にあります。

質問 1 : これは、小さな malloc がデータ セクションにメモリを割り当て、ヒープをまったく使用しないということですか?

パターン 2 - ビッグ マロック

15 行目で要求されたメモリ サイズを増やすと、次のようになります。

ptr = malloc(sizeof(int)*100000);

次の出力が得られます。

localVar address (i.e., stack) = 0xbf93ba68
globalVar address (i.e., data section) = 0x804a024
Limit of data section = 0x8b16000
ptr address (should be on stack)= 0xbf93ba6c
ptr points to: 0xb750b008
Limit of data section after malloc= 0x8b16000

ここでわかるように、データ セクションの制限は変更されておらず、割り当てられたメモリ領域は、データ セクションとスタックの間のギャップ セクションの中央にあります。

質問 2 : これは実際にヒープを使用している大きな malloc ですか?

質問 3 : この動作について何か説明はありますか? 最初の例(小さなmalloc)では、割り当てられたメモリを解放した後でも、データ内にあるため、セグフォルトを発生させることなくポインタを使用してそのメモリを使用することができます。これにより、バグの検出が困難になる可能性があります。

仕様の更新: Ubuntu 12.04、32 ビット、gcc バージョン 4.6.3、Linux カーネル 3.2.0-54-generic-pae。

更新 2: 以下のロドリゴの回答がこの謎を解決しました。このウィキペディアのリンクも役に立ちました。

4

1 に答える 1

13

まず第一に、何が起こるかを完全に確認する唯一の方法は、 のソース コードを読むことですmalloc。または、さらに良いことに、デバッガーを使用してステップスルーします。

とにかく、これらのことについての私の理解は次のとおりです。

  1. システム コールsbrk()を使用して、データ セクションのサイズを増やします。通常、直接呼び出すことはありませんが、ヒープに使用できるメモリを増やすためにの実装によって呼び出されます。malloc()
  2. この関数malloc()は、OS からメモリを割り当てません。データセクションを分割し、これらの部分を必要な人に割り当てるだけです。を使用free()して、1 つのピースを未使用で再割り当て可能としてマークします。
  3. ポイント 2 は単純化しすぎです。少なくとも、大きなブロックの GCC 実装では、プライベートでファイルに依存しないオプションmalloc()を使用してそれらを割り当てます。mmap()したがって、これらのブロックはデータ セグメントの外側にあります。明らかに、free()そのようなブロックを呼び出すと、 が呼び出されますmunmap()

正確に大きなブロックとは何かは、多くの詳細に依存します。詳細man malloptについては、を参照してください。

それから、解放されたメモリにアクセスするとどうなるかを推測できます。

  1. ブロックが小さかった場合、メモリはまだそこにあるため、読み取っても何も起こりません。書き込みを行うと、内部ヒープ構造が破損するか、再利用されて任意のランダム構造が破損する可能性があります。
  2. ブロックが大きい場合、メモリはマップされていないため、アクセスするとセグメンテーション違反が発生します。その間に別の大きなブロックが割り当てられる (または別のスレッドが呼び出さmmap()れ、同じアドレス範囲がたまたま使用される) というありそうもない状況を除きます。

明確化

データ セクションという用語は、コンテキストに応じて 2 つの異なる意味で使用されます。

  1. 実行可能ファイルの.dataセクション (リンカーの視点)。.bssまたはを含む場合もあり.rdataます。何も意味しないOSの場合、フラグ以外の内容(読み取り専用、実行可能...)をほとんど考慮せずに、プログラムの一部をメモリにマップするだけです。
  2. ヒープ、すべてのプロセスが持つメモリのブロックで、実行可能ファイルから読み取られず、 を使用して拡大できますsbrk()

単純なプログラムのメモリ レイアウトを出力する次のコマンドで確認できます ( cat)。

$ cat /proc/self/maps
08048000-08053000 r-xp 00000000 00:0f 1821106    /usr/bin/cat
08053000-08054000 r--p 0000a000 00:0f 1821106    /usr/bin/cat
08054000-08055000 rw-p 0000b000 00:0f 1821106    /usr/bin/cat
09152000-09173000 rw-p 00000000 00:00 0          [heap]
b73df000-b75a5000 r--p 00000000 00:0f 2241249    /usr/lib/locale/locale-archive
b75a5000-b75a6000 rw-p 00000000 00:00 0 
b75a6000-b774f000 r-xp 00000000 00:0f 2240939    /usr/lib/libc-2.18.so
b774f000-b7750000 ---p 001a9000 00:0f 2240939    /usr/lib/libc-2.18.so
b7750000-b7752000 r--p 001a9000 00:0f 2240939    /usr/lib/libc-2.18.so
b7752000-b7753000 rw-p 001ab000 00:0f 2240939    /usr/lib/libc-2.18.so
b7753000-b7756000 rw-p 00000000 00:00 0 
b7781000-b7782000 rw-p 00000000 00:00 0 
b7782000-b7783000 r-xp 00000000 00:00 0          [vdso]
b7783000-b77a3000 r-xp 00000000 00:0f 2240927    /usr/lib/ld-2.18.so
b77a3000-b77a4000 r--p 0001f000 00:0f 2240927    /usr/lib/ld-2.18.so
b77a4000-b77a5000 rw-p 00020000 00:0f 2240927    /usr/lib/ld-2.18.so
bfba0000-bfbc1000 rw-p 00000000 00:00 0          [stack]

最初の行は実行コード (.textセクション) です。

2 行目は読み取り専用データ (.rdataセクション) とその他の読み取り専用セクションです。

3 行目は.data+.bssとその他の書き込み可能なセクションです。

4行目はヒープです!

次の名前の行は、メモリ マップされたファイルまたは共有オブジェクトです。名前のないものは、おそらく大規模な malloc されたメモリ ブロックです (または、プライベートな匿名の mmap であり、区別することはできません)。

最後の行はスタックです!

于 2013-10-10T20:08:10.573 に答える