2009年11月21日土曜日

オブジェクトの空間的取扱いに関する整理(私家版)

オブジェクトを計算機の中でどういう単位というか塊で取り扱うことがあるのかなぁ、というのを、ふと、自分なりにまとめてみたくなった。ここでいうオブジェクトは、オブジェクト指向のオブジェクトではなくて、なんといったらいいのか、0と1の連なりのこと。

こうやって書いてみると、この程度のことしかまとめとして書けない自分に愕然とする。。。

こつこつやるしかないですね。。。


- オブジェクトの空間的取扱いに関する整理(私家版)
- ビット

オブジェクトはビットで表現する。

- 計算機のビットの取扱い

ビットは表現力が低いので、複数ビットを単位と
した塊を定義して、それを計算機は処理対象とす
ることが多い。

- 処理単位:ニブル

割愛。

- 処理単位:バイト

処理単位のひとつ。現在は8ビットであることが多
い。記憶領域の番地もバイト単位で指定すること
が多い(これをバイトアドレッシングと言う)。

- 処理単位:オクテット

バイトはアーキテクチャ毎に異なるため、8ビット
を曖昧さなく表す用語として、通信の世界で使わ
れはじめた。

- 複数バイトの取扱い

複数バイトになると、それを記憶領域上にどういう
順序で配置するかという選択が発生する(エンディ
アン)。

- 処理単位:ワード

ワードは文脈依存であり、いくつかの定義がある。
文脈は、CPUアーキテクチャ、記憶領域、OS、プロ
グラム言語などである。ここではCPUアーキテクチャ
文脈で話をする。

CPUアーキテクチャにおいてもワードの用法は揺れ
がある。例えば、PICマイコンなどでは、ワードは
バイトと並ぶような単位であり、バイトはデータ
メモリをCPUが取り扱う単位であり(8ビット)、ワー
ドはプログラムメモリをCPUが取り扱う単位である
(14ビット)。

しかし、PCアーキテクチャにおいては、ワードは、
バイトを束ねた単位とすることが多い。ここでは
その文脈とする。

バイトの表現力にも限界がみえたため、現在の計算
機は複数バイトを単位として処理単位とすることが
多い。その単位をワードと言う。複数バイトなので、
もちろんエンディアンの選択が存在する。

ワードは、CPUの歴史に依存している場合もある。
例えば、IAでは、現在でも2バイトがワードである。
ただし、ダブルワード、クワッドワード、ダブル
クワッドワードなどの単位が存在し、命令もそれ
らの取扱いを考慮して設計されている。

複数ワードをどう配置するかというエンディアン
の選択もある。通常、バイトエンディアンと同じ
ものを選択する。

- バイトアドレッシング、ワード、レジスタ

この3者のサイズは同値にするのが基本。ただし弱い
基本。

バイトアドレッシングが64ビットということは、ア
ドレスの表現が最大64ビットの数値ということ。

レジスタは、アドレスを扱うので、効率的にするに
は64ビット以上のサイズがあった方がよい。そこで、
基本、レジスタのサイズも64ビットにする。アーキ
テクチャ独自のメリットがある場合は、64ビット
以上にすることもある。

ワードは、CPU内のデータ処理やCPU内外のデータ転
送の単位である。これは少くともアドレスをまと
めて扱うことができるべきである。ただし、ワー
ドの場合、細かなデータの記憶領域への格納につ
いての効率など、いろいろなファクタがあるので、
そのままワード=64ビットとしないこともある。す
なわち、ワードは64ビット以下というのが基本で
あり、長いものはダブルワードやロングワードな
どと定義することが多い。

- 整列ルールと境界

複数バイトを記憶領域に配置することを考える。記
憶領域側からしてみれば、どこを開始番地にして、
どれだけの長さを配置することも可能である。

しかしCPU側から見ると、なんでもOKじゃなくて、
ルールがあった方が、いろいろと効率がよいこと
が多い。

なのでアーキテクチャの選択として、複数バイト
の連なりをどうメモリ上に置くべきかがルールと
して決まっていることがある。

よくあるのが、2バイトのデータは2バイト境界にな
ど、偶数バイトで境界を定めるものである。この場
合、3バイトのデータが複数ある場合、4バイト境界
毎に配置するため、1バイトずつの無駄がでる。

