永遠まで鳴り響く 時の鐘を止めて

この投稿は NetBSD Advent Calendar 2017 - Qiita 12月16日の内容になります。

『青い精霊達よ… その力をもって 永遠まで鳴り響く 時の鐘を止めて...』
ゲームアーツの超名作RPG"グランディア"のヒロイン、フィーナが覚える"時の門"という魔法の発動時のセリフです。
中学生だった当時はセガサターンを持っていなかったのでプレイを諦めていたのですが、遅れてPS版が発売されたので速攻購入して猿のようにやり込んだ思い出があります。
フィーナ専用魔法の"時の門"は"自分以外の時間を止めるという中二病をくすぐりまくる効果"を発揮しますが、習得がとても大変で、2徹ぐらいしてようやく覚えさせたような記憶が...

さて、NetBSDで時の門を発動させてみましょう!

方法はbintime_add()を下記のようにコメントしてカーネルコンパイルすれば大丈夫です!
https://nxr.netbsd.org/xref/src/sys/sys/time.h#118

static __inline void
bintime_add(struct bintime *bt, const struct bintime *bt2)
{
#if 0
	uint64_t u;

	u = bt->frac;
	bt->frac += bt2->frac;
	if (u > bt->frac)
		bt->sec++;
	bt->sec += bt2->sec;
#endif
}


もしくは、bintime_add()の上に定義してあるbintime_addx()をbintime_add()のようにコメントした後に
https://nxr.netbsd.org/xref/src/sys/sys/time.h#107

static __inline void
bintime_addx(struct bintime *bt, uint64_t x)
{
#if 0
	uint64_t u;

	u = bt->frac;
	bt->frac += x;
	if (u > bt->frac)
		bt->sec++;
#endif
}

カーネルの構成ファイルからオーディオ関係のドライバを外して、カーネルコンパイルしてやると、同じように時が止まった世界を味わうことができます。
https://nxr.netbsd.org/xref/src/sys/arch/i386/conf/GENERIC#1397


では、bintime_addx()がカーネル内のどこで使われているのか?

いろいろ使われていますが一番大事なところはtc_windup()だと思います。(違っていたらすいません...)
https://nxr.netbsd.org/xref/src/sys/kern/kern_tc.c#751

では、今度はbintime_addx()やbintime_add()のコメントを戻してtc_windup()の中身のdeltaをいじって0を代入してみましょう。

	/*
	 * Capture a timecounter delta on the current timecounter and if
	 * changing timecounters, a counter value from the new timecounter.
	 * Update the offset fields accordingly.
	 */
#if 0
	delta = tc_delta(th);
#endif
	delta = 0;
	if (th->th_counter != timecounter)
		ncount = timecounter->tc_get_timecount(timecounter);
	else
		ncount = 0;
	th->th_offset_count += delta;
	bintime_addx(&th->th_offset, th->th_scale * delta);

systemが0:-1.95だと... いろいろおかしいようです...
う〜ん、今度は"刻が未来にすすむと 誰がきめたんだ〜"と∀ガンダムの世界になってしまいました...
deltaって何でしょう?


delta?マクロス?

0を代入していじってみたdeltaですが、tc_windup()ではtc_delta()を使用して値を算出していました。
https://nxr.netbsd.org/xref/src/sys/kern/kern_tc.c#303

static inline u_int
tc_delta(struct timehands *th)
{
	struct timecounter *tc;

	tc = th->th_counter;
	return ((tc->tc_get_timecount(tc) -
		 th->th_offset_count) & tc->tc_counter_mask);
}

tc_get_timecount?th_offset_count?tc_counter_mask?
やはり、変数の意味がわからないとツライものがあります... そこでtimecounter構造体を見てみましょう!
https://nxr.netbsd.org/xref/src/sys/sys/timetc.h#46

