母の死とがんゲノム医療

2019年9月17日に母が乳がんで亡くなった。
昭和31年12月1日生まれ、享年62歳だった。

母はがんゲノム検査という保険適用外の検査を行っていた。
(P5がんゲノムレポート、費用100万円程)
www.p5genome.com

残念ながらがんゲノムレポートは活用される事無く母は亡くなってしまった。
しかしながら、がんゲノム医療は今後ガン治療の主要な選択肢になっていくであろうから、
その記録としてインターネットに経緯を残しておく事にした。

がんゲノム医療については
漫画 フラジャイル 病理医岸京一郎の所見(15巻、16巻)がわかりやすく詳しい。
kc.kodansha.co.jp
(16巻は10月8日現在、未刊)

---------- 以下亡くなるまで時系列 ----------

2017年11月21日
母にがんの診断がおりた。
母は浸潤性小葉癌という乳がんで発見時は皮膚とリンパへの転移でステージIVだった。
しかし骨には転移しておらず、皮膚への転移も乳がんが有る左胸と同じということでステージ3C扱いで、根治を目指した。


トリプルネガティブ乳がんだったのでまずは抗がん剤治療のみ行った。
がんが小さくなったため2018年6月4日手術を行った。
その後は放射線治療を行った。

2019年2月5日
乳がんが再発した。


上半身に湿疹が沢山でき、調べたら乳がんが皮膚に転移したものだった。
(主治医も見た事がない症例で、最初は”何でしょうかね〜”という扱いだった)
しかし骨には転移していなかったため、抗がん剤治療を行い、がんと共存しQOLを上げていくという治療方針を決めた。
母は抗がん剤の影響で髪は抜けていたが、カツラをかぶって区役所の子供課で嘱託の仕事をしていた。
母子手帳の交付など行っており、とても仕事は楽しかったようだ。
また、琴や囲碁の友達にもめぐまれたようだ。
(葬儀の遺影はこの頃の琴の発表会のものを選んだ)

2019年7月16日
P5がんゲノムレポートを受ける。

2019年8月23日
効かなかったので、三種類目の抗がん剤の治療をはじめる。

2019年8月27日
母は腰が痛く動けなくなり入院した。
8月18日の週から腰に違和感は感じていたようだが、本人はぎっくり腰だと思っていたようだ。
しかし、トイレに行くのも難しくなり、ついに病院に頼み込んで入院させてもらった。
また、足にも小さなコブのようなデキモノが沢山できており、それが時々疼くようで、精密検査を行う事になった。
足のコブのようなデキモノへの影響を考慮して抗がん剤の治療を一時的にやめることになった。

2019年9月3日
母は区役所の子供課の仕事を辞めた。
ひたすら「悲しい」と言っていた。
ただ、「いつ頃退院かなぁ?」と退院を楽しみにもしていた。
(実際、9月14日に退院の予定だった)

2019年9月8日
腰痛と足の小さなコブのようなデキモノの検査の結果が出た。
腰痛はがんが骨に転移しており、骨がもろくなった事が原因だった。
(背骨が圧迫骨折しているのでは?という疑いもあった)
また、足のデキモノは悪性リンパ腫の疑いがあったが、精密検査をした結果”悪性リンパ腫とは言い切れない”とのことだった。
悪性リンパ腫乳がんの併発という最悪の展開を免れたので一安心だった。



しかし、「骨に転移したという事は、いままで穏やかに進行していたがんの性質が変わったかもしれない」とも主治医は言っていた。
(母のがんは薬は効きにくいものの、進行は遅いタイプだった)

ここで母が行ったがんゲノム検査の結果が返ってきた。
レポートには、"ここなら治験が行える可能性があります"という病院が海外のものを含めて幾つか書いてあった。
"治験を行うか?"、"標準治療の抗がん剤治療を行うか?"という話になった。
ゲノムレポートに書いてあった治験の段階はフェーズ Iやフェーズ IIのものだった。
フェーズ Iやフェーズ IIはまだ動物実験レベル。
しかし、治験を行わないとがんゲノム検査を行った意味が無い。
話し合った結果、家族一致で標準治療の抗がん剤治療を行うという事になった。
ただし骨にがんが転移した結果、血液を作る機能が落ち始めていたので、抗がん剤の使用はタイミングを見て行うという治療方針になった。

このとき、主治医とこのようなやりとりがあった。

私:「がんゲノム検査を行ったのはこの病院で何人ですか?」
主治医:「お母さんを入れて二人目です。」
私:「一人目の方はどうなったのですか?」
主治医:「亡くなられました」
とても不吉な予感がした。

