3

glibc 2.15 では、malloc.c、特に free() 関数を調べていて、unlink() マクロについて混乱しました。ソースによると、使用中のチャンクは次のようになります。

   chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  Size of previous chunk, if allocated            
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  Size of chunk, in bytes                       
     mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  User data starts here...                          .
    .                                                               .
    .             (malloc_usable_size() bytes)                      .
    .                                                               
nextchunk->+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

free() されたチャンクは次のようになります。

    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                         Size of previous chunk                    
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 `head:'           Size of chunk, in bytes                          
  mem->     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  Forward pointer to next chunk in list             
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  Back pointer to previous chunk in list            
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  Unused space (may be 0 bytes long)                .
    .                                                               .
    .                                                               
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

使用されたチャンクが free() されると、受け取ったメモリ ポインタを引数として取り、そこからオフセットを差し引いてチャンク ポインタを取得します。その間に一連のチェックがありますが、チャンクが mmap されていない場合は、通常、別の空きチャンクと前方または後方に統合されます。free() されたチャンクはすでにビンにあるため、その特定のビン内でそれを統合するチャンクを検索するだけですよね?前方統合の場合、unlink()マクロが呼び出され、free() されたチャンクに続くチャンクに適用されます。次のチャンク (「nextchunk」と呼びます) がリンク解除されると、次のコードが発生するため、これがわかりません。

    #define unlink(P, BK, FD) {                                            
    FD = P->fd;                                                          
    BK = P->bk;
    .
    .
    .
    FD->bk = BK;                                                       
    BK->fd = FD;
    .
    .
    .
                             }

BK->fdBK が free() されたチャンクを指し、その構造を見ると前方または後方ポインターがないことを考慮して、どのように参照できますか。fd および bk フィールドが free() されたチャンクに追加されるコードの部分を見逃したに違いありませんが、どこにあるのかわかりません。誰でも助けることができますか?ありがとう。

4

1 に答える 1

1

この行は、解放されるチャンクにフォワードポインタを作成します。

BK->fd = FD;

BKは、以前はユーザーデータのチャンクでしたが、現在は空きデータのチャンクであるためmalloc、適切と思われる場合はメモリ上で走り書きすることができます。

それが役立つなら、あなたはそれを組合のように考えることができます:

union {
    struct {
        chunk *fd;
        chunk *bk;
    } freed;
    unsigned char user_data[N];
};

ユニオンでは、どのユニオンメンバーにも書き込むことができますが、最後に書き込んだメンバーからしか読み取ることができません。したがって、が呼び出されるとfree、データが書き込まれます。これは問題ありません。唯一の結果は、現在ガベージが発生している可能性があることです。比較すると、チャンクにユーザーデータ(空きではない)が含まれている場合、およびポインターはエイリアスであるため、ガベージになります。fdbkuser_datafdbkuser_data

user_data(技術的には、エイリアスであるため、どのエイリアスからでもいつでも読み取ることができますがunsigned char、実際には関係ありません。)

更新:これは低レベルのCコードです。malloc実装には低レベルのCコードが必要です。フィールドが存在する、または存在しないという考えは、低レベルのコードでは意味がありません。これは、さまざまなタイプとの間でキャストし、ポインターが相互にエイリアスできるようにするためです。

低レベルコードでは、フィールドは単なるメモリオフセットです。私のシステムでは、コンパイルするアーキテクチャに応じて、フィールドfdのオフセットが0になり、フィールドのオフセットが8または4になる場合があります。bkしたがって、次のコード:

BK->fd = FD;

これは、「値FDをメモリ位置BK+0に書き込む」ことを意味します。メモリ内の単なる場所と考えると、BK->fdどのように機能するかを理解するのに役立つ場合がありますfree。(コンパイル時に型情報とエイリアシングルールも存在するため、実際にはメモリ内の場所だけではありません。)

低レベルCを理解する: 低レベルCコードを理解したい場合は、アセンブリ言語を理解するのに非常に役立ちます。必須ではありませんが、役に立ちます。x86、MIPS、PowerPC、ARMなど、どのアセンブリ言語を習得するかは関係ありません。少しだけ、多くのアセンブリを習得する必要はありません。x86を学ぶ必要はありません。MIPSを使用したことがなくても、MIPSを学ぶことができます。(実際、MIPSはおそらく習得が容易です。)

小さなCコードをアセンブリに変換できる十分なアセンブリを学習するだけで、内部で何が行われているのかを理解できます。上記のCコードの1行は、非常に単純であるため、おそらくアセンブリコードの1行に変換されます。

また、Cを記述しているときは、アセンブリについてあまり考えないようにしてください。Cを記述しているときは、コンパイラのアセンブリを記述しています。つまり、アセンブリを記述していないということです。

于 2012-05-18T06:46:38.683 に答える