struct timecounter {
	timecounter_get_t	*tc_get_timecount;
		/*
		 * This function reads the counter.  It is not required to
		 * mask any unimplemented bits out, as long as they are
		 * constant.
		 */

tc->tc_get_timecountは関数ポインタのようです... 何が入っているのかな? 検索してみます。
https://nxr.netbsd.org/s?n=25&start=0&q=tc_get_timecount&sort=relevancy&project=src,

どうやら、 /src/sys/arch/powerpc/や/src/sys/arch/sparc/sparc/、/src/sys/arch/arm/のようにマシン依存(Machine Dependent: MD)のところの関数が入っているようですね...

一般的に使われているx86なマシンだとtsc_get_timecount()が代入されているようです。
(HPET?(∩゚д゚)アーアーきこえなーい)
https://nxr.netbsd.org/xref/src/sys/arch/x86/x86/tsc.c#62

tsc_get_timecount()の定義を見てみると...
https://nxr.netbsd.org/xref/src/sys/arch/i386/i386/i386func.S#245

ENTRY(tsc_get_timecount)
	movl	CPUVAR(CURLWP), %ecx
1:
	pushl	L_NCSW(%ecx)
	rdtsc
	addl	CPUVAR(CC_SKEW), %eax
	popl	%edx
	cmpl	%edx, L_NCSW(%ecx)
	jne	2f
	ret
2:
	jmp	1b
END(tsc_get_timecount)

とrdtscというCPUの命令を発行しているようです... rdtscとは?調べてみると...
__rdtsc
rdtsc ‐ 通信用語の基礎知識
TSC ‐ 通信用語の基礎知識

どうやらCPUの中にあるカウンタ(タイマー)の値を読み出す命令のようですね!

では、次のtc->tc_counter_maskは何が代入されているのかというと、~0Uなので、無視して良いと思います。
https://nxr.netbsd.org/xref/src/sys/arch/x86/x86/tsc.c#64

最後のth->th_offset_countには何が入っているのか?というと、もう一度、一度tc_windup()を見てみましょう...