2019年9月14日
40度近くの高熱が数日に一回出る事、腰がまた痛くなり動けない事を理由に退院は延期された。
血小板の数は4000程度になり、抗がん剤治療も延期されたままだった。

2019年9月16日21時頃
母からポケモンGOのギフトが贈られてきたので、「俺の送ったギフトも開けてほしい」とメールをしたところ、
「母の体調は最悪です。ポケモンどころではありません。母は疲れたのでもう寝ます。」と返ってきた。
聞くと、見舞いに来た長女が母の携帯で遊んでいたらしい。
母に「また明日ね」とメールをした。

2019年9月17日
6時頃、母にメールで「おはよう」と送っても返事が無い。
9時頃、「調子は?」と送っても返事が無い。
9時32分、妹からメールで「病院から呼び出しかかった」と連絡が入る。
10時45分、長女が先生にお悔やみを言われる。
18時、病院に到着。長女から前日を含めてどのような最後だったのか聞く。

---------- 以下のようだったらしい。 ----------
前日は母のお見舞いをした時に話は出来たが、ずっと目をつぶったままだった事
ご飯は全部食べた事
話をしていていると急に眠り、長女が「今寝てたでしょ〜?」と言うと「寝てないよ」というやりとりが何回かあった事
当日は長女が病院に駆けつけると、母は目は半開きで苦しそうに息をしていた事
看護婦さんが朝の巡回を行った時はすでにその状態だった事
長女の呼びかけには答えなかった事
そのうち呼吸が弱くなり、長女が看護婦さんに「息して無いですよ!」と言うと、看護婦さんが母の名前を大声で叫んだ事
主治医の先生は手術中で来られなかった事
人工呼吸などはやらなかった事
看取ってくださった先生が、「眠ったようなお顔ですし、最後は苦しくは無かったはずですよ」とおっしゃった事。
--------------------

18時20分、主治医の先生から説明を受ける。

---------- 以下のような内容だった。 ----------
想定外の事態だった事
死因は乳がんだが、直接の原因はわからない事
血液検査の結果では、血小板が少ない以外に異常は見られなかった事
抗がん剤が効きすぎてがん細胞が一気に死に、その毒素が血液に一気に流れ出た事によるショック死の可能性が有るが、8月23日を最後に抗がん剤治療はやっておらず、その可能性も薄いとの事
--------------------

---------- 時系列ここまで ----------

解剖をして死因を調べる事もできたが、長女が望まなかったのでやらなかった。
自分の中のがんは"朦朧とした中で痛み止めを打ちつつ、ガリガリにやせて亡くなる"というイメージだったので、前日にメールでやりとりが出来て、眠るように突然亡くなった母の死は本当に想定外だった。

結果としてがんゲノム検査は残念ながら母の治療に生かす事は出来なかった。
ゲノムレポートに書いてあった適用できる可能性がある治験の段階がフェーズ Iやフェーズ IIのものだったからだ。
また、母のお世話になっていた病院は乳腺科に力を入れてはいたものの、がんゲノム検査のレポートを生かして治療に役立てるということは手にあまるような感じだった。
調べたところ、ゲノム検査の結果で判明した薬を患者に処方できる割合は10%程度しか無いそうだ。
そういう意味では検査の結果を治療に反映させる事がいまは難しいといえるのかもしれない。

それを承知だったかはわからないが、抗がん剤がなかなか効かない状態の中で100万円という安く無い費用を払って母は勝負をした。
母は新しもの好きだったし、"何事も興味を持ったものはやってみる"という性格だったので、検査とその結果には満足していたと思う。
もちろん、あなたのガンにはこの薬が効きます!という検査の結果が出るのが一番だったと思うが...

NDA等は特に聞いてないので、母のがんゲノムレポートを公開したいと思う。

f:id:nullnilaki:20191010022849p:plain
1
f:id:nullnilaki:20191010022925p:plain
2
f:id:nullnilaki:20191010022940p:plain
3
f:id:nullnilaki:20191010022952p:plain
4
f:id:nullnilaki:20191010023004p:plain
5

また、母は遺伝子検査も行い、乳ガンが長女に遺伝しないかどうかも調べていた。
(費用は20万円程度。残念ながら書類見つからず。女優アンジェリーナ・ジョリーが行った事で有名)
不幸中の幸いで母のガンは遺伝性ではなかった

遺伝子という一人一人異なるものを調べて、ガンを予防したり治療に生かしたりというがんゲノム医療
気軽に使えるようになるのはまだ5年10年先になるだろうが、そのような未来が早く実現し、少しでもガンで苦しむ人が少なくなれば良いと思う。
もちろん、ガン検診は予防のために積極的に行くべきだと思う。