- 仮想記憶 (仮想アドレス)

複数プログラムが独立して動くことをハードウエ
アでサポートするために、記憶領域のアドレスは
仮想化されていることが多い。(仮想アドレスのニー
ズはこれだけではないが)

この仮想記憶のベースとなる機構はCPUにて実
装されることが多い。一般に、OSがそのCPUの機能
を利用しつつ、ハードディスクなどの二次記憶
の機構と統合して、記憶階層の機構を実現してい
ることが多い。この機構をページングシステムと
呼ぶ。

仮想記憶を実現する道具立てとして、仮想ア
ドレス、ページ、ページフレーム、ページテーブ
ルなどがある。ちなみにページングとは、ページ
をページフレームに割り付ける(物理記憶上にデー
タがある状態にする)こと。

- ページとページフレーム

仮想記憶を実現するために、仮想アドレスを複数バ
イトの単位で分割して取り扱う。この単位をページ
と言う。同じ単位で、実際に装備している物理記憶
領域も分割して取り扱う。これをページフレーム
と言う。

ページとページフレームを紐づけるのが、ページ
テーブルである。

例えば、IA-32では、現状4KBと4MBのページを取り
扱うことができる。

- ページングシステムとファイルシステム

ページングシステムがどのように二次記憶を使う
かは2通りの手法がある。

1つは、二次記憶の取扱いについてファイルシステ
ムに相乗りする手法である。

もう1つは、二次記憶の取扱いについて、ファイル
システムとは別の独自の取扱いをする手法である。

現在は前者が多い。ここでは前者を取り扱う。

- ページングの仕組み

ページングはいろいろなアルゴリズムや手法があり、
OSの設計によっていろいろである。

Linuxは、デマンドページングとコピーオンライト
(COW)である。(PCアーキテクチャのOSでは、現在
はみんなこれかもしれない)

- デマンドページング

デマンドページングではないということは、あ
るプログラムを動かすとき、始めに全てのオブ
ジェクトをページングするということ。デマン
ドページングは、始めは何もページングしてお
らず、メモリアクセスされるにつれてページン
グが進んでいくという方式。

- コピーオンライト

子プロセスを生成するときに、子プロは生成当
初、親プロのページフレームをCOWマーク付きで
参照する方式。すなわち複数のプロセスのペー
ジによって、単一のページフレームが共有され
ている。自分がCOWページに書き込みをするとき
に始めてコピーされる。

ページングで重要なトピックは、ページフレーム
の共有ということだ。共有できれば、物理記憶領
域の消費を抑えられ、空間的にも効率がよいし、
実行速度的にも効率がよい。

- ページングとオブジェクト

共有の鍵となるのは、書き込み(write)の有無だ。
そこで、ページへのオブジェクトの配置としては、
オブジェクトの中で、読み出しだけの部分はROペー
ジとして取り扱えるとよい。また書き込みがあるか
もしれない部分もまとめてCOWページとして取り扱
えるとよいだろう。

- 共有ライブラリと位置独立コード(PIC)

同じライブラリを使ったプログラムが複数動いてい
るとする。すると、それぞれの仮想記憶に別々に
ほとんど同じライブラリのオブジェクトが配置さ
れていることになる。これは空間効率が悪い。

だが、ROページにはできない。それぞれの仮想アド
レス空間におけるライブラリの再配置先は異なり
うるからだ。

そこで、位置独立コード(Position Independent
Code)という手法をとる。オブジェクトの構造を整
理して、再配置情報はCOWページへ、それ以外は、
ROページへいけるようにするというもの。すると
この後者の部分はアドレス位置独立のコードと言
える。

- セグメントシステム

仮想記憶とページングシステムについては一旦忘れ
る。

組み込み機器などえ、CPUのアドレス空間が小さく、
それに比して物理記憶領域の量が多い場合がある。
例えば、アドレス空間が16ビット(64KBアドレス可
能) で、物理記憶領域が1MBであるなど。こういう
状況にて、プログラムが64KB以上の記憶領域を使
うための仕組みとして、セグメントシステムがあ
る。

具体的には、アドレスの指定に16ビット値を2つ使
う。ひとつの値を左に4ビットシフトしたものを物
理記憶領域のアドレスの起点とし、もうひとつの
値をそこからのオフセットとする。これによって、
物理記憶領域のすべてをアドレスできるようにな
る。

