ヒープ調査メモ
arduino でヒープを使いたくなったので調べてみた結果をメモっておきます.
ヒープを使うには
そもそも malloc 的な関数は使えるのかね? と思ってしらべたらありました.
http://www11.ocn.ne.jp/~akibow/avr-libc-user-manual-1.4.3/malloc.html を読むと以下のような構造になっていることがわかりました.
- ヒープの先頭は __malloc_heap_start から
- ヒープの末尾は __malloc_heap_end が 0 の場合, スタックトップアドレスに 32byte 足したところまで
- __malloc_heap_end で任意の末尾を設定することも可能. 詳細はhttp://www11.ocn.ne.jp/~akibow/avr-libc-user-manual-1.4.3/malloc.htmlに記載の通り.
- スタックトップアドレスはマクロ SP で参照できます. くわしくは arduino-0013\hardware\tools\avr\avr\include\avr\common.h とデータシートを見てください.
以下のスケッチを使って確認してみました.(arduino は arduino-0013 with atmega328 の状態です)
#include <stdlib.h>
void setup() {
Serial.begin(9600);
Serial.println((int)__malloc_heap_start, HEX);
Serial.println((int)__malloc_heap_end, HEX);
Serial.println((int)__malloc_margin, HEX);
Serial.println((int)SP, HEX);
}
void loop() {}
出力結果はこんなんなりました.
1A2
0
20
8F9
ヒープの構造は http://www11.ocn.ne.jp/~akibow/avr-libc-user-manual-1.4.3/malloc.html にある以下の図の通りになってるので,
このプログラムの場合, だいたい 1800byte 強*1ヒープとして使えることがわかりました.*2
メモリ割り当て状況をしらべるには
avr-objdump と avr-nm を使ってしらべてみます.
arduino IDE が以下にインストールされている状況とします.
c:\arduino-0013\hardware\tools\avr\bin
まずはパスを通します.
set PATH=%PATH%;c:\arduino-0013\hardware\tools\avr\bin
スケッチを保存してあるフォルダ*3へ移動してセクション情報を出力します.
cd c:\sketch_090218a/applet
avr-objdump -h sketch_090228a.elf > list.lst
出力されるファイルはこんな感じになりました.
sketch_090228a.elf: file format elf32-avr
Sections:
Idx Name Size VMA LMA File off Algn
0 .data 0000000c 00800100 00000730 000007a4 2**0
CONTENTS, ALLOC, LOAD, DATA
1 .text 00000730 00000000 00000000 00000074 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
2 .bss 00000096 0080010c 0000073c 000007b0 2**0
ALLOC
3 .debug_aranges 000002e8 00000000 00000000 000007b0 2**0
CONTENTS, READONLY, DEBUGGING
4 .debug_pubnames 000005e8 00000000 00000000 00000a98 2**0
CONTENTS, READONLY, DEBUGGING
5 .debug_info 00001e7c 00000000 00000000 00001080 2**0
CONTENTS, READONLY, DEBUGGING
6 .debug_abbrev 00000bb4 00000000 00000000 00002efc 2**0
CONTENTS, READONLY, DEBUGGING
7 .debug_line 000018c5 00000000 00000000 00003ab0 2**0
CONTENTS, READONLY, DEBUGGING
8 .debug_frame 00000470 00000000 00000000 00005378 2**2
CONTENTS, READONLY, DEBUGGING
9 .debug_str 000009e4 00000000 00000000 000057e8 2**0
CONTENTS, READONLY, DEBUGGING
10 .debug_loc 000010bc 00000000 00000000 000061cc 2**0
CONTENTS, READONLY, DEBUGGING
11 .debug_ranges 00000290 00000000 00000000 00007288 2**0
CONTENTS, READONLY, DEBUGGING
http://www11.ocn.ne.jp/~akibow/avr-libc-user-manual-1.4.3/mem_sections.html をしらべたところによると, セクションには以下の制限があるぽいです.*4
- .text (コード)は上位 16bit を 0x0000 にしなければいけない.
- .data, .bss (データ)は上位 16bit を 0x0080 にしなければいけない.
avr-objdump の出力結果のよみかたはこんな感じです.
セクション | 意味 |
---|---|
.text | アドレス 0x0000 から 1840 バイト割り当て済み |
.data | アドレス 0x0100 から 12 バイト割り当て済み |
.bss | アドレス 0x010C から 150 バイト割り当て済み |
atmega328 の データシート の 5.3 SRAM Data Memory にある以下の図的にもあってるようです.
つづきまして、avr-nm をつかってヒープ関連のシンボルを見てみたいと思います.
こんな感じでシンボル一覧を出力すると
avr-nm -n -C sketch\_090228a.elf \> syms.lst
こんな結果がでてきます
00000000 W __heap_end
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 a __tmp_reg__
〜〜〜長いので中略〜〜〜
00000730 T _etext
0000073c A __data_load_end
000008ff W __stack
00800100 D __data_start
00800100 D __malloc_margin
00800102 D __malloc_heap_start
00800104 D __malloc_heap_end
00800106 V vtable for HardwareSerial
0080010c B Serial
0080010c B __bss_start
0080010c D __data_end
0080010c D _edata
0080010e b intFunc
00800112 B timer0_overflow_count
00800116 B timer0_clock_cycles
0080011a B timer0_millis
0080011e B rx_buffer_head
00800120 B rx_buffer_tail
00800122 B rx_buffer
008001a2 B __bss_end
008001a2 N __heap_start
008001a2 N _end
00810000 N __eeprom_end
上記 avr-nm の結果とスケッチの実行結果を比較してみると,
と正しいようです.
また, http://www11.ocn.ne.jp/~akibow/avr-libc-user-manual-1.4.3/malloc.html に記載があるとおり, __heap_start は .bss セクションの末尾に配置されていることが見てとれます. なので .data, .bss のサイズによって __heap_start のアドレスが決定することになるようです.
まとめ
なんだかとりとめない内容で申し訳ありませんが, まとめると以下 3 点となります.
- ヒープを使うには普通に malloc をつかう(free, calloc, realloc もある)
- 最大ヒープ量は SRAM 容量, スタック使用量, __malloc_margin, .data & .bss サイズに依存する
- avr-objdump, avr-nm をつかってスケッチのメモリ使用状況をしらべることができる.
参照
- http://www11.ocn.ne.jp/~akibow/avr-libc-user-manual-1.4.3/malloc.html
- http://www11.ocn.ne.jp/~akibow/avr-libc-user-manual-1.4.3/mem_sections.html
- atmega328 データシート
*1:0x8F9 - 0x20 - 0x1A2 = 0x725
*2:ヒープのリスト管理領域含む
*3:ここでは c:\sketch_090228a にあるものとして説明しています
*4:ここを調べたところ, ハーバードアーキテクチャだからという理由が記述されていますが追い切れていなくて詳細不明です.