OS自作入門 -Advent08-

NO IMAGE

Step3 静的変数の読み書き 前半

どうやらこの本は前半にある程度の知識に関する座学、その後にソースコードに手を加えて先に進んでいくという流れらしい。なので私にとっては前半部分の方がやや敷居が高い。ソースコードの方になるとまぁ読めるし書くのも苦痛ではないのでそんなでもない。

ということで今日はメモリに関するお話。

何やら前回までの方法だと「変数」というものを使うことができないらしい。それはプログラミングをする上ではちょっと辛い。

今回はそんな話も含めて以下のような内容が書いてあるらしい。

  • ROM
  • RAM
  • データ領域
  • スタック
  • リンカ・スクリプト

メモリ構成

通常のアプリケーションレイヤーでは仮想メモリという機構が動いてアドレスを仮想化してくれているため、メモリ構成やメモリ・マップまで気にする必要はない。

しかし今のプログラムの状態だと

volatile int value = 10;
....
....
  putxavl(value, 0); puts("\n");
  value = 20
  putxavl(value, 0); puts("\n");

とやってもvalueの値を変えることはできない。この仕組みを理科するにはROMとRAMの理解をする必要があるようだ。

ROM (Read Only Memory)
読み込み専用。プログラムからの書き込みは不可。不揮発性(電源OFFにしても内容が消えない)

RAM (Random Access Memory)
読み書き可能。揮発性。

本来はCPUとは独立していて、前回同様に外部に接続してアドレス・マッピングをすることも可能。だが機材が大きくなってしまうこと等の問題によりある程度の容量のROM, RAMは備えていることが多い。

H8の場合

|——————- 0x000000
| 割り込みベクタ
|——————- 0x0000ff
| 内臓ROM(512KB)
|——————- 0x07ffff
|——————- 0xffbf20
| 内臓RAM(16KB)
|——————- 0xffff1f
|——————- 0xffff20
| 内臓I/Oレジスタ
|——————- 0xffffe9

とこんな感じになっているらしい。

CPUはメモリ上にあるプログラムしか動かすことができない。このプログラムは単にメモリ上に展開されるのではなくいくつかの領域に分けられて展開される。「テキスト領域」「データ領域」「BSS領域」らしい。テキスト領域には機械語のコードが、データ領域とBSS領域には静的変数が配置される。データ領域とBSS領域の違いは、データ領域には初期化された静的変数、BSS領域には初期化されていない静的変数が入る。コードの節約とかのためのBSSに置いたりするらしい。

メモリはこのように分かれているが、実行形式ファイルも同様にある程度分かれているもののようだ。まぁ確かにメモリ上にどのように展開するのか。という情報がない限りはうまく動作させることはできないだろう。今回の実行形式ファイルはELF形式と呼ばれるものらしくreadelfというコマンドで解析することができる。

解析した結果をGithubに置いておいた。.textセクションはtext領域に、.dataセクションはデータ領域にそれぞれ相当するものらしい。なので、例えばserial.cのregという構造体が_regというエントリになっているのが読めたり、regsのアドレスが.dataセクション内に配置されていたり(初期値のある変数なので)、_main関数が.textセクションにあることが分かったり、.textセクションが上記内情ROMにマッピングされる箇所に配置されていたり、などが読み取れるようだ。


プログラムはROM上に配置される必要がある。この配置場所の設定はリンクの段階で行われ、プログラムのどの部分がどのアドレスに配置されているのかというのを指定する必要がある。それをやっているのがリンカ・スクリプトとよばれるものらしい。ld.scrのこと。SECTIONという記述があり以降.text.とか.dataとかを記述しているのが読み取れるかと。

これを順次配置していく形になる。なので例えば一番最初は割り込みベクタ(前回の話で_startが最初の要素として定義されている配列)が書き込まれ、その次には機械語のコード.textが.textセクションに配置され…という風になっていく。

しかしこのままだと静的変数の領域もROM上に乗ってしまい変更ができなくなる。単純にdata領域を書き込む初めのアドレスをRAM上に配置されるようにリンカ・スクリプトを書き直すという手もあるが、それだとh8writeはROMに対して書き込むので.dataセクションを書くことができません。なので以下の方針をとるらしい。

  • 初期値はROMに保存
    これによりh8writeでも書き込みが可能
  • 電源ONにして起動した後に、プログラム最初の方でROM => RAMにコピーする
  • プログラムから変数へのアクセスはRAM上のアドレスを指すようにする

この過程により、「変数の初期値が配置されるアドレス」と「プログラムがアクセスする際のアドレス」は別々のものになり、それぞれ「物理アドレス(Physical Address: PA, Load Address: LA)」、「論理アドレス(Logical Address, Link Address: LA)」と呼ばれるらしい。


ELF形式の場合は、セグメントという管理単位もある。プログラム実行時にはセグメント情報が参照されメモリ上にロードされる(ローダーというプログラムが行う)。セクション情報との違いは、リンク時に同じ領域にリンカがまとめるために使うものであり、セグメントはローダーが参照してメモリ上に配置するためのものとのことだ。

これで今日の分は終了。明日からおそらくROM => RAMのコピースクリプト書いたり、物理アドレスと論理アドレスの参照をして見比べたりしてみるんだろう。と推測する。