ここで、ひとつの起点からオフセットにて指定で
きる領域のサイズは依然として16ビット(64KB)で
ある。この領域をセグメントと呼ぶ。起点のアド
レスをセグメントアドレスやセレクタと呼ぶ。

IA-32の32ビットプロテクトモードでは、レジスタ
のサイズは十分であるが、セグメントシステムが使
われている。

セレクタ(16ビット):オフセット(32ビット)

の組でアドレスを行なう。オフセットが32ビットな
ので、これだけで4GBを扱うことが可能である。こ
の領域のことも引き続きセグメントと呼ぶ。さて、
セグメントが4GBだとすると、セレクタを何につかっ
ているかという疑問がある。セレクタはセグメント
ディスクリプタという情報の指定に使われている。
セグメントディスクリプタは、主に

アクセス権(8ビット)
ベースアドレス(32ビット)
リミット(20ビット)

で構成され、リミットは1B単位または4KB単位で解
釈される。4KB解釈のとき最大で4GBになる。

32ビットプロテクトモードでは、セグメントシステ
ムとページングシステムの併用が可能である。


こつこつ。

2009年11月20日金曜日

【Linkers&Loaders】2 アーキテクチャに関する問題

この本を読むと、リンカがえらい縁の下の力持ちというか、重要というか、と思える。ハードウエア + OSというものどもに対して、アプリケーションソフトウエアが動くようにしているのはリンカなんですね。

あと、本書から、ABIやリンカにおいてC++が特別なポジションにいることが読み取れる。C++もいつか覗いてみたい。


** 2 アーキテクチャに関する問題 (Architectual Issues)
- この章、パタヘネを昔読んだのでなんとか理解でき
た。パタヘネを読んでいなかったら、挫折していた
だろう。しかし、結構時間がかかった。。。
- メモはほぼ本書と同内容なので、ここには出せない。
一部補足的なことだけ投稿する。

- LinuxのABIってどこでどのように規程されているん
だろう。それとも、それがELFそのものということか?
いや、ELFはフォーマットであって、その上でさらに
運用みたいなものがあるんだろう。
- バイト境界の説明で、「Nバイトで構成されるデータ
のアドレスは、少くとも下記log 2 Nビットが0でなけ
ればならない」という趣旨のことが書かれている。こ
れ、ちゃんと考えてみる。
- まず2を底とする対数について。
log 2 1 = log 2 #b1 = 0
log 2 2 = log 2 #b10 = 1
log 2 4 = log 2 #b100 = 2
log 2 8 = log 2 #b1000 = 3
...
- 2の倍数だけ整数となる。するとここで、Nバイト
で、と言っているのは2の倍数に限っているのかな。
- さて、バイト単位でアドレスが振られているとする
と、バイト境界というのはこんな感じかな?

#x00 #b0000 0000 -2 -4 -8 -16
#x01 #b0000 0001
#x02 #b0000 0010 -2
#x03 #b0000 0011
#x04 #b0000 0100 -2 -4
#x05 #b0000 0101
#x06 #b0000 0110 -2
#x07 #b0000 0111
#x08 #b0000 1000 -2 -4 -8
#x09 #b0000 1001
#x0A #b0000 1010 -2
#x0B #b0000 1011
#x0C #b0000 1100 -2 -4
#x0D #b0000 1101
#x0E #b0000 1110 -2
#x0F #b0000 1111
#x10 #b0001 0000 -2 -4 -8 -16
#x11 #b0001 0001
...
- あ、ほんとだ。log 2 Nが共通して0になってる。


こつこつ。

2009年11月19日木曜日

【Linkers&Loaders】1 リンクとロード (補足)

メモ。Linuxでオブジェクトファイルを解析する方法。

  • readelf
    だいたいこれで済む。
  • nm,objdump
    古くからある方法。nmはmakeの中などでも使われている。


readelfはDWARFにも対応している。

【Linkers&Loaders】1 リンクとロード


* Linkers & Loaders
** 1 リンクとロード (Linking and Loading)
- この章は、リンカとローダの歴史の紹介かな。