	/*
	 * Capture a timecounter delta on the current timecounter and if
	 * changing timecounters, a counter value from the new timecounter.
	 * Update the offset fields accordingly.
	 */
	delta = tc_delta(th);
	if (th->th_counter != timecounter)
		ncount = timecounter->tc_get_timecount(timecounter);
	else
		ncount = 0;
	th->th_offset_count += delta;
	bintime_addx(&th->th_offset, th->th_scale * delta);

th_offset_countには今調べているtc_delta()の戻り値が入っています。
つまり、tc_delta()の動きは、1回目のth_offset_countが0だとすると...

1回目:th_offset_count = 0 + (現在のCPUのカウンタ - 0)
2回目:th_offset_count = 1回目のth_offset_countの値 + (現在のCPUのカウンタ - 1回目の時点でのCPUのカウンタ)
3回目:th_offset_count = 2回目のth_offset_countの値 + (現在のCPUのカウンタ - 2回目の時点でのCPUのカウンタ)
...

のようなイメージになるんじゃないかなぁ...と思います。
また、コメントにも(offset at last time update (tc_windup())と書いてあります。
https://nxr.netbsd.org/xref/src/sys/kern/kern_tc.c#102

では、deltaの意味がわかってきたので、次はdeltaを掛けているth_scaleを調べてみたいともいます。

	/*
	 * Capture a timecounter delta on the current timecounter and if
	 * changing timecounters, a counter value from the new timecounter.
	 * Update the offset fields accordingly.
	 */
	delta = tc_delta(th);
	if (th->th_counter != timecounter)
		ncount = timecounter->tc_get_timecount(timecounter);
	else
		ncount = 0;
	th->th_offset_count += delta;
	bintime_addx(&th->th_offset, th->th_scale * delta);
scale?ソードアート・オンライン オーディナル・スケール?

実はtc_windup()やtc_get_timecount()の関数名の頭についているtcには意味があります。
tcはtimecounterの略なのです。
NetBSDOpenBSD(FreeBSDも?)はカーネル内でtimecounterの仕組みを使って時刻の管理をしています。
timecounter.9
論文はこちら
http://phk.freebsd.dk/pubs/timecounter.pdf


そして、timecounterのscaleにはお約束があります。

標準の刻み=1秒あたり 2^64 カウントする←これ重要

th_scaleの算出方法もtc_windup()に書いてあります。
https://nxr.netbsd.org/xref/src/sys/kern/kern_tc.c#833

	/*-
	 * Recalculate the scaling factor.  We want the number of 1/2^64
	 * fractions of a second per period of the hardware counter, taking
	 * into account the th_adjustment factor which the NTP PLL/adjtime(2)
	 * processing provides us with.
	 *
	 * The th_adjustment is nanoseconds per second with 32 bit binary
	 * fraction and we want 64 bit binary fraction of second:
	 *
	 *	 x = a * 2^32 / 10^9 = a * 4.294967296
	 *
	 * The range of th_adjustment is +/- 5000PPM so inside a 64bit int
	 * we can only multiply by about 850 without overflowing, but that
	 * leaves suitably precise fractions for multiply before divide.
	 *
	 * Divide before multiply with a fraction of 2199/512 results in a
	 * systematic undercompensation of 10PPM of th_adjustment.  On a
	 * 5000PPM adjustment this is a 0.05PPM error.  This is acceptable.
 	 *
	 * We happily sacrifice the lowest of the 64 bits of our result
	 * to the goddess of code clarity.
	 *
	 */
	if (s_update) {
		scale = (u_int64_t)1 << 63;
		scale += (th->th_adjustment / 1024) * 2199;
		scale /= th->th_counter->tc_frequency;
		th->th_scale = scale * 2;
	}

th->th_adjustmentは0が代入されていると考えて無視してください。
なぜ、scale = (u_int64_t)1 << 64ではなく、scale = (u_int64_t)1 << 63なのか、自分では理解できませんでしたが、最終的にth->th_scale = scale * 2で辻褄合わせをしているので問題は無いと思います。

th->th_counter->tc_frequencyにはCPUのクロックが代入されています。
(正確にはCPUのクロックではなく、タイマーのクロックが代入されていますが、今回はCPUのクロックをタイマーとします)

そして、scale /= th->th_counter->tc_frequencyで1クロックの間にどれだけカウントが進んでいるか → scaleを求めています。
"scale = 2^64 / CPUの周波数"という感じなのかな?と思います。

ああすっきりした。
これで、bintime_addx()やbintime_add()、tc_deltaをいじると、時間が止まったように見えるという理由がわかったと思います。
時刻(クロック)が加算されないため、なんですよね。

clock?クロックワーク・プラネット?

さて、このtc_windup()がどこから呼び出されているかというと...
これまた色々なところから呼ばれているんですが、一番重要なところはtc_ticktock()から呼ばれているところだと思います。
(違っていたらすいません...)
https://nxr.netbsd.org/xref/src/sys/kern/kern_tc.c#1319

さらにさらに、tc_ticktock()がどこから呼ばれているか?というとhardclock()から呼ばれています。
https://nxr.netbsd.org/xref/src/sys/kern/kern_clock.c#246

hardclock()とはナンジャラホイ?というかというと、クロック割込みハンドラから呼ばれる処理のようです。
https://nxr.netbsd.org/xref/src/sys/arch/x86/x86/lapic.c#564

void
lapic_clockintr(void *arg, struct intrframe *frame)
{
	struct cpu_info *ci = curcpu();

	ci->ci_lapic_counter += lapic_tval;
	ci->ci_isources[LIR_TIMER]->is_evcnt.ev_count++;
	hardclock((struct clockframe *)frame);
}

では、クロック割り込みとはナンジャラホイ?というかというと、タイマーをセットすることによって発生する一秒の間に定期的に起こる割り込みの事を言います。

このタイミングはhzというカーネルの構成ファイルで制御することが可能で、hzの数字を大きくすればそれだけクロック割り込みの発生頻度が高くなります。
https://www.daemon-systems.org/man/hz.9.html

そして、クロック割り込みが発生した時に処理される関数がクロック割込みハンドラになります。

たとえば下記はmc146818という時計にクロック割り込みの頻度(hz)を設定している処理です。
https://nxr.netbsd.org/xref/src/sys/dev/dec/mcclock.c#87

hzの数字が大きければ大きいほど良いのか?というと、大きければそれだけ割込みの頻度があがってシステムがおもくなってしまうので、CPUが遅いマシンはhzの数字が小さい場合が多いようです。

LUNA-Iはhz(一秒の間のクロック割り込みの回数)が60回
https://nxr.netbsd.org/xref/src/sys/arch/luna68k/luna68k/clock.c#73

Alphaはhz(一秒の間のクロック割り込みの回数)が1024回
https://nxr.netbsd.org/xref/src/sys/arch/alpha/conf/std.alpha#9

どうやらLUNA-IはCPUがMC68030で20Mhzととても遅いのでクロック割込みの頻度が少な目のようです...

じゃぁCPUが速いマシンだったらhzの数字を大きくしても大丈夫なのか?というと、NetBSDではハードウェアがからむ時刻関係の処理は最高でも1秒間に1000回を想定しているらしく、
仮にhzを5000に設定してhardclock()を一秒間に5000回呼び出したとしても、inittimecounter()でtc_tickを計算して、tc_ticktock()でtc_windup()が呼び出される回数を制御してるようです。
https://nxr.netbsd.org/xref/src/sys/kern/kern_tc.c#1304

/*
 * Timecounters need to be updated every so often to prevent the hardware
 * counter from overflowing.  Updating also recalculates the cached values
 * used by the get*() family of functions, so their precision depends on
 * the update frequency.
 */

static int tc_tick;

void
tc_ticktock(void)
{
	static int count;

	if (++count < tc_tick)
		return;
	count = 0;
	mutex_spin_enter(&timecounter_lock);
...
	tc_windup();
	mutex_spin_exit(&timecounter_lock);
}

void
inittimecounter(void)
{
	u_int p;

	mutex_init(&timecounter_lock, MUTEX_DEFAULT, IPL_HIGH);

	/*
	 * Set the initial timeout to
	 * max(1, ).
	 * People should probably not use the sysctl to set the timeout
	 * to smaller than its inital value, since that value is the
	 * smallest reasonable one.  If they want better timestamps they
	 * should use the non-"get"* functions.
	 */
	if (hz > 1000)
		tc_tick = (hz + 500) / 1000;
	else
		tc_tick = 1;
...
}

これでようやくNetBSDのtimecounter(9)の処理の概要が掴めたかと思います。

  1. タイマ割り込みが一秒間にhz回発生する
  2. hardclock()が呼ばれる
  3. tc_ticktock()が呼ばれる
  4. tc_windup()が呼ばれる
  5. CPUのクロック(タイマーのクロック)を読んで"標準の刻み=1秒あたり 2^64 カウント"を超えていたら1秒加算する


という感じかなと思います!

それでは後編に続く!

Tru64 UNIXで遊ぼう!(inline編)

Tru64 UNIXでlibarchiveをコンパイル出来るように試行錯誤していたところ、面白いものを見つけました。

github.com

どうやら、Tru64 UNIXの場合は関数指定子inlineを__inlineと定義しているようです。

どういうことなの?

せっかくなのでアセンブラを出力させてみました。

まずは普通のinline版

$ more inline.c

static inline int
max(int i1, int i2) {
        return (i1 > i2) ? i1:i2;
}
 
int
main(int argc, char *argv[])
{
        return max(1,5);
}

コンパイラCompaq C Compilerです。

$ cc -V
Compaq C V6.5-303 (dtk) on HP Tru64 UNIX V5.1B (Rev. 2650)
Compiler Driver V6.5-302 (dtk) cc Driver
$ cc -S inline.c
                                                                                                                                                                                                    
$ more inline.s                                                                                                                                                                                                     
	.set noat
	.set noreorder
	.text
	.arch	generic
	.align 4
	.file 1 "inline.c"
	.loc 1 1
 #      1 static inline int
	.ent 	$$1$max
	.loc 1 1
	.loc 1 2
 #      2 max(int i1, int i2) {
$$1$max:														   # 000002
	.frame  $sp, 0, $26
	.prologue 0
	.context full
	sextl	$17, $17
	sextl	$16, $0
	.loc 1 3
 #      3         return (i1 > i2) ? i1:i2;
	cmplt	$0, $17, $1												   # 000003
	cmovne	$1, $17, $0
	.loc 1 4
 #      4 }
	ret	($26)													   # 000004
	.end 	$$1$max
	unop
	unop
	unop
	.loc 1 1
	.loc 1 6
 #      5  
 #      6 int
	.globl  main
	.ent 	main
	.loc 1 6
	.loc 1 7
 #      7 main(int argc, char *argv[])
main:															   # 000007
	.frame  $sp, 0, $26
	.prologue 0
	.loc 1 9
 #      8 {
 #      9         return max(1,5);
	.context full
	mov	5, $17													   # 000009
	mov	1, $16
	br	$$1$max
	.end 	main
	.loc 1 6

おおう、普通にmaxを呼び出してますね...

では待望の__inline版

$ more __inline.c

static __inline int
max(int i1, int i2) {
        return (i1 > i2) ? i1:i2;
}
 
int
main(int argc, char *argv[])
{
        return max(1,5);
}
$ cc -S __inline.c
                                                                                                                                                                                                  
$ more __inline.s                                                                                                                                                                                                   
	.set noat
	.set noreorder
	.text
	.arch	generic
	.align 4
	.file 1 "__inline.c"
	.loc 1 6
 #      6 int
	.globl  main
	.ent 	main
	.loc 1 6
	.loc 1 7
 #      7 main(int argc, char *argv[])
main:															   # 000007
	.frame  $sp, 0, $26
	.prologue 0
	.loc 1 3
	.context full
	mov	5, $0													   # 000003
	.loc 1 10
 #      8 {
 #      9         return max(1,5);
 #     10 }
	ret	($26)													   # 000010
	.end 	main
	.loc 1 6

おーっ!インライン展開されています!

というわけで、Compaq C Compilerの場合、関数指定子inlineを__inlineと定義すれば、最適化をかけなくてもインライン展開されるということがわかりました〜
(もちろん普通?のinlineの場合でも最適化をかけるとインライン展開される)

ちなみにコンパイラgccを使った場合は、関数指定子inline、__inlineどちらを使っても結果は変わりませんでした〜
(もちろん最適化をかけると、どちらもインライン展開される)

$ gcc -v 
Using built-in specs.
Target: alphaev68-dec-osf5.1b
Configured with: ./configure --enable-languages=c,c++ --enable-threads=posix --disable-nls --without-gnu-ld --with-ld=/usr/ccs/bin/ld --without-gnu-as --with-as=/usr/bin/as --disable-libssp
Thread model: posix
gcc version 4.4.7 (GCC) 
$ /usr/local/bin/gcc -S __inline.c
  
$ more __inline.s                                                                                                                                                                                                   
	.file	1 "__inline.c"
	.verstamp 3 11
	.set noreorder
	.set volatile
	.set noat
	.arch ev6
	.text
	.align 2
	.ent max
$max..ng:
max:
	.frame $15,32,$26,0
	.mask 0x4008000,-32
	lda $30,-32($30)
	stq $26,0($30)
	stq $15,8($30)
	bis $31,$30,$15
	.prologue 0
	bis $31,$16,$2
	bis $31,$17,$1
	stl $2,16($15)
	stl $1,20($15)
	ldl $2,20($15)
	ldl $1,16($15)
	cmple $2,$1,$3
	cmoveq $3,$2,$1
	addl $31,$1,$1
	bis $31,$1,$0
	bis $31,$15,$30
	ldq $26,0($30)
	ldq $15,8($30)
	lda $30,32($30)
	ret $31,($26),1
	.end max
	.align 2
	.globl main
	.ent main
main:
	.frame $15,32,$26,0
	.mask 0x4008000,-32
	ldgp $29,0($27)
$main..ng:
	lda $30,-32($30)
	stq $26,0($30)
	stq $15,8($30)
	bis $31,$30,$15
	.prologue 1
	bis $31,$16,$1
	stq $17,24($15)
	stl $1,16($15)
	lda $16,1($31)
	lda $17,5($31)
	bsr $26,$max..ng
	bis $31,$0,$1
	bis $31,$1,$0
	bis $31,$15,$30
	ldq $26,0($30)
	ldq $15,8($30)
	lda $30,32($30)
	ret $31,($26),1
	.end main

うーむ、libarchiveにこの修正を突っ込んだ人はどこで発見したんですかね?
有名な話なのかな?

Linux、隣は何をするひとぞ

TwitterのTLでよく見かける"どんかん"というキーワード

よく見てみると、発言しているのはlinux kernel developerの人が多いようです。

彼らは何を考え、何を行っているのでしょうか?今晩はその生態を探って見たいと思います...

と、意味深な書き出しをしましたが、きっかけはTLに流れてきたedvakfさんの

というツイートでした。

自分の観測する範囲だとBSDの人は結構な割合で備忘録を残している気がします。

↓BeBOX!
NetBSD/bebox porting notes

↓つつい師匠3連発
The far way to the facilitated eventual NetBSD/news68k port

togetter.com

togetter.com

↓しゅううさんは自分の中ではMIPSの偉い人なので
syuu1228.hatenablog.com

もちろん自分も可能な限り備忘録を残してきました。

togetter.com

nullnilaki.hatenablog.com

nullnilaki.hatenablog.com

自分がこのような記録を残しておく理由は
1.あとでネタにする
2.他人が調べものに来たときに参考になるように
3.人が試行錯誤の結果、失敗したり成功したりする内容は読み物として単純に面白い

からです。

ですが、このようなパッチ投稿の記録などLinuxではあまり見たことがなかったので、まずはLinux界隈でのお作法についてUNIX警察のn_sodaさんに聞いてみました。

そうすると、親切にもsatoru_takeuchiさんが教えてくださいました。
satoru_takeuchiさん、改めましてありがとうございます。

う〜ん、残念ながらLinuxのパッチ投稿記みたいなものは無いみたいです。
みなさんお仕事でカーネル書いているから、必要無いのかもしれませんね...
それとも文化が違うのかしら?

n_sodaさんがLinuxのパッチの書き方の資料を見つけてくださったので、
自分がLinuxにパッチを投げる時が来たら、備忘録を付けつつ頑張ってみたいと思いました!

Tru64 UNIXで遊ぼう!(C99編)

Tru64 UNIXでpkgsrcを動かそうと必死こいて作業していると、面白いエラーに遭遇しました。


libverify.c: In function 'fmt_time':
libverify.c:678: warning: incompatible implicit declaration of built-in function 'llabs'
libverify.c: In function 'read_ssh_file':
libverify.c:2354: error: expected ')' before 'PRIi64'
*** Error code 1
gcc -lssl -lcrypto -L/root/pkgsrc/bootstrap/work/wrk/pkgtools/pkg_install/work/bzip2 -L/root/pkgsrc/bootstrap/work/wrk/pkgtools/pkg_install/work/zlib -L/root/pkgsrc/bootstrap/work/wrk/pkgtools/pkg_install/work/libarchive/.libs -L/root/pkgsrc/bootstrap/work/wrk/pkgtools/pkg_install/work/libfetch -L/root/pkgsrc/bootstrap/work/wrk/pkgtools/pkg_install/work/netpgpverify -L/root/pkgsrc/bootstrap/work/wrk/pkgtools/pkg_install/work/libnbcompat -L/usr/lib -Wl,-R/usr/lib -Wl,-R/root/pkgsrc/bootstrap/pkg/lib -L../lib -o pkg_add main.o perform.o -linstall -lnetpgpverify -larchive -lbz2 -lz -lfetch -lssl -lcrypto -lcrypto -lbz2 -lz -lnbcompat -lnbcompat
strtoull
collect2: ld returned 1 exit status
*** [pkg_add] Error code 1

あれ? Tru64 UNIXCompaq CはC99に対応しているはず...

下記マニュアルにも
Compaq C Version 6.4 provides most of the language-feature support for the new C99 standard
と書いてあります

Tru64 UNIX

しかし、リンカがエラーを吐いているという事は、ライブラリがstrtoullをサポートしていないとしか考えられない?

というわけでCompaq Cのリリースノートを見てみました!
すると衝撃的な事が...

    6.1 New C99 library functions
       
	The Compac C compiler kit ships system header files that declare new
	ANSI/ISO C99 library names such as round, roundf, and roundl (math.h).

	The C99 header files install into /usr/include.dtk.  This directory is
	added to the compiler's #include search path automatically by the cc
	driver so that the header files will be read during "normal" compiles.

	The header files declare ALL of the new C99 names even though many of
	those functions do not yet exist in the libraries of shipping versions
	of Tru64 UNIX.  The names are reserved by the C99 Standard and the
	library functions will become available in future versions of the OS.

工エエエエエエェェェェェェ(゚Д゚)ェェェェェェエエエエエエ工工工

ヘッダファイルはC99に対応したけど、ライブラリは対応してないだと...

C99用の/usr/include.dtk/inttypes.hを見ると

$ more /usr/include.dtk/inttypes.h                                                                                                                                                             
/*
 * *****************************************************************
 * *                                                               *
 * *    Copyright 2001 Compaq Computer Corporation                 *
 * *                                                               *
...
/* begin - fprintf macros for signed integers */
#define PRId8		"d"
#define PRId16		"d"
#define PRId32		"d"
#define PRId64		"ld"
...

なので

#include <stdio.h>
#include <inttypes.h>
#include <stdlib.h>
 
int
main(void)
{
  int64_t data = 12345;
 
  printf("data=%"PRId64"\n",data);
  return EXIT_SUCCESS;
  
}

gccは対応していない...
(/usr/include.dtkを見に行かないので...)

$ /usr/local/bin/gcc meso.c                                                                                                                                                                    
meso.c: In function 'main':
meso.c:10: error: expected ')' before 'PRId64'

Compaq Cは対応している
(/usr/include.dtkを見に行く)

$ cc meso.c
$ ./a.out                                                                                                                                                                                      
data=12345

または、C99用の/usr/include.dtk/stdlib.hを見ると

$ more /usr/include.dtk/stdlib.h                                                                                                                                                               
/*
 * ********************************************************************************
 * *                                                                              *
 * *    Copyright 2002 Compaq Information Technologies Group, L.P.                *
...
    extern unsigned long long int strtoull(
    		const char * /*restrict*/ __nptr,
    		char ** /*restrict*/ __endptr,
    		int __base);
....

なので

#include <stdio.h>
#include <stdlib.h>

#define BASE 0

int
main(void)
{
   char *string, *stopstring;
   unsigned long long x;

   string = "255";
   printf("string = %s\n", string);
   x = strtoull(string, &stopstring, BASE);
   printf("strtoull = %llu \n", x);
   return EXIT_SUCCESS;
}

gccもCompaq Cもリンカがエラーを出力

$ /usr/local/bin/gcc foo.c                                                                                                                                                                     
strtoll
collect2: ld returned 1 exit status

$ cc foo.c                  
ld:
Unresolved:
strtoll

2006年リリースのTru64 UNIX 5.1B-4でC99に対応していない状態で
2010年リリースのTru64 UNIX 5.1B-6が最終リリースなので
Tru64 UNIXはC99に対応していない、がファイナルアンサーという事なんでしょうかねぇ?
教えて、教えてエロイ人

