パスワードを忘れた? アカウント作成
737753 journal

BAKの日記: x86 中華思想

日記 by BAK

Linux カーネルにおいては,歴史的・ユーザシェア的な経緯もあってやはり中心は x86 アーキテクチャである. ARM, MIPS, 68k, PPC, SPARC などなどサポートされているが,x86 と同等な機能・安定性はゆめゆめ期待してはいけない. 特に,最近カーネルに追加された機能は,まだまだ他のアーキテクチャの洗礼を受けていない,と考えたほうがよい.

ARM や MIPS なんかに移植していてハマるあたり,としては…

アラインメント
ARM や SPARC なんかの RISC CPU では,奇数バイトへの 16 bit word access とか,4 で割り切れないアドレスへの 32 bit word access なんかすると,例外が発生してトラップがかかったりして,不幸なことになる. 例外で捕捉するのならまだいいのだが,ARM Linux のユーザプロセスの場合,変なメモリアクセスをして,そのまま何もなかったように実行してしまうので,始末が悪い.

構造体への詰めもの
ARM, MIPS, SPARC などでは,構造体では,アラインメントの不整合が起きないように構造体のメンバが配置される. たとえば,struct hoge { char a; int b; }; なんて構造体の場合,構造体の先頭は 4 の倍数アドレスに配置され,int b を 4 の倍数アドレスに配置するために char a のあとに 3 バイトの詰め物がされる. x86 では,このような詰め物はない. 構造体を素直に使っている場合は平和なのだが,どこかのストリームから読んできたデータのポインタを構造体のポインタにキャストしてデコードするとかいうワザを使うと,この詰めものが問題になる.

キャッシュの一貫性
x86 の場合は,バススヌープをしていて,cachable なエリアのメモリに対し DMA 転送を行っても,つじつまがあうよう,ハードウェアレベルでよきにはからってくれる. が,他のアーキテクチャでは,そのような親切な機構はない,と思ったほうがいい. つまり,メモリを割り当てる時に「ここはキャッシュ可能」「ここはキャッシュ不能」と明示的に割当を行う必要がある,ということである.

もちろん,メモリマップドな I/O に対しても(一部の例外を除いては)キャッシュ不能のメモリマッピングを行うべきである.I/O ポートというのは,書き込んだ値がそのまま読みだせるとは限らず,また,一度読み込んだ値が次に読んだ時にも同じである,とは限らない. また,デバイスによっては「読む」「書く」というバス上の信号の変化自体が意味を持つこともある. というわけで,I/O ボートへのポインタは,volatile で宣言しておいて,コンパイラが最適化して操作を端折ってしまわないようにするのが普通である. (話がちと脱線してしまったな)

char の符号
これは ARM の場合に当てはまる話である. ARM の gcc では,char は符号なしの 8 bit の数である. が,x86 では,符号付と解釈される. ANSI の C の規格では「char 型の符号あり/なしは実装依存」となっているので,これ自体は何ら不具合ではない.

が,char で宣言した変数を符号付きの変数として使用していることがあり,これがコードの互換性を損ねる場合がある. 解決法は

  • 明示的に signed char で変数宣言する
  • コンパイルオプションで -fsigned-char を指定する

「とりあえず動くカーネルを」という場合は後者の方が手っ取り早いが,ホントは前者の方法で対処した方が良いと思う. 敢えて char が符号なし(or 符号あり)となっているのは,パフォーマンス上の理由があるかもしれないからである.

typodupeerror

一つのことを行い、またそれをうまくやるプログラムを書け -- Malcolm Douglas McIlroy

読み込み中...