April 2006

「alternative マクロと自己修正コード」エントリのはてなブックマークのコメントが変態に満ち溢れている件について。

誰だ、犯人は!(笑い


エライメにあいます
この変態が!! ランキング!

このエントリーをはてなブックマークに追加

Pentium4登場時にWindows XPの起動がPentium4だとPentiumIIIよりもものすごく速い。
というキャンペーンがあって、みんなで「なんでやねん!」と連呼していたのだが、もしかしてWindowsもLinuxのalternativeマクロみたいな仕組みを入れてるんかね?
Linuxとは逆に、デフォルトがPentium4で。

それだとすると納得するんだけどな。
このエントリーをはてなブックマークに追加

alternativeマクロについて

シーケンスロック その3 において

> alternativeマクロは興味深い動作をするので、回を改めて詳しく説明しますが、

などと書いておいて すっかり忘れていた alternativeマクロですがいい機会なので解説しましょう。
いつものように lxr.linux.no あたりからソースを引っ張ってきている。

linux/include/asm-i386/system.h より
288 /* 
289 * Alternative instructions for different CPU types or capabilities.
290 *
291 * This allows to use optimized instructions even on generic binary
292 * kernels.
293 *
294 * length of oldinstr must be longer or equal the length of newinstr
295 * It can be padded with nops as needed.
296 *
297 * For non barrier like inlines please define new variants
298 * without volatile and memory clobber.
299 */
300 #define alternative(oldinstr, newinstr, feature) \
301 asm volatile ("661:\n\t" oldinstr "\n662:\n" \
302 ".section .altinstructions,\"a\"\n" \
303 " .align 4\n" \
304 " .long 661b\n" /* label */ \
305 " .long 663f\n" /* new instruction */ \
306 " .byte %c0\n" /* feature bit */ \
307 " .byte 662b-661b\n" /* sourcelen */ \
308 " .byte 664f-663f\n" /* replacementlen */ \
309 ".previous\n" \
310 ".section .altinstr_replacement,\"ax\"\n" \
311 "663:\n\t" newinstr "\n664:\n" /* replacement */ \
312 ".previous" :: "i" (feature) : "memory")


これがalternativeマクロの定義ね。
はっはっは、これを見せると確実に引かれるので省いたのだよ。

見やすくするために、このマクロを展開してみる。

まず、こんなファイルをカット&ペーストを駆使して作る

main.c
#define alternative(oldinstr, newinstr, feature) 	\
asm volatile ("661:\n\t" oldinstr "\n662:\n" \
".section .altinstructions,\"a\"\n" \
" .align 4\n" \
" .long 661b\n" /* label */ \
" .long 663f\n" /* new instruction */ \
" .byte %c0\n" /* feature bit */ \
" .byte 662b-661b\n" /* sourcelen */ \
" .byte 664f-663f\n" /* replacementlen */ \
".previous\n" \
".section .altinstr_replacement,\"ax\"\n" \
"663:\n\t" newinstr "\n664:\n" /* replacement */ \
".previous" :: "i" (feature) : "memory")

#define X86_FEATURE_XMM2 57 /* dummy */
#define mb() alternative("lock; addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2)


main(){
mb();
mb();
}


X86_FEATURE_XMM2 の定義は適当にでっちあげたが、何、かまやしない。

これを -S 付きでコンパイル

 $ gcc -S main.c -o main.s 


出来上がったファイルがこちら

main.s
	.file	"main.c"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
addl $15, %eax
addl $15, %eax
shrl $4, %eax
sall $4, %eax
subl %eax, %esp
#APP
661:
lock; addl $0,0(%esp)
662:
.section .altinstructions,"a"
.align 4
.long 661b
.long 663f
.byte 57
.byte 662b-661b
.byte 664f-663f
.previous
.section .altinstr_replacement,"ax"
663:
mfence
664:
.previous
661:
lock; addl $0,0(%esp)
662:
.section .altinstructions,"a"
.align 4
.long 661b
.long 663f
.byte 57
.byte 662b-661b
.byte 664f-663f
.previous
.section .altinstr_replacement,"ax"
663:
mfence
664:
.previous
#NO_APP
leave
ret
.size main, .-main
.section .note.GNU-stack,"",@progbits
.ident "GCC: (GNU) 3.4.4 20050721 (Red Hat 3.4.4-2)"


gccは親切なことに、asm文の範囲を#APPと#NO_APPで教えてくれています。
あとasm文が volatile修飾されているので素直にマクロが2つとも展開されています。

まず文法的な説明から
詳しくは

 $info gas 


で調べて欲しいが簡単に。

セクション切り替え
.section [section_name], "[FLAGS]"

FLAGS:
'a' allocatable, 実行時にメモリにロードされる。これがOFFなセクションってほとんど見ない
'x' executable, 実行可能

セクション名は任意のASCII名がつけれるが、"."(ドット) で初めておくのが
スカートのプリーツを乱さないと同じくらい、ここでのたしなみ。

.previous 1つ前の.section命令をキャンセルする。
つまり
.section .foo
.previous

.scction .foo
.text
と同じなのだけれど、最初が.text じゃなかったときもうまく動くのが利点。


ローカルなラベル
[positive_number]:

普通のラベルと違い、複数あられてもOKなラベル。
このラベルを参照するときは末尾に"f"か"b"をつける。
"f"は参照より後ろ(forward)で一番近いラベル、"b"は参照より前(backword)で一番近いラベルをあらわす。

.byte
.long
即値埋め込み命令。
.byte 57
とやればそのまんまバイナリに57という数字が埋め込まれる。
普通はC言語的な意味での「定数」をバイナリに埋め込むために使うが
たまに変態的な使い方として
x86プロテクトモードアセンブラの一部に16bitモードのアセンブラを埋め込みたいとか
ARMのアセンブラの一部にTHUMBコードを埋め込みたいとかいうバカ(褒め言葉)が
.byteを駆使して、実行コードを記述する例も見られる。

あ、話がそれた。

.align [argument]
後続のデータをargument byteにアライメントさせる。

また、この引数ではラベルへの参照と簡単な四則演算が使える。つまり

.long 661b
と書けばラベル661を前方参照してそのアドレスを埋め込む

.byte 662b-661b
と書けばラベル662のアドレス引く661のアドレス、つまりその範囲のコードの長さが埋め込まれる。
ここで、661bとかってのはローカルラベルだから、最初のmbと2番目のmbで661bの展開先が異なることには
注意していただきたい。
ここを理解しないと、何を埋め込んでいるのかサッパリ分からなくなってしまう。

つまり661bは引数oldinstrのアドレスになっていて、663fは引数newinstrのアドレスになる。


これを確かめるために、先ほどのmain.c をコンパイルして出来た実行ファイルをobjdumpしてみる

セクション:
索引名 サイズ VMA LMA File off Algn
(中略)
12 .altinstr_replacement 00000006 0804841c 0804841c 0000041c 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .fini 0000001a 08048424 08048424 00000424 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .rodata 00000008 08048440 08048440 00000440 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
15 .altinstructions 00000017 08048448 08048448 00000448 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA


よしよし、.altinstr_replacement と .altinstructions がちゃんとあるな。

あっと、寄り道になるけど補足しておこう。
なぜか .altinstr_replacement と .altinstructions の順番が逆になっているけど
これはセクションフラグに"x"を入れたかどうかの違いが現れている。
gccは特に指定しないと"x"がONになっているコード部分を実行ファイルの前のほうに
寄せるんです。


次に .altinstructions セクションをダンプしてみる。
ファイル位置は0x448でサイズが0x17とobjdump君はいっているので

[mkosaki@centos alternative_macro]$ od -A x -t x1  -j 0x448 -N 0x17 a.out
000448 50 83 04 08 1c 84 04 08 39 06 03 00 56 83 04 08
000458 1f 84 04 08 39 06 03


ほれ、でてきた。これを「じーーー」っと見ると

50 83 04 08 1c 84 04 08 39 06 03 00 
56 83 04 08 1f 84 04 08 39 06 03
----------- ----------- - - - -
oldinstr newinstr | | | |
| | | +-- pad (4byte alignにするため)
| | |
| | +------ mfence命令のopcodeは 0F AE /6 の3バイト
| |
| +--------- add mem imm8で5バイトlock prefixで+1
|
+ ----------- feature(0x39=57ダミーで入れたよね)


と、なんとなくソレらしい値になっていることが分かる。
特に661bとかのラベル参照がちゃんと、違うアドレスになっていることを確認していただきたい。


つまり、これは

linux/include/asm-i386/system.h の
278 struct alt_instr { 
279 __u8 *instr; /* original instruction */
280 __u8 *replacement;
281 __u8 cpuid; /* cpuid bit set for replacement */
282 __u8 instrlen; /* length of original instruction */
283 __u8 replacementlen; /* length of new instruction, <= instrlen */
284 __u8 pad;
285 };


というデータ構造をメンドクサイ方法で作っているだけだという事。
なんでわざわざ面倒な方法を採用するかというと、C言語で普通に書くと変数には
名前をつけなくてはいけない。
でも、そこらじゅうで使われるマクロだと名前のバッティングを避けるのがえらい面倒なので
アセンブラで無理やり無名データを作っているわけ。


さて、ここらで今までに分かったことを一端まとめると

alternativeマクロは、oldinstr引数だけを実行コードとして生成して、
newinstr引数、feature引数をつかって、.altinstr_replacement と .altinstructions
というなんだかよく分からないデータ構造を作る。

という事が分かった。


エェェ(´д`)ェェエ
newinstr が実行コードに入らないんじゃ意味ないじゃーーん。

と思うかもしれないが、信じて欲しい、最終的にはnewinstrが実行されるように
別の場所で細工しているのだ。

それをこれから解説していく。


まず、 .sectionがマクロ内で使っていた時点で「きっとリンカスクリプト」で何か
細工をしているに違いない。
と、ピンとこなければならない。

だって、他にセクションなんか使う場所ないんだもの。

で、リンカスクリプトを見てみると・・・

linux/arch/i386/kernel/vmlinux.lds.S
86   . = ALIGN(4);
87 __alt_instructions = .;
88 .altinstructions : { *(.altinstructions) }
89 __alt_instructions_end = .;
90 .altinstr_replacement : { *(.altinstr_replacement) }


ありました。
複数のファイルの、色々な場所に散らばっている.altinstructions と.altinstr_replacementを一箇所に集めています。
見方は
出力先のセクション名: { 入力ファイル名(入力ファイル内のセクション)}
で入力ファイルが*(ワイルドカード)だから全てのファイルのセクションを集めるって意味。

そして、.altinstructions セクションの直前と直後に__alt_instructions, __alt_instructions_endという
シンボル(C言語で言うところの変数)をつくり、カレントマーカを代入しています。

これも定石処理(どこの世界の常識だよ!)で、無名のデータを作ったまま何もしないと、
本当に使いようがなくなってしまうので、
リンク時に先頭にシンボルを作って、C言語の別の場所から extern __alt_instructions とかやって
参照できるようにするんですな。

あとは、__alt_instructions を作った以上はどこかでつかっているに違いないと、grepすると

linux/arch/i386/kernel/setup.c
1285 /* Replace instructions with better alternatives for this CPU type.
1286
1287 This runs before SMP is initialized to avoid SMP problems with
1288 self modifying code. This implies that assymetric systems where
1289 APs have less capabilities than the boot processor are not handled.
1290 In this case boot with "noreplacement". */
1291 void apply_alternatives(void *start, void *end)
1292 {
1293 struct alt_instr *a;
1294 int diff, i, k;
1295 unsigned char **noptable = intel_nops;
1296 for (i = 0; noptypes[i].cpuid >= 0; i++) {
1297 if (boot_cpu_has(noptypes[i].cpuid)) {
1298 noptable = noptypes[i].noptable;
1299 break;
1300 }
1301 }
1302 for (a = start; (void *)a < end; a++) {
1303 if (!boot_cpu_has(a->cpuid))
1304 continue;
1305 BUG_ON(a->replacementlen > a->instrlen);
1306 memcpy(a->instr, a->replacement, a->replacementlen);
1307 diff = a->instrlen - a->replacementlen;
1308 /* Pad the rest with nops */
1309 for (i = a->replacementlen; diff > 0; diff -= k, i += k) {
1310 k = diff;
1311 if (k > ASM_NOP_MAX)
1312 k = ASM_NOP_MAX;
1313 memcpy(a->instr + i, noptable[k], k);
1314 }
1315 }
1316 }
1317
1318 static int no_replacement __initdata = 0;
1319
1320 void __init alternative_instructions(void)
1321 {
1322 extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
1323 if (no_replacement)
1324 return;
1325 apply_alternatives(__alt_instructions, __alt_instructions_end);
1326 }
1327
1328 static int __init noreplacement_setup(char *s)
1329 {
1330 no_replacement = 1;
1331 return 0;
1332 }
1333
1334 __setup("noreplacement", noreplacement_setup);


という処理をしているのがわかります。

__initはLinuxカーネル内における __attribute__((constructor)) 指定であり、
カーネル起動時に自動的に呼ばれます。
なんでgcc標準の__attribute__((constructor)) を使わないのかってはなしは面白いので別の機会にでも。

__setup()ってのはカーネル起動時のコマンドライン引数(lilo.confとかgrub.confとかに書くヤツ)
で第一引数の文字列が指定されたら第二引数の関数を呼べというある種のコールバック登録。

話がそれた。


で、見てみると
__alt_instructions をそのまま alt_instr構造体の配列とみなして
(そうできるように、.longやら.byteを駆使してデータ構造を作ったんだよね)

cpuid命令の結果からfeature bitがONになっていれば boot_cpu_has(a->cpuid)
newinstr をoldinstrの位置にmemcpy()して実行コードを直接書き換えてしまう(!)
という荒業を行っています。

これで実行時にif文を使わずにCPUにあわせた最適な処理が実行できるわけ。
alternativeマクロのコメントに記述してあるnewinstr引数はoldinstr引数よりも
生成コードが短くないといけないって制限もこれで納得していただけると思う。

1クロックでも無駄にしないカーネル魂。感じていただけただろうか。

あっと、書き忘れ
memcpy(a->instr + i, noptable[k], k); ってのは残りのバイトをNOPで埋めるんだけども
x86はNOPがいくつか種類があって、たとえば4バイトあまったときに
1バイトのNOP x4 よりも4バイトNOP x1 のほうがCPU にやさしい
(バファリンとカーネルハッカーの半分はやさしさで出来ています)
ので、こういうメンドクさいループを実行している。


どうだろう?
思ったよりも、ややこしかったのではないだろうか?
元を正せばマルチプロセッサ同期命令などという基本的な命令を
P6, Pentium4などという超最近に追加するIntelが悪いのだが、
素直にコンパイルオプションなぞにしてしまったら、ディストリビュータはきっと
Pentium4をOFFにしたGenericカーネルを配布しだして遅くなるし
条件分岐命令なんぞいれた日にゃあ、目も当てられない速度になるのは請け合い。
ってことで、かなり強引な手法がとられている。

普通のLinuxでは実行コードは書き換え不可なので、自己修正コードをつくるのはかなり難儀なのだが、そこはカーネル、なにものにも邪魔されずにハックされてる様は逆にすがすがしくもある
(なにが?)

当然、というか、x86以外ではここまでエエカげんな命令セット拡張はしてないので、
alternativeマクロ自体が存在しない。

どうだまいったか!(だからなにが?)



P.S.
ところで、これを書いている中で些細なバグをまた見つけてしまった。
Intelが発行している
「IA-32 インテル アーキテクチャ・ソフトウェア・デベロッパーズ・マニュアル 下巻: システムプログラミングガイド」の10.6節 自己修正コード の項にはコードを書き換えたらcpuid命令(など)を発行してシリアルか操作を実行してね。
と書いてあるけど、してないよね。
まあ、書き換え直後に実行しない限りprefechは考えなくて良いし、起動時に書き換えてるのでキャッシュにすでに
乗っている可能性は0なので、実害はないんだろうけど、うがが・・


P.S.2
今回は余興もかねて、よく分からないときの調べ方も含めて書こうとしたのだが、幾分中途半端だったかも。
次からは普通に、結論だけ書く記事に戻るかもしれん。
そのほうがこっちもラクだし。


P.S.3
早く発売してくれ



修正してやる
修正してやる! ランキング!
(そして返り討ち)

このエントリーをはてなブックマークに追加

すこし旧聞になってしまうが、すごくおもしろい動画をみつけたのでご紹介。

Ad Innovator様のマイクロソフト版iPodパッケージ開発パロディビデオ、マイクロソフト社内の仕業と認める というエントリを参照していただきたい。

これ、実はマイクロソフトのパッケージ部門直々に作成したMSのパッケージデザインを揶揄する作品なんですねー
ものすごく的確にiPodのデザインが無茶苦茶にされていくさまはいっそ感動を覚えます。

あちこちのBlogでスタッフがキレたと大絶賛!
そら絶賛もするわー

まあ見てくだされ。



そろそろ成仏、する?
逆ギレ! ランキング!

このエントリーをはてなブックマークに追加

最近、SPAM がものすごく多くて閉口していた。
よく考えたらもう10年以上同じメアド。
しかも技術系のMLとかで過去ログをWebに公開されちゃうパターンでがんがん流出してるから、もうトンでもない

で、見つけた記事がこれ「Gmailをスパムフィルタとして活用してみる」

携帯に転送とか考えずに、パソコンで読むだけなら単にGmailアカウントに転送するだけ。
勝手にSpamを振り分けてPOPでダウンロードされないようにしてくれます。
しかも振り分け精度はものすごくGoodです。

しかもしかも
Gmailを経由させることにより、メールのバックアップが自動的に取れてしまいます。
(GmailはPOPでメール取得してもメールを消さない仕様)

SPAM、本当に減りました。
オススメです。

Gmailアカウント持ってない人は言ってくれれば招待いたしますので一声くださいまし




BlackPepperCan
SPAMなんかに負けない! ランキング!

このエントリーをはてなブックマークに追加

今回は小ネタ。
IA64なLinuxで簡単にItanium2のPerformance Monitor機能を使う方法

$ pfmon -eL2_MISSES,L3_MISSES ls
lpgserial projects
9210 L2_MISSES
2397 L3_MISSES


-e オプションの後にモニタしたいイベント名を入れるだけ

イベント名はCPUのマニュアルを見る必要があるが
大体分かっているときは

$ pfmon -lL3_
L2_BYPASS_L3_DATA1
L2_BYPASS_L3_INST1
L2_L3ACCESS_CANCEL_INV_L3_BYP
L2_L3ACCESS_CANCEL_SPEC_L3_BYP
L3_LINES_REPLACED
L3_MISSES
L3_READS_ALL_ALL
L3_READS_ALL_HIT
L3_READS_ALL_MISS
L3_READS_DATA_READ_ALL
L3_READS_DATA_READ_HIT
L3_READS_DATA_READ_MISS
L3_READS_DINST_FETCH_ALL
L3_READS_DINST_FETCH_HIT
L3_READS_DINST_FETCH_MISS
L3_READS_INST_FETCH_ALL
L3_READS_INST_FETCH_HIT
L3_READS_INST_FETCH_MISS
L3_REFERENCES
L3_WRITES_ALL_ALL
L3_WRITES_ALL_HIT
L3_WRITES_ALL_MISS
L3_WRITES_DATA_WRITE_ALL
L3_WRITES_DATA_WRITE_HIT
L3_WRITES_DATA_WRITE_MISS
L3_WRITES_L2_WB_ALL
L3_WRITES_L2_WB_HIT
L3_WRITES_L2_WB_MISS


-l 文字列 でその文字列を含むイベントをリストアップできる。
イベントを度忘れしたときに便利


また、

$ pfmon -iL2_IFET_CANCELS_ANY
Name : L2_IFET_CANCELS_ANY
VCode : 0xa1
Code : 0xa1
PMD/PMC: [ ]
Umask : 0000
EAR : No (N/A)
BTB : No
MaxIncr: 1 (Threshold 0)
Qual : [Instruction Address Range] [OpCode Match] [Data Address Range]
Group : L2 Cache
Set : 0
Desc : Instruction Fetch Cancels by the L2 -- total instruction fetch cancels by L2


-i オプションでそのイベント説明を表示できる
ただし、元のインテルのマニュアルの説明がかなり説明不足なのでDescを読んでも意味不明になることしばしば




追記:
ここまで書いて、はからずもこの記事が
higeponさんの「プログラマの皆さんホームディレクトリで ls してみようよ」エントリに応募資格があることに気づいてしまった。
早速応募。

って、ぜんぜん面白くないlsだこと。。。orz
このエントリーをはてなブックマークに追加

「誰も読まないOSのソース・コード」というエントリのツッコミで極めて面白いものを見つけた

孤高さんのブログより

■ 小飼氏のエントリに細かいツッコミをしてみる。。
(中略)
「赤松ナントカ」と読んで「健?」とか思ってしまった私はラブひなとネギま!ぐらい本質的に同じだ(意味不明)



それ、もはやツッコミでもなんでもないw

というか、こんなに引用してもらえるんだったらバナナはおやつに含まない! ぐらいのネタ条項を仕込んでおくんだった。
反省


追記: 引用元にちゃんとリンク貼れていませんでした。今度はうまくいってるかな


しりにいれない
そんな所にツッコマないで! ランキング!

このエントリーをはてなブックマークに追加

日経BPのサイトに、誰も読まないOSのソース・コードという記事がアップされている。

わりと衝撃的な見出しから始まるのでさくっと引用

 まず,結論から言おう。 「エンジニアがOSのソース・コードを読めるようになると,活躍の場が一気に広がる」。そして,「コツさえ分かれば,OSのソース・コードはびっくりするほど簡単に読める」。



 赤松氏が「ソースを読んでいる人がほとんどいない」と感じる理由はもう1つある。それは,誰もがよく使うソフトでさえバグが大量にある,ということだ。少しでもソースをのぞけば,誰にでもすぐに見つけられるほど簡単なバグなのに,ネットにも情報が上がらず放置されているという。例えば,赤松氏が見つけたバグの具体例を一つだけお見せしよう。




まあ、寝言は寝てから言ってくれといった感じだが、カーネル読めるぐらいで生活は楽にならんっちゅーの。
だいたい、アンタprocfsとネットワークしか読んでないならカーネル屋としては全然駆け出しやんけ。

最近のLinuxでごろごろ変わっているエンタープライズ向けのスケーラビリティー向上パッチに追従できてナンボやで。


閑話休題

ところで、しょーもない不具合が長年放置され続けるってケースはオイラも結構実体験があったりする。
しょーもなさすぎるから、みんなが「ま、いっか」モードになってしまうわけだな。

具体例を一つ。

えーと、UNIXなタイマーってのはとっても貧弱な機能しかAPIとして提供してないので
大規模開発になるとすぐにSIGALRMの取り合いになってしまうという根本的に間違って仕様になっています。

んで、普通はタイマーを管理するサブシステムやらスレッドやらを作るのだが、当時オイラが関わっていた
プロジェクトでは10msの精度が必要だったのでタイマー担当者が以下のようなスレッドを作ったのだな

	while(1){
usleep(10 * 1000);
for(elem=list_head(); elem; elem=elem->next){
elem->remain -= (10 * 1000);
if( elem->remain == 0 ){
/* timer fired */
elem->func( elem->arg );
}
}
}


ま、エラー処理とか大幅に省略してあるけど、雰囲気は伝わると思う。

で、動かしてみるとなぜかタイマーがかっきり2倍近く狂ってしまう。
多少の誤差は覚悟していたが、なんで2倍やねんと。まあ、そういう話になってだな。

ソースを見てみたんよ。


で、出てきたのは以下(当時のソースはもうないから、lxr.linux.no の2.4.28のソースより引用してる)

linux/kernel/timer.c より
836 asmlinkage long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp)
837 {
838 struct timespec t;
839 unsigned long expire;
840
(中略)
860
861 expire = timespec_to_jiffies(&t) + (t.tv_sec || t.tv_nsec);
862



linux/include/linux/time.h より
32 timespec_to_jiffies(struct timespec *value)
33 {
34 unsigned long sec = value->tv_sec;
35 long nsec = value->tv_nsec;
36
37 if (sec >= (MAX_JIFFY_OFFSET / HZ))
38 return MAX_JIFFY_OFFSET;
39 nsec += 1000000000L / HZ - 1;
40 nsec /= 1000000000L / HZ;
41 return HZ * sec + nsec;
42 }


見て分かるように、時間をjiffiesに変換する処理で端数切り上げ
(当時はカーネルタイマが100Hzなので10ms単位)
してるのに、呼び出し側でさらに1足している。

つまり、usleep(0)は本当に0と解釈するけど、usleep(10 * 1000)は内部でusleep(20 * 1000)にされてしまっていたわけ。

うーん、ugly


一瞬パッチを作ろうかとも思ったが、いつものように「それが仕様だ」「usleepは引数以上の時間寝ることを禁止していない」
「今あるアプリが誤動作するかもしれない」etc.. と返ってくる返答が容易に想像できたのでやめた。

んで結局、タイマーサブシステムを書き直して対処しましたとさ。


ま、この程度の「うがーーー、なんだこのuglyな仕様はーーーー!!!」と叫んで初めて一人前って気もする。
Linuxの場合。昔からすんげぇ癖が強いから。



で、ここでさらに話を変えて
この話に言及していたブログでこんなのを見つけました。

でいずおぶぱーがとりー

「LinuxのOSコードを読めるようになれば、イロイロわかるよ~。コツさえ掴めばなんとかなるよ。」
ッて話。
にしても、Cを知らない俺には敷居高いと思う。だから、とりあえず関連有る連載が始まった日経Linuxでも立ち読みしてみるかな。




おほほほ。
C言語なんてカーネルを読んで勉強するものですわよ。

実際オイラは、情報系の学校とか出てないのでC言語はK&R本と、日本橋で買ってきたFreeBSD2.2 のCDのソースコードで
覚えましたわよ。

といっても、カーネルをいきなり理解できるはずもなく、半分以上理解できなかったが、データ構造の作り方の定石とかが
いきなり学べたのは本当にうれしかった。
たぶん、僕はカーネルを読んでC言語を覚えた最後の世代だと思う。同年代でそういう話あんまり聞かないし。

さらに言うとUNIXカーネルの動きがちゃんと理解できたと確信できたのは、

Haskellを勉強するためにPugsのソースを読むというのは、Cの勉強をするためにUnixのカーネルの読むようなものだ。いいねいいね、この主客転倒ぶり。*1

*1:Lionsの本(Lions’ Commentary on UNIX)はCのいい教科書でもある。 Cでどのように表現するかという勉強になるしね。



と、未来のいつかの日記でもオススメされている
Lions本を読破した後だったりする。
まじでいい本です。

僕はLinux 0.9 とかを読むより、こっちをすすめるな。
x86はカーネルの本筋じゃないところでやたら複雑だから、途中で心が折れる気がする。
はじめの一歩にぶん殴られたかのような衝撃が襲うものな。 > Linuxのブートまわり



どうも、話が脱線してしまった。
結局いいたかったのは

・Linuxはそんなにバグバグじゃないよ。コードの質たかいよ
・でもuglyな仕様は、すごいたくさんあるよ。そこはuglyさ加減を楽しもうよ
・C言語できなくても気にすることないよ。カーネル読んでいけばそのうち覚わるから
・赤松ナントカはシッタカ・うそつき

って事ぐらいか


そして、まとまらないままに、了


追記:
上であげたusleepの問題は2.6系列では問題ない。
なぜならjiffiesが1024になったときに問題自体が自然消滅してるから。
1ms単位の精度だと、どうせ元から全然保障できてないので
(精度が細かすぎてちょっと割り込み入っただけで崩れまくる)
1msずれるぐらい、気にならない。というか気にしてられない
このエントリーをはてなブックマークに追加

前回紹介した友達漫画だが、ひとつ面白い特徴がある。
どうもゲームの原作者が この漫画家のファンだったらしく 原作者によるやたら大量の解説文がついている。

量が多すぎて巻末に収まりきらなくなったらしく、各話が終わるたびに濃ゆいヲタ解説文が始まるので漫画を楽しむ本か解説を楽しむ本か微妙な位置づけになっている有様。



さすがご自身が日記

 3月25日発売予定の磨伸映一郎作品集「月の彼方、永遠の眼鏡」にバカみたいな量の寄稿文を送りつけました。でもあれぐらいやらないと磨伸映一郎の波に呑まれちゃうんだよ! 磨伸氏のアンソロ漫画は、ほら、アレだ、ニトロ(薬)みたいなもんだから。我こそ重度のオタクなり、なんて自覚のある方は是非読んでみてほしい……!



と豪語するだけの事はありますな。



友人のmixi日記を読むと

ちなみにもう
http://www.typemoon.org/bbb/diary/
で御本人が語ってるのでバレるも何もないんですが、
解説は奈須きのこさんが立候補して下さいました!
ありがたい事ですヨ!!
何気に「月姫」「メルブラ」の製作裏話も満載で
読んでるこっちが嬉しくなりますヨ!



つーかメルブラのシナリオが●●●シナリオノーマルED後という
設定だとか「次のメルブラの追加ヒロインが凄い」とか
極秘情報満載気味なので読んでるコッチが『いいのか!?』って
感じですよ?




どうも原作者が自分から立候補してきたらしい。ほほー





で、これを見ていてふと思い出したのが結城ブログのこの文章

長男「本を読む楽しさっていうのは、別の世界に行くおもしろさだね」
私「なになに、何だかすごいね。まさにその通りだと思う。別世界に行くことがファンタジーの本質だからね」
長男「たとえばナルニアを読む人はナルニアという世界に行く」
私「そうだね。ところで、そこに多重構造があるのは知っている?」
長男「どゆこと?」
私「登場人物と読んでいる私たちのことを考えてみると…」
長男「わかった! こうだね。中に入って、さらに中に入る」




パロディ漫画ってのは、原作への言及とあたらしい表現がキモなわけで、
  原作ゲームを言及するパロディ漫画
⇒パロディ漫画を言及する原作者による解説文
⇒原作者の解説文に言及する漫画家の日記
⇒漫画家の日記に言及するただの友達(おれ)
⇒ただの友達のブログを読んでいる読者(あなた)


と、かなり何十にも多重構造のテクストを構成している。うーん文学的に構造を理解しようとすると極めて興味深い。




それはさておき、こんだけ大量のヲタ解説文がありながらも、まだ半分以上のネタが未解説ってどういう事ですか?!
てか、漫画でほぼ全ての台詞にネタが仕込んであるって正気か?!
ちょ。。それどこのパズルゲーム(ry


あー、映画とかアニメとかゲームとかのネタに素養がある人はぜひドウゾ(天使のような邪悪な笑みをうかべつつ)


まったく近頃の右翼は!
右翼だってヲタクに走る時代なんですから! ランキング!

このエントリーをはてなブックマークに追加

私事になりますが、わたくし最近、「汝は人狼なりや?」と小一時間問い詰めたい。というチャットベースの人間vs人間で楽しむ推理ゲームを毎週遊んでいます。

山奥の山荘で連続殺人犯をくびり殺さないと村人は死ぬ。しかし証拠は0なので、基本的に相手の揚げ足をとるしか推理のしようがない。いきおい無罪の人が次々死んでいく。
村人は全滅するまえに犯人を見つけることができるか!

まあそんなゲームです。

新規参加者はいつでも大募集中ですので興味のあるかたはぜひどうぞ。

ルールがよく分からない方への解説はこちら↓

①「接触編」<参加する為に知る事>
http://mixi.jp/view_diary.pl?id=47488594&owner_id=104959
②「発動編」<実際に遊ぶ為に知る事>
http://mixi.jp/view_diary.pl?id=47497682&owner_id=104959
③「鳳凰編」<知ってて得する応用編>
http://mixi.jp/view_diary.pl?id=47508081&owner_id=104959


mixi IDを持ってない人は管理人のみが見えるモードコメントでメアド教えてくれれば招待いたしますよん。
注: 招待にはメールアドレスが必要です。プライバシーとか気になる人は、どこかでフリーメールアドレスか何かを作ってくださいませ

ま、それはさておき。


それでですね。その参加者の一人がどうも漫画家さんであることが最近発覚いたしましてですね。
初単行本を出したというので買ってみたわけですよ。友達として




マイミク登録を拒否られるぐらい薄い友情ですが!
友達として!


これが予想外(失礼)にめちゃくちゃ面白い!

ゲームのパロディ漫画になっていて、そういう系統の漫画をあつめたアンソロジー作品集から一人の作品だけをピックアップした総集編という体裁なのだが。

なんというか、超えちゃってるね。
原作をあまり理解していない僕がいうのもなんだが、これ、パロディである必要ないよ。

主人公がコタツにずーーーと籠もっているまま話終了。完。とか
ヒロインがずーーと公衆TOTOに籠もっているまま話終了。完。とか

とくに2つ目は原作付きマンガとしてはどうなのよ?(゚Д゚;≡;゚Д゚)



さすが、本人が表紙にて

あふれ出す眼鏡愛が止まらない、超問題作品集、ここに



と豪語するだけのことはあるわ。


負けるときになぜか3人乗り自転車で逃げていくヒロイン
(背景に「おしおきだべー」の掛け声付き)

とか

最終的にはPTAと全面戦争する気か、このハレンチな学園めー!!

とか

やばいネタ満載。
てか一番笑ったのは後書きのこの文章

「お、おおお・・・」
ご本人からのメールに添付された文章を読了し、思わず心からの声が漏れた (中略)
し、「しみるぅ」に対して「ホルヘ・バカ」を返せる人なんて何年ぶりだろう・・・




何年もの間、誰にも通じなかったネタを商業誌で炸裂させないでいただきたい


アナタに複線ドリフトーーーー!!! と叫んでしまった某執事漫画 in サンデーを笑う資格はない。


ps 最終的には666の数字とともに復活した第六天魔王信長を自らの命とひきかえに聖剣エクスカリバーで封印して王子の涙で命を取り戻す映画とくしゃみで主人公が爆死する映画の正体をだれか教えて!


ps2 いまぐぐった所、作品のまっとうなレビューとしては以下のURLがオススメのようです。

レビログ::月の彼方、永遠の眼鏡 TYPE-MOON作品集 奈須きのこ解説付き




複線ドリフト
ドリフトしないと曲がれない線路ってどうなのよ?! ランキング!

このエントリーをはてなブックマークに追加

↑このページのトップヘ