     ____________
    ヾミ || || || || || || || ,l,,l,,l 川〃彡|
     V~~''-山┴''''""~   ヾニニ彡|       C99に対応したライブラリを出す・・・・・・!
     / 二ー―''二      ヾニニ┤       出すが・・・
    <'-.,   ̄ ̄     _,,,..-‐、 〉ニニ|       今回 まだ その時と場所の
   /"''-ニ,‐l   l`__ニ-‐'''""` /ニ二|       指定まではしていない
   | ===、!  `=====、  l =lべ=|
.   | `ー゚‐'/   `ー‐゚―'   l.=lへ|~|       そのことを
    |`ー‐/    `ー――  H<,〉|=|       どうか諸君らも
    |  /    、          l|__ノー|       思い出していただきたい
.   | /`ー ~ ′   \   .|ヾ.ニ|ヽ
    |l 下王l王l王l王lヲ|   | ヾ_,| \     つまり・・・・
.     |    ≡         |   `l   \__   我々がその気になれば
    !、           _,,..-'′ /l     | ~'''  対応は
‐''" ̄| `iー-..,,,_,,,,,....-‐'''"    /  |      |    10年後 20年後ということも
 -―|  |\          /    |      |   可能だろう・・・・・・・・・・ということ・・・・!
    |   |  \      /      |      |

Tru64 UNIXで遊ぼう!(開発環境編)

Tru64 UNIXをインストールし終わったら、せっかくなので開発環境を整えましょう。
まずは標準コンパイラCompaq_C_Compilerをインストールします。

とてもありがたいことにHPが無料で公開しています。

HP Software - Developers' Toolkit Supplement - Software Kit Downloads

マニュアルも公開してあります。

Tru64 UNIX

インストールは簡単です。

Compaq_C_Compilerは優秀ですが、gccでなければコンパイルできないアプリケーションが沢山ありますので、gccもインストールします。

方法は

Compiling GCC 4.4.7 on Tru64 5.1B | Astrobaby's random thoughts

に書いてありますが、自分も参考で書いておきます。

Gnu tarをインストールします。

Tru64 UNIX標準のtarでは下記のようなLongLinkエラーが発生して大抵のtar ballの展開に失敗するからです。

バージョンは1.26を使ってください。


次はlibiconvをインストールします。


次はgmpをインストールします。


次はmpfrをインストールします。


次はGnu makeをインストールします。


ここまできたらgccコンパイルを行います。

プロセスのメモリの使用制限に引っかかってコンパイルに失敗しました。

その場合は/etc/sysconfigtabに

proc:
 per_proc_stack_size = 33554432
 per_proc_data_size = 801326592

と書いて回避します。
(リブートが必要)

うまくいけば、gccコンパイルに成功するはずです。

今回はソースからのインストールを行いましたが、面倒くさがり屋さんのためにパッケージも用意してあります。

Index of pub/pkgsrc/misc/tnn/index.html

pkgsrcは素晴らしい!
ただし、パッケージ版gccを使うにしてもGnu tarのインストールは必須です。

Tru64 UNIXで遊ぼう!(インストール編)

今は亡きDECの遺産、OSF/1ことDigital UNIXことTru64 UNIXで遊ぼうというという記事です。

ユーザーが少なくなってきて、記事が散逸し始めているので、基礎的なことをまとめておきます。

インストール編とタイトルに書きましたが、インストールする前に大事なことが1つあります。

Tru64 UNIXはライセンスが無いとまともに動きません。
ヤフオクなどでOSのメディアは時々出品されていますが、ライセンスが付いていないことがほとんどです。
ライセンス付きのTru64 UNIXが出品されていれば、死ぬ気でゲットしましょう。
最低限必要なライセンスはOSF-BASE、OSF-USR、OSF-SVR、OSF-DEVです。

Tru64 UNIXを新し目のハードにインストールする場合は、New Hardware Delivery kits(NHD)というCDが必須です。
Tru64 UNIXメディアセットに同梱されていますが、無い場合はHPが無料で公開しているので、焼いて使いましょう。

Tru64 UNIX New Hardware Delivery Online Documentation

インストールは以下のNHDのCD1枚とTru64 UNIXのインストールCD1枚の計2枚をとっかえひっかえして進めていきます。

インストーラにTru64 UNIXのCDを要求されてもCDドライブに入れてはいけない場合など罠が満載なので、以下のマニュアルを熟読して進めてください。

NHD for Version 5.1B Installation Instructions

インストール時に英語の他に追加で使用する言語を選択できますが、自分の場合は日本語を選択するとグラフィカルインストールに失敗するという罠に陥っていまいました。
グラフィカルインストールを行う場合は追加の言語で日本語を選ばないほうが良いかもしれません。
テキストインストールならば追加の言語で日本語を選択しても問題ありません。
グラフィカルインストールとテキストインストールの切り替えは
SRM画面で

>>set console graphics
>>set console serial

で切り替えできます。

Tru64 UNIXインストーラが標準で割り当てるHDDのパーティションの容量が少ないので、後で悶絶必死です。
(以下の例だと/が384MBしかない)

必ずパーティションは切り直しておきましょう。
(以下の例だとパーティションaが/で15GB、パーティションbがswapで1GB、パーティションgが/usrで10GB、パーティションdがLSMの予備で8GB)

一度インストールが完了すると、リブートがかかり、インストーラが自動でパッチを当て始めます。

パッチ当てが終わると再々度リブートがかかり、インストールは完了です。

とっても高性能だけれど、いまいち影が薄いAdvFSで遊んだり、スピードメーターを出したりして遊びましょう。

Tru64 UNIXの管理コマンドはsysmanです。

登録するユーザーはsuする場合はGroupはsystemにしておきます。

【夢を超えた】Mac版XM6iを使ってNetBSD/x68kを動かすという話【夢の、頂きへ】

急にお金M68Kアーキテクチャコンパイルされたバイナリが必要になることってありますよね。

そんなときあなたならどうしますか?
誰かに借りるにしても、理由を説明して頭を下げないといけない・・何より人間関係が気まずくなるのだけは避けたいものですよね。
そんなあなたにおすすめなのが、即日融資でお金を借りるXM6iにNetBSD/x68kをインストールしてコンパイルする方法です。

XM6iとは?

XM6i - クロスプラットフォーム X68000/X68030 エミュレータだそうです。
XM6i - クロスプラットフォーム X68000/X68030 エミュレータ

X68000/X68030というのは1987年3月28日にシャープが発売したパーソナルコンピューターだそうです。
X68000 - Wikipedia

オープンソースカンファレンス広島のjapan netbsd users groupのブースでは毎年X68000/X68030を使って、すごーい!たーのしー!ことをやっているので、興味がある人は見に行ってみると変なスイッチが入ったり入らなかったりするかもしれません。
OSC2016広島 (2016/11/27) 発表スライド

インストール方法とMac版の問題

配布元のダウンロードページに
http://xm6i.org/download.html

Mac 版について

ver 0.53 から SDL2 を使用しているため、 サウンドなし版、あり版の区別はなくなりました。 
Mac OS X 10.8 でビルドし、10.11 でも動作を確認しています。 それ以外のバージョンについては分かりません。
 

とあったので、ダウンロードして、展開し、バイナリを実行したところ、異常終了してしまいました。

とっても困っていたところ、@LabDrunkerさんに"MacPortsを使ってGCC5を入れて、/opt/local/lib以下のlibstdc++が使われるようにしたら、動くようになりました。"という貴重なアドバイスを頂きました。

どうして、この解決方法に気づいたのか質問してみたところ、

とのご回答をいただきました。ぼんくら物理学者... だと... こいつ…かなりの切れ者...

確かにotoolを使ってxm6iがリンクしている共有ライブラリを調べてみると、/opt/loca/lib以下のlibstdc++が使われているようです。

実験として/opt/localを掘って/usr/libにシンボリックリンクをはってみましたが、xm6iが異常終了してしまいました。

これは@LabDrunkerもおっしゃっていますが、gcc5からlibstdc++ではデフォルトで新しいABIを利用するようになっているためと思われます。(そしてMac版xm6iはgcc5を使ってコンパイルされているためと思われます)

早速MacPortsから、gcc5をインストールしてみました。



問題が解決したので、XM6iが起動するようになりました!

NetBSD/x68kのインストール

早速準備が整ったので、NetBSD/x68kをインストールしてみます。

やり方は、@tsutsuiiさんのページに詳しい方法が書いてありますので、参考にしてください。
NetBSD/x68k on XM6i ver 0.41 - クロスプラットフォーム X68000/X68030 エミュレータ

インストールCD ISOイメージ

http://ftp7.jp.NetBSD.org/pub/NetBSD/iso/6.1.3/ の
iso ディレクトリ内の NetBSD-6.1.3-x68k.iso (約162MB) をダウンロードして c:\XM6i\NetBSD\ に置く。

もちろん、OSのバージョンは適宜読み替えてください。

インストールには数時間かかりますが、きっとうまくいくはず... です...

↓ちなみに失敗例

無事インストールすることが出来ました!

失敗した原因は


です。

よって、ノリでインストールしなければ、インストールに時間はかかるものの問題無くNetBSD/x68k on XM6i Mac版の環境を楽しむことが出来ます!

Mac版XM6iのネットワークの設定は

 Mac OS X 版では Nereid イーサネットエミュレーションのホスト側に bpf(4)
  を使用しており、ホストマシンを介して外部ネットワークとの通信が可能です。
  ただし Mac OS X 側の制約によりホストマシン自身との通信は行えません

とあります。

Mac版のXM6iを試すような人にパソコンを一台しか持っていない人なんていないと思いますので、問題ないと思います。

ああ・・次はpkgsrcだ...