この内容を公開すべきか悩んだが、がんゲノム検査を受けようか悩んでいる人の一助になれば良いと思い、インターネットに経緯を残しておく。

IRIXインストールしたい、したくない?

IRIXインストールしたい、したくない?

Personal IRISやIndigo R3000などのふるーいSgiマシンにIRIXをインストールしたくなる事が時々あると思います。
上記のマシンはIRIX 5.3が最終サポートOSになりますが、自分が調べた限りCDからのインストールがうまくいかなかったので、対処法を備忘録として書いておきます。

純正のCDドライブ用意した?

そもそもSgiのマシンにIRIXをインストールする時は純正のCDドライブを用意しないとインストールCDを認識しないなどトラブルの種なのですが、自分は純正のCDドライブを持っているにもかかわらず下記のようなエラーがでてうまくいきませんでした。

>> hinv
              Memory size: 32 Mbytes
   Instruction cache size: 32 Kbytes
          Data cache size: 32 Kbytes
            System option: Audio processor, revision 10
                SCSI Disk: dksc(0,1)
               SCSI CDROM: Controller 0, ID 6
                CPU board: IP12 33 MHz, with FPU
>> boot -f dksc(0,6,8)sashIP12
sc0,6,0: cmd=0x28 Unexpected info phase 46, state 49.  Resetting SCSI bus
dks0d6s8: retrying request
dks0d6s8: Drive not ready.
error on vh read
dksc(0,6,8)sashIP12: is not a bootable file
Unable to load dksc(0,6,8)sashIP12: no such device

もうちょっと新しいIndigo R4000といったマシンだと問題なく認識されます。

>> hinv
                   System: IP20
                Processor: R4000 50 Mhz, with FPU
     Primary I-cache size: 8 Kbytes
     Primary D-cache size: 8 Kbytes
     Secondary cache size: 1024 Kbytes
              Memory size: 144 Mbytes
                 Graphics: LG1
               SCSI CDROM: scsi(0)cdrom(6)
>> boot -f dksc(0,6,8)sashARCS
72912+9440+3024+331696+23768d+3644+5808 entry: 0x8ff9a950
Standalone Shell SGI Version 5.3 ARCS   Oct 18, 1994 (BE)
sash: 

自分が使用したCDドライブは下記の物になります。

IRIXの/usr/Cadmin/imagesディレクトリには下記のようなCDドライブのイメージが有りますので、もっと古い純正のドライブが有るのかもしれません...


ISOの〜

CDドライブからインストールできないなら、HDDからインストールすれば良いじゃない!
ということで、IRIXのインストールCDをISOイメージ化して適当なHDDにddした後に、そのHDDからIRIXのインストールを行うという回避策を思いつきました。

>> hinv
           Memory size: 40 Mbytes
Instruction cache size: 64 Kbytes
       Data cache size: 64 Kbytes
             SCSI Disk: dksc(0,1)
             SCSI Disk: dksc(0,4)
         System option: Bit-Plane Expansion
         System option: Z-Buffer
              Graphics: GR1.2
             CPU board: IP12 36 MHZ with Floating Point Unit
>> boot -f dksc(0,4,8)sashIP12
166848+82032+319248 entry: 0x80700c90

Error-- gfx(0) keyboard not responding

console open ("gfx(0)") failed: errno=6

Standalone Shell SGI Version 5.3 IP12  Nov  3, 1994 (BE)
sash: dksc(0,4,7)stand/fx.IP12

上記の例ではHDDと認識されたdksc(0,4)からIRIXをインストールするためのsashを呼び出す事に成功しています。

IRIXをインストールする方法

これでめでたくISOイメージ化したIRIXインストールCDをddしたHDD(くどい表現)からIRIXのインストールが可能になったのですが、一つ問題があります。
それは、IRIXのインストールが気持ち面倒くさいという事です。
SgiマシンのファームウェアにはIRIXをインストールするためのメニューがあるのですが、残念ながらこの機能が使えません。
なので、ちょっとだけ手作業が必要になります。

System Maintenance Menu

1) Start System
2) Install System Software
3) Run Diagnostics
4) Recover System
5) Enter Command Monitor

インストール作業は下記を参照してください。
パーティションの切り方。IRIX5.3はXFSではなくEFSですが、似たようなもんです。
Installation Guide SGI. Installing IRIX 6.5. Preparing a new disk

↓IRIXのインストールのやり方。簡単簡単。
sean caron > IRIX 5.3 on SGI Indigo R3000

いちおう自分の環境で作業した時のログもはっておきます。
IRIX_install · GitHub

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

この投稿は 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のインストールは必須です。