3/31 (金)
[Prog] UNIX シグナルを再帰的に使用する
シグナルが発生しシグナルハンドラに処理が移った場合、 同種のシグナルは自動的にマスクされる。 そのためシグナルハンドラ中で再帰的なシグナルが起こせない。
シグナルを再帰的に受け取りたい場合には、
sa_flags
に SA_NODEFER
を
セットすればよい。
サンプルプログラム。
mmap で 16 ページほど書き込み保護がある仮想メモリを取って、
一番下位のページに書き込みを行う。
SEGV ハンドラ内でフォルトアドレスより一つ上のページに書き込みを行っているので、
うまくいけば再帰的に SEGV ハンドラが呼ばれることになる。
#include <signal.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/mman.h>
static volatile size_t pagesize;
static volatile char* boundary;
static void signal_handler(int sig, siginfo_t* sig_info, void* sig_data) {
char* fault_address = (char*)sig_info->si_addr;
char* page_aligned_address = (char*)((uintptr_t)fault_address & ~(pagesize-1));
printf("Start SEGV handling: %p\n", fault_address);
fflush(stdout);
if (mmap((void*)page_aligned_address, pagesize,
PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) == MAP_FAILED) {
perror("mmap in signal_hanlder"), exit(1);
}
if (fault_address + pagesize < boundary) {
*(fault_address + pagesize) = 0;
}
printf("End SEGV handling: %p\n", fault_address);
fflush(stdout);
}
int main(int argc, char **argv) {
int value = 0;
char* p = 0;
struct sigaction newAct, oldAct;
sigemptyset(&newAct.sa_mask);
sigaddset(&newAct.sa_mask, SIGSEGV);
newAct.sa_sigaction = signal_handler;
newAct.sa_flags = SA_SIGINFO | SA_NODEFER | SA_RESTART;
if (sigaction(SIGSEGV, &newAct, &oldAct)) {
perror("sigaction error"), exit(1);
}
pagesize = (size_t)sysconf(_SC_PAGESIZE);
p = mmap(0, pagesize * 16, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
boundary = p + pagesize * 16,
*p = 0; // force to write an unmapped-memory page.
return 0;
}
追記:4/5
どうも SA_NODEFER
でこのような再帰的な呼び出しができるのは、
Liunx だけだったらしい。
昨年の8月に流れたパッチ
http://www.kernel.org/hg/linux-2.6/?cs=0a3157772326で、
この処理は塞がれてしまった。
む〜。
3/27 (月)
IA-64 プログラムはむずかしいにょ
シリアライズ命令を入れるのが面倒〜〜〜。
ちょっとシリアライズをサボっただけで、動かなくなるものなのか?
rsm psr.ic ;; ;; srlz.i ;; こいつを忘れるとハングアップ mov %0=cr.itir mov %1=cr.ifa
覚え書き
家賃払った。
3/26 (日)
3/23 (木)
AS/400 の仮想メモリ機構
3月11日の日記を書いた後に思うところがあって、Inside the AS/400 を読み直してみる。 と言っても、書籍ではなく日本 IBM が昔 Web で公開していたのをダウンロードして保存しておいたものだが。
AS/400 の仮想メモリシステムは、 以下のような特徴的な機能を持っている。
- 単一レベル記憶 (Single-Level Storage; SLS)
-
OS の仮想メモリの構成方法には MAS (Multiple Address Space) と
SAS (Single Address Space) の二通りの方法がある。
MAS はプロセス毎に別々の仮想メモリ空間を割り当てる方法だ。
SAS は OS に一つだけその代わり巨大な仮想メモリ空間があり、
その中に個別のプロセスの仮想メモリ空間を割り当てて行く方式だ。
SAS の良いところは、 プロセスの切り替わりに TLB フラッシュ等が不要になり プロセススイッチを高速に行える。 AS/400 をオンラインで使っている際には 1,200 命令毎にタスクスイッチが起きていて、 TLB フラッシュをしているようでは速度がでない。 逆に SAS のデメリットとしては、 プロセス内のメモリ保護が MAS よりも面倒になる。
SLS は第一に SAS である。 SLS が他の SAS と違うのは共有メモリの扱いか?
一般の共有メモリは複数の仮想メモリ空間の間で同一の物理メモリを共有するが、 異なる仮想メモリそのものは別々で、 プロセスのページテーブルを同一の物理ページフレームに向けることで実現する。 一方 SLS の共有メモリでは、 共有メモリ域として割り当てられた仮想メモリ領域を複数のプロセスが共有することで実現する。 物理メモリのページフレームと仮想メモリページが 1:1 の割り当てになるので処理が簡単になるという。 反面、 いったんメモリ上に取られた共有メモリ域の仮想メモリは回収して再利用できないという弱点を持つようだ。
あと AS/400 はセグメンテーションをオフにした形で PowerPC を使っている。 - 持続性 (Persistence)
- これも SLS の特徴なのだが、
巨大な SLS のメモリ空間がそのまま memory-mapped file のようにディスクに割り当てられている。
これがスワップであり、コアダンプであり、同時に memory-mapped file だ。
UNIX 的な MAS の世界とはかなり違う。 - ポインタ保護のタグ
- AS/400 用の PowerPC はエラー訂正コード(ECC) を修正して特別ビットを持たしている
(タグ活動モード)。
データロード時にポインタを読み込んだスロットには 1 を立てておき、
ユーザーが何かデータ書き込みを行うと 0 に変り
ポインタに何かいい加減な値を代入したことが分かるようになっている。
GC 屋としては垂涎ものの機能だが、高価すぎないか? - C2機密保護の監査
- ユーザープロセスの行ったメモリアクセスをロギングできる機能。
タグ活動モードとか普通のマシンにちょっとないハード機構を要求するのはまずいよな。
3/21 (火)
[Work] Linux のルートディスクが吹き飛んで
IA-64/Linux のカーネルのビルド & テストを繰り返しているうちに、 ルートディスクが吹っ飛ぶ。 オリジナルのカーネルとテストカーネルを入れ替えながらリブートを繰り返しているうちに、 パーティションテーブルの中のパーティションのタイプ情報が消えてしまったようだ。
幸い重要データは RAID システム上にあったので OS を入れ直すだけですんだが、 この忙しいのにえらい時間を使わされるとは…
3/20 (月)
[Linux] CPU の活線挿抜
商用 UNIX の多くには稼動中に任意の CPU を停止状態にする機能がある。 Solaris の場合には psradm(1M)、psrinfo(1M) で、CPU を停止させ、保守作業を行ない、再実行するということが簡単にできていた。
エンタープライズ系の機能の薄い Linux には CPU のホットプラグはできないものと信じていたのだが、gotom 氏から話を聞いて Linux は 2.5 系カーネルから CPU のホットプラグに対応していると知る。 デフォルトで機能は有効になっていないが、CONFIG_HOTPLUG_CPU=y にしてビルドし直せば使えるようになる。
使い方は /sys 以下のファイルを触る。
/sys/devices/system/cpu/{cpu0,cpu1,...}/online
があり、例えば CPU1 を停止させたい場合には以下のコマンドで対応するようだ。
# echo 0 > /sys/devices/system/cpu/cpu1/online
再開させたければ 1 を代入。
ただしこの機能では、ブートプロセッサの CPU0 はなぜか停止させることができない。 この制限は IA-32 でも IA-64 でも同様。
3/19 (日)
HFP (Hexadecimal Floating-Point)
今日も IBM System/370 のお勉強。
S/370 の浮動小数点は Hexadecimal Floating-Point (HFP) と呼ばれ、他のプロセッサで一般的な Binary Floating-Point (BFP) とは違う。
BFP は (符号) × 2^(指数) × 1.(仮数ビット)
という形をしているのに対して、HFP は (符号) × 16^(指数) × (仮数ビット)
という形をしている。
仮数ビット部は 4 ビットづつ digit の配列として構成されていて、仮数ビット部のシフトは 4 ビット単位になる。
仮に digit という型があるのなら、32 ビットの HFP データは以下のような構造になる。
struct HFP32 { bit sign:1; char exponent:7; digit significand[6]; };
指数は 16^-64 から 16^+63 までをあらわせるように、exponent は 64 を引いた値を使う。
数値 | 符号 | 指数 | 仮数 |
---|---|---|---|
+0 | 0 | 0000000 | 0000 0000 0000 0000 0000 0000 |
0.5 | 0 | 1000000 | 1000 0000 0000 0000 0000 0000 |
1 | 0 | 1000001 | 0001 0000 0000 0000 0000 0000 |
15 | 0 | 1000001 | 1111 0000 0000 0000 0000 0000 |
16 | 0 | 1000010 | 0001 0000 0000 0000 0000 0000 |
HFP は指数の底が 16 なので、同じ指数ビットを持つ BFP よりもより広い数値範囲をカバーできる。 一方、ビットで表現できる次に大きい(小さい)数字までの差分が、BFP では段差がなく滑らかなわけだが、HFP では「桁上がり」があるためガクガクと段差ができるのがデメリットかもしれない。
あと BFP の仮数の前にあった「隠された1」が HFP にはない。 ちょっと損をしている。
[Movie] 機動戦士Zガンダム III 星の鼓動は愛(公式)
会社での仕事を途中で切り上げて、川崎チネチッタで17:20からの部を鑑賞。上映は7番館。 日記と mixi を調べてみると、昨年の 6月18日 に I を、11月5日に II を鑑賞している。
TV 版のストーリーがだいぶ端寄られ、地球再降下がなくなっている。 それでもかなりハイペースの進行。
ラストは ZZ にはつながらないエンディングだが、ZZ は黒歴史ってことで…
P.S.
Iの作画監督で II で降りたはずの恩田尚之が、III では再び作画監督を勤めている。 Why?
3/18 (土)
[Work][Linux] 初めての Linux カーネルハック
昼から会社に出勤。 Java 最適化ルーチンに出たバグの仕事を片付けてから、Itanium2 マシンで動作する Linux カーネルのビルドに取り掛かる。
Itanium2 マシンは Fusion MPT という SCSI チップで動いているらしく、デフォルトのビルトだとカーネルが動いても root ディスクがマウントできない。 initrd をゴリゴリ作らねばならないのだが、どうやって作るのが忘れていることに気づいた。 2年ぐらい前にディスクレス・ネットブートのために散々試行錯誤したことがあったのに、さっぱり忘れているよ (T_T)
埒があかないので RHEL4 のカーネルである 2.6.9-22.EL のソースをダウンロードして、そのままビルドする。今、動いてるのと同じものができるはずだが、なぜかネットワークカードが動かない… 「まぁ、いいや」と投げやりな気分で、今日の本作業に取り掛かる (この時すでに日付が変わっていた)。
2月27日の調査で、ユーザラントのメモリフォルト処理(つまり SEGV シグナルハンドル)に 1.3GHz Itanium2 で 2,000 サイクル程度かかると分かった。
問題はカーネルラントでどのぐらい時間がかかるかだ。
メモリ系の例外処理を行うカーネルハンドラの先頭で CPU のリアルタイムクロックカウンタを採取し、siginfo_t
構造体の __pid0
フィールドが空いているので、そこに情報を詰めて返すようにカーネルを修正。
結果、メモリバイオレーションからハンドリングルーチンの先頭に到達するまでに、最短でも 200 サイクル、半分の確率で 600 サイクルかかる。 ユーザラントの処理の 1/4 ぐらいが本質的なものだったようだ。
不思議なのはアドレス 0 へのヌルポインタ書き込みも、書き込み属性を落としたページへのメモリ保護違反書き込みも速度的に大差がなかった点だ。 ページ属性が read only の場合にはそのページの情報自体は TLB に入っている。 一方、ヌルポインタの場合にはページテーブル中にエントリがないのだが、TLB には情報が入っていない。 TLB ミスの後に VHPT ウォークの処理がかかる分だけ遅くなりそうな気がするのだが…
3/16 (木)
[Work] 大量の MSDN サブスクリプションの CD/DVD の整理
引越し前に MSDN Professional の CD/DVD の整理を行う。 隔週ぐらいで会社に CD/DVD の箱が届くわけだが、必要なソフトはオンラインでダウンロードしてしまうから物理メディアは使わないのよね…
日本語版と英語版以外のメディアは捨ててしまう。 南無三。
3/13 (月)
[CPU] Non-faulting の probe 命令
Virtual Memory の話続きだが、世にある CPU の中には仮想メモリ空間上のメモリアドレスがアクセス可能かどうかをフォルトを起こさずに確認できる命令を持っているものがある。 例えば以下のような命令がある。
- IA-64 の probe.{w,r} 命令
- x86 の VERR/VERW 命令
- SPARC V9 だと alternative space を使ったロード命令
こういう命令を使えば、ユーザラントでページのアクセス権やマップ/アンマップ情報がとれそうな気がする。 いろいろ試行錯誤してみる。
とりあえず IA-64 の probe.{w,r} 命令で実験。 マップされた情報の read/write 属性は取得可能であった。 しかしマップされていないページにアクセスすると SEGV シグナルが発生する。 IA-64 で TLB ミスが発生した場合 OS に管理が移ってしまうので、命令レベルで「穴が空いているページ」を特定するのは無理なのかも知れない。
#include <stdio.h> int probe_writable_address(void* p) { unsigned long res = 0; asm volatile ("probe.w %0=%1,0" : "=r"(res) : "r"(p)); return (int)res; } int probe_readable_address(void* p) { unsigned long res = 0; asm volatile ("probe.r %0=%1,0" : "=r"(res) : "r"(p)); return (int)res; }
IA-32 の VERR/VERW 命令の方は正常に動作せず。 アンマップページも、読み書き禁止ページも、普通のページも同じように ZF=0 (アクセス不能) を返す。 あんまり使えない機能みたいだ。
3/9 (木)
[Prog] Linux Ski Simulator (公式)
2月27日に買った詳細Linuxカーネル 第2版を読みながら、よろめきながらも IA-64/Linux のハックに取り掛かる。
Itanium2 マシンはあるが、hp の提供している IA-64 用エミュレータ上でデバッグするほうが楽らしい。 ソフトをダウンロードしてインストール中。 もう少し使い方が分かったらこの日記に書き書きしてみよう。
つ ttp://www.gelato.unsw.edu.au/IA64wiki/CrossDevelopment
3/7 (火)
デジカメの写真を DVD-R に焼く
転ばぬ先の杖。 DiMAGE Xi で撮りためてきた デジカメ写真を DVD-R に焼きつける。 数えてみると3年で3,903 枚、2.5GB分になっている。
購入当時 5万円くらいしたし途中 SD カードを買い足したことを考えると、1枚20円ぐらい掛かっている計算。 貧乏性なので、元を取ったと感じるにはランニングコスト(?)が1枚5円ぐらいまで下がらないといかんなぁ。
3/5 (日)
Bフレッツのハイパーファミリー
昨年の8/31に案内があった Bフレッツニューファミリータイプの宅内装置の取替工事が実施される。
ニューファミリーは収容局の終端装置(B-OLT)と地域IP網の間が 100 Mbps なのを最大32人で共有していたのがボトルネックだったのだが、ハイパーファミリーは 1Gbps に底上げされた。
久しぶりに speed.rbbtoday で速度チェックをしてみる。 前回は2004年2月17日。
SPEED 2.5 (speed.rbbtoday.com) 計測日時 : 2006年3月06日月曜日 05時31分26秒 下り(ISP→PC): 47.3Mbps 上り(PC→ISP): 44.33Mbps
上り・下りとも理論速度の半分出れば御の字でしょう。
「ゆうき亭」で晩飯 (公式?)
3/4 (土)
掃除機を購入
部屋の掃除はコロコロと雑巾を使ってやっていたが、限界を感じ掃除機を購入することにした。 購入場所は渋谷のさくら屋。
ダイソンの掃除機はパワフリャなんだけど筐体が大きいのと値段が高いので、日立のたつまきサイクロン サイクロンクリーナー(CV-SJ9-A)を購入。
ほこっりっぽい部屋を掃除すると、ゴミダメのバスケットダストケースが2回もいっぱいになる。
うひょ〜。
3/3 (金)
[Prog] Linux で mmap、mprotect、munmap の速度
2月27日の続き。
IA-64/Linux で mmap 系命令の実行速度を計測してみる。
Itanium2 1.3GHz L2:3MB なので 1300 サイクルで 1μ秒になる。
確保するメモリは 8G バイト。
API | サイクル数 | 備考 |
---|---|---|
mmap | 1,300 | PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS |
mprotect | 16,000 | PROT_READ|PROT_WRITE と PROT_NONE を交互に |
munmap | 23,000 |
mmap の異常な速さが気になるがいったいなぜ。
mmap も遅くはなっているが、munmap ほどではありません。
あとは、他CPUとの同期処理などをやっていると思われます。
一応、調べたデータを↓(400mくらいでやめました)
Allocate size = 16384
mmap = 7747550, ave = 7747
munmap = 7835123, ave = 7835
munmap/page = 7835123
Allocate size = 32768
mmap = 7747587, ave = 7747
munmap = 7847158, ave = 7847
munmap/page = 3923579
Allocate size = 49152
mmap = 7745226, ave = 7745
munmap = 7852062, ave = 7852
munmap/page = 2617354
snip snip snip
Allocate size = 403980288
mmap = 7853898, ave = 7853
munmap = 55702646, ave = 55702
munmap/page = 2259
Allocate size = 403996672
mmap = 7861224, ave = 7861
munmap = 55693471, ave = 55693
munmap/page = 2258
Allocate size = 404013056
mmap = 7859386, ave = 7859
munmap = 55721878, ave = 55721
munmap/page = 2259
Allocate size = 404029440
mmap = 7854493, ave = 7854
munmap = 55725049, ave = 55725
munmap/page = 2259
カーネルソースを見ながら該当箇所をさがしています。