- そうか、アセンブリ言語において、シンボルとニーモ
ニックは、どちらも文字の連なりで表わされてはいる
けれど、まったく別物なんだな。
- ここで、別物、というのは、ニーモニックはざっくり
言うと文脈自由で変換されるが、シンボルはざっくり
言うと文脈依存であるということ。ここで言う文脈と
は、コードのかきぶりの文脈ではなくて、それを機械
語にして実行するときの文脈ということ。
- その「別物」であり「文脈」があるところを担当し
ているのがLinkerとLoaderと理解しておく。

- Common Lispのシンボルと対比しながら読んでいくと
大変興味深い。

- この章、かなり面白い。。。おすすめ。


こつこつ。

Linkers & Loaders を読む

さて、Autotoolsの本は、これからLibtoolに入っていく。さらっと読んでみると、頭にすっと入ってこない。これは読む前から予測できていた。そもそもlinkについての知識が少いからだ。概要というかイメージはわかるのだが、ちゃんと勉強したことがない。

そこで折角なので、Linkers & Loadersを読んでみることにする。

この本、いろいろなアーキテクチャ?(OS?)に触れているようだが、a.outとELF以外は、ざっと見るだけにしようと思う。

また、Perlでの実習がなかなかよいものらしいが、それは二回目に読むときにやることにするかもしれない。Perlはずいぶん書いていないから。これを読むためにPerlを復習する、というのは今のスタックの深さでは避けたい。スタックじゃなくてリングになってしまう気がする。。。

【autotools】8(US 9) 小規模なGNU Autotoolsプロジェクト

この本は、GNU Autotoolsの本というよりは、GNU Autotoolsプロジェクトの本だということを実感した。「GNU Autotools」をつかったソフトウエアプロジェクトの実践的なノウハウをチュートリアル形式で教えるというものだ。

C言語の書籍とかUnix APIプログラミングの書籍を読んでも、どうも実際のプログラミングとは距離があるなぁ、と感じていた部分がここだったのか、とも感じた。

また、題材が、Unixシェルを作るためのライブラリというのが面白い。これはいいオモチャを見付けた。先々、いろいろいじってみたい。


** 8(US 9) 小規模なGNU Autotoolsプロジェクト
- 今までの章で学んだことを含めて、小規模なサンプ
ルをいじってみるという内容。
- これは楽しみ。

- 本書では、トップレベルをきれいにしておくために
Autotools中間ファイル(install-shなど)をconfig/
に入れておく、と書かれている。small-1.0ではこう
なっているが、small-2.0では違う。config/ を使う
のはすでに時代遅れということか。
- いや、調べてみると、それはmakeが作るものとなっていた。
- さて、small-2.0のトップレベルはこんな感じ。

Makefile.am
bootstrap
config-h.in~
configure.ac
m4/
replace/
sic/
src/

- config-h.in~が、いまのところ謎。とくに'~'が、、、
- GNUのC言語のスタイルでは、

int func(void)

を

int
func(void)

とするのはソース検索のためだったのか。それだけ
なのかなぁ。
- libibertyとlibitにフォールバック関数のノウハウ
が蓄積されている。
- Sicというプロジェクトをやってみる。このプロジェ
クトは、Unixシェルのベースを提供するポータブルな
ライブラリを提供するのが目的。
- GNU Autotoolsプロジェクトの実戦的な基礎がつまっ
ている。これはいい!
- このサンプルソースは問題なくビルドできた。
- シェルが動いている!

small-2.0 $ ./src/sic
] date
2009年 11月 19日 木曜日 02:59:19 JST
] ls -F
Makefile autom4te.cache/ config-h.in config.status* m4/ stamp-h1
Makefile.am autoscan.log config-h.in~ configure* replace/
Makefile.in bootstrap* config.h configure.ac sic/
aclocal.m4 config/ config.log configure.scan src/
] exit
small-2.0 $


こつこつ。

【autotools】7(US 8) ブートストラップ


** 7(US 8) Bootstrapping
- メタの更新をどうするか、という話題。メタ、とは、
ソースコードの変更ではなく、configure.acや
Makefile.amを変更したときに、build system全体を
どう更新するか、ということ。
- 現状は、autoreconfが成熟してきたのかな、と思える。
AutomakeのInfoがautoreconfを使っているのと、
autoconf --add-missing で追加されるbootstrapの中
身もautoreconfになっているから。


こつこつ。