2011.6
やあみんな、ビリーだよ。
今回は、メモリの使いかたについて説明してみよう。
これまでにビリーが講義した、
「七の巻:スタックってなあに?」「八の巻:メモリを壊してみましょう」
「拾壱の巻:コードサイズを聞かれたら」で復習しておくと、さらに理解度アップだよ!
プログラムの本、コンパイラのマニュアルなどを見ていると、ヒープ領域やスタック領域という言葉が出て来るよね。
これはいったい何だろう?
メモリの領域をさしているようだけど、「コードサイズを聞かれたら」で解説した、セクションとは違うのかな?
『セクション』は、ROMやRAMをどのような用途に使うかを決めるものなんだ。
「プログラム(コード)を配置する領域」「データを配置する領域」などだね。
組込みの世界では、
一般的にプログラムやOSをコンパイル(リンク)した時点でセクションのアドレスとサイズが決まっていて、
動作中にセクションのアドレスやサイズが変わることは無いんだ。
これに対して、『ヒープ領域』や『スタック領域』は、プログラム中で一時的に使用するメモリのことで、
普通はRAM上のどこかのセクションの一部に属することになる。
『ヒープ領域』は、アプリケーションやOSで動的に割り当てたり解放するものなんだ。
プログラムで一時的に必要になるメモリで、例えばファイルを読み出すときに読みだしたファイル内容を置いておいたり、
ネットワークでデータを送受信する時にデータを置いておく時に使うよ。
必要な時に動的に確保し、使い終わったら解放することで、限られたメモリを有効に使うことができるね。
malloc()という関数でメモリを割り当て、free()という関数で解放するのが標準的な方法なんだ。
左のような関数を実行すると、
100バイトの領域がメモリ中のどこかにヒープとして
確保されることになるんだ。
どこに確保されるかはmalloc(100)を実行した時点で決まるので
前もって知ることはできないけど、
data1 という変数にこのヒープ領域のアドレスが格納されるので、
これでアドレスを知ることができるんだ。
『スタック領域』はコンパイラやOSが割り当て、アプリケーションでは自由に操作できない領域だ。
プログラムが内部的にデータを保存しておく必要がある場合に、スタック領域が使用されるよ。
このほか、C言語でプログラムを作る場合などでは
「自動変数」と呼ばれるものがあって
(オート変数、スタック変数等とも呼ばれる)、
スタックに配置される変数があるんだ。
左のような関数を作った場合、
data1やdata2は通常スタック上に配置される。
ヒープ領域はメモリ上のどこかの場所に確保されるわけだけど、
どのアドレスになるかは OS、開発環境(コンパイラ、リンカ)の他、
製作するプログラムによるので、
同じCPUを使っていれば必ず特定のアドレスになる、
というものではないんだ。
例えば、あるOS/コンパイラの組み合わせでは、メモリ(RAM)は以下のように使用される。
このような、メモリのアドレスと用途を表した図をメモリマップと呼ぶんだ。
(VECT)や(C)などがOS、コンパイラなどによって「セクション」と呼ばれるものだ。
左図のようなメモリ配置の場合、
「ヒープ領域」は「OS管理領域」の中に
その一部として存在する。
このようなメモリ配置の場合、
コード、初期化データ、未初期化データの各領域は
製作するプログラムによってサイズが変わるため、
OS管理領域のサイズや開始アドレスも
これによって
変わってくるんだ。
さっきのメモリマップに出てきた「OS未使用領域」とは、何だろう?未使用なので、使われない?
ここでいう「OS未使用領域」とは、OSの管理下にはおかず、
アプリケーションが自由に使用できる領域のことを指すんだ。
OSやコンパイラ(リンカ)の設定でこのような領域を作ることができるよ。
ではわざわざ「未使用領域」を設けるメリットって、何だろう?
答えはいくつかあるのだけど、最大のメリットは、OSからヒープ領域を割り当ててもらう方式とは違って、
プログラムから読み書きするアドレスを決め打ちできることなんだ。
OSからmallocなどを使ってメモリを割り当てると、毎回アドレスが同じになるとは限らない。
一方、未使用領域をアプリケーションで直接使う場合は、必ず同じアドレスを使用することができる。
マルチタスクでプログラムを組む場合に、複数のタスクでメモリを共有する時は、
事前にアドレスが分かっていると便利だね。
例えば、デジタルカメラで写真を撮ってメモリカードに保存することを考えてみよう。
単純に設計すると、写真を撮って、JPEGなどのファイル形式に変換して、
メモリカードに画像を保存する、ということになる。
ファイル変換とメモリカードの保存の処理は時間がかかるから、
連続で写真を撮りたくても、保存が終わるまで次の写真を撮ることができないね。
そこで、「写真を撮る」タスクと「ファイル変換しメモリカードに画像を保存する」タスクを
別にすることにしよう。
1枚の画像で4MBのサイズがあるとして考えるよ。
「OS未使用領域」として、写真4枚分、16MBの領域を設定する。
「写真を撮る」タスクは、ここに画像データを保存するだけにしよう。
メモリにデータを書き込むのは、メモリカードに書き込むのよりもはるかに速いから、
「写真を撮るタスク」は続けて4枚まで連続で写真を撮ることができるね。
「ファイル変換しメモリカードに画像を保存するタスク」は、
あらかじめ決められたメモリアドレスに置かれている画像データをJPEGなどに変換し、メモリカードに保存する。
アドレスがあらかじめ決まっているので、両タスク間で画像データのアドレスを連絡する手段を作らなくてもよくて、
簡単だね。
次回は、下の3つを中心に解説するよ。
- スタックとヒープの違いは?
- どのような場合に使い分けるべき?
- OSを使用しない場合はどうなる?
お楽しみに!