malloc(1)

第1章から第4章まではUNIXの基礎知識、Cの基礎知識やソースの概要なので、第5章から読み進めていくことにします。

まず、straceをしているときに見かけたことがある気がするmallocとmfreeです。メモリの取得や開放のための関数です。
いきなり疑問点がひとつ。p297に「coremapとswapmapのそれぞれは、2515行で宣言されているmap型の構造体の配列である」と書かれてあるのですが、どこにその宣言がされているのか分かりませんでした。


203 : int coremap[CMAPSIZ];
204 : int swapmap[SMAPSIZ];

2515: struct map
2516: {
2517: char *m_size;
2518: char *m_addr;
2519: }
intの配列が宣言されていることは分かるんですけど…とりあえず、どこかでmap構造体の配列であることが宣言されていることを前提として読み進めるとします。map構造体は、アドレスのサイズ(charのポインタ)とアドレスの始点(charのポインタ)を要素としているようです。要素にはアドレスの終点はないようです。サイズと始点があれば計算できるものなので、余計なデータでメモリを消費しないということでしょうか。
さて、malloc関数のソースは以下の通り。

2528: malloc(mp, size)
2529: struct map *mp;
2530: {
2531: register int a;
2532: resister struct map *bp;
2533:
2534: for (bp =mp; bp->m_size; bp++){
2535: if(bp->m_size >= size){
2536: a = bp->m_addr;
2537: bp->m_addr =+ size;
2538: if ((bp->m_size =- size) == 0)
2539: do {
2540: bp++;
2541: (bp-1)->m_addr = bp->m_addr;
2542: } while((bp-1)->m_size = bp->m_size);
2543: return(a);
2544: }
2545: }
2546 return(0);
2547 }
想像よりも遥かに短いです。

2528: malloc(mp, size)
2529: struct map *mp;
2530: {
...
2547: }
さっそく見慣れない書き方がでてきました。古い文法なのかなと調べてみたところ、第2章のp271に解説されていました。なんとなく想像できるように、関数の第1引数のmpがmap構造体へのポインタであることを宣言しているということのようです。宣言されていないsizeは「デフォルトとして整数であるとみなされる」ということらしいです。
2534行からはmallocの第2引数に渡されたsizeに十分なメモリをmap構造体の配列から取り除くという処理(リソースマップの管理)をしています。

2534: for (bp =mp; bp->m_size; bp++){
条件判定部の「 bp->m_size」は「 bp->m_size > 0」という意味らしいです。そして、ゼロサイズのエントリは最後の要素(見張りのエントリ)として存在しているようです。要するにエントリの最後まできたら、リソースマップに適当なエントリがないということで偽になって、

2546 return(0);
を返すようです。

2535: if(bp->m_size >= size){
2536: a = bp->m_addr;
2537: bp->m_addr =+ size;
2538: if ((bp->m_size =- size) == 0)
2539: do {
2540: bp++;
2541: (bp-1)->m_addr = bp->m_addr;
2542: } while((bp-1)->m_size = bp->m_size);
2543: return(a);
2544: }
2545: }
ここはそのまま読み進めるとつらいので、具体的な例を挙げながら読んでいくことにします。次のようなエントリを持ったリソースマップがあるとします。

エントリ サイズ アドレス
0 10 20
1 15 50
2 5 80

  • 例1 サイズが5のリクエストを受け取ったとき

エントリ0から検査していった場合、エントリ0のサイズ(10)は求めるサイズ(5)以上ですので、2535行目のif文は真になります。if文内ではaにエントリ0の始点のアドレス(20)を代入して(2536行目)、エントリ0の始点のアドレス(20)に求めるサイズ(5)を足します(2537行目)。さらにエントリのサイズ(10)から求めるサイズ(5)を引けば(2538行目)、以下のようになります。


エントリ サイズ アドレス
0 5 25
1 15 50
2 5 80
エントリ0のサイズが0になったときに2538行目のif文が真になってdo〜while文の処理を行いますが、ここでは偽なので2543行目で変更前のエントリ0の始点のアドレス(20)をreturnします。

  • 例2 サイズが10のリクエストを受け取ったとき

2538行目までは例1と同じです。


エントリ サイズ アドレス
0 0 30
1 15 50
2 5 80
ここでエントリ0のサイズが0になりますので、2538行目のif文が真になります。do〜while文ではリソースマップのエントリを繰り下げています。結果以下のようになります。

エントリ サイズ アドレス
0 15 50
1 5 80
ところで、2542行目のwhileの条件文は「==」で比較しているのではなく、「=」で代入しているだけなんですね。

2542: } while((bp-1)->m_size = bp->m_size);
どうやらbpがmap構造体へのポインタを示さなくなったときにこの条件文は偽になるようです。