SVX日記
2005-12-08(Thu) うるう秒を先取る
久々にうるう秒がやってくるらしい。地球の自転のズレがどうとかで発生する、いわば「スキマな時間」であるワケだが、たった1秒であるから有効に活用することもできない。うるう年にある「うるう日」は、シッカリ24時間あるから活用できるが、暦の一部であるからフツーに平日である。ちぇ。
ちなみにこの「閏」という漢字。元は「うるう」という読みはなく「ジュン」と読むのが正しかったらしい。なんでも「潤」の漢字と読み間違ったところから「うるう年」という読みが生まれ「閏」の訓読みとして定着したそうな。マコトにダサいが、ママあるコトでもある。オーケー、OK、Oll-Kollect、All-Collect……こいつもそうだしな。
もうひとついうと、うるうシリーズには「うるう年」「うるう月」「うるう日」「うるう秒」がある。うるう月は太陰暦の概念であり、現在の暦には存在しない。一方で、うるう年はうるう日の存在する年を表す言葉であり、実体を持たない時間である。コトバというものの、誠にファジーであることよ。
それに引き換え、地球の自転の正確なこと。そりゃ、4年に一度、100年に一度、400年に一度の「うるう日」の補正は入るが、それ以外は数年に一度のうるう秒だけなんて、どうにかしているとしか思えない正確さ、偶然さである。月がいつもこっちを向いているのと同じくらい奇妙である。いつか悪いことがおきるような気がするぞ。知らないぞ。
しかしながら、いただけないのは、365日、7日/週、12月/年という現在の暦制度。プログラムでの日時の扱いの面倒だったらない。まぁ、365を因数分解したところで、5x73までしか分解できないから、画期的に美しい暦は提案できないだろうから、仕方ないトコロではあるが……あぁ、この世の基数が2でないことの、なんという醜さよ、かく言う私の指も片手に8本ないのが恨めしい……というギャグは置いておいて。
さて、今日のテーマはうるう秒である。たった1秒であるが、これが結構ややこしい。なにがややこしいかって、うるう秒がOSでどう扱われているかがややこしいのだ。で、オイラはLinuxの人なので、このあたりの知識をねっとりと勉強するハメになった。実はこの件に関しての質問が多いのよ。1秒くらいどうでもいいじゃんとは思うし、その1秒を重要視する必要のある状況は多くないだろうから、興味本位の質問だとは思うのだが。
結論から言うと、Linuxのカーネルにはうるう秒が清く正しくプログラムされている。このインターネット時代、NTPで時刻同期をするのはもはや常識ではあるが、このNTPプロトコルにもうるう秒を取り扱うための情報が含まれているのだ。その名もLI(LeapIndicater)ビット。このビットはうるう秒が存在する日は一日中オンになっていて、この情報は下位のNTPサーバに伝播していく。
で、問題は実際の補正がどのように行われるかである。NTPクライアントがLIビットを受け取ると、これはOSに渡される。Linuxの場合、カーネル内部のtime_stateという変数がTIME_INSに変化する。そして、時間が日本時間で8時59分59秒から60秒になった瞬間、60秒から1が引かれ、59秒に戻されるのである。かくして1秒間の余計な時間が刻まれることで、その瞬間とて、1秒足りともズレることなく正しく時が刻まれるのであった。
392 case TIME_INS:
393 if (xtime.tv_sec % 86400 == 0) {
394 xtime.tv_sec--;
395 time_state = TIME_OOP;
396 printk(KERN_NOTICE "Clock: inserting leap second 23:59:60 UTC\n");
397 }
398 break;
385 case TIME_OK:
386 if (time_status & STA_INS)
387 time_state = TIME_INS;
388 else if (time_status & STA_DEL)
389 time_state = TIME_DEL;
390 break;
240 #define TIME_INS 1 /* insert leap second */
221 #define STA_INS 0x0010 /* insert leap (rw) */
このビットを立ててやれば、なにも正月を待つことなく、明日の朝9時にでも、うるう秒を挿入することができるのである。このビット、通常はNTPから、おそらくadjtimex関数経由で立てられるビットであるが、面倒くさいので今回は直接カーネル内部の変数をイジってしまうことにする。カーネル内部の変数をイジるには、以前に作ったモジュール、intruder.cを使う。
1 //
2 // カーネルの内部変数を確認、変更する&内部関数を呼ぶモジュール
3 //
4 // コンパイル方法
5 // # cc -c -I /usr/src/linux-`uname -r`/include intruder.c
6 //
7 // 実行方法
8 // # insmod intruder.o; rmmod intruder; dmesg | tail
9 //
10
11 #define MODULE
12 #include <linux/module.h>
13
14 #include <linux/kernel.h> // for printk
15 #include <linux/time.h> // for struct timeval
16 #include <linux/timex.h> // for STA_INS
17
18 int init_module(void) {
19 printk(">>> Intruder module installed.\n");
20 {
21 int* time_status = (int*)0xc0XXXXXX; // /boot/System.map - time_status
22 printk("time_status = %x (before)\n", *time_status);
23 *time_status |= STA_INS;
24 printk("time_status = %x (after)\n", *time_status);
25 }
26 {
27 int* time_state = (int*)0xc0XXXXXX; // /boot/System.map - time_state
28 // *time_state = 1;
29 printk("time_state = %x\n", *time_state);
30 }
31 return 0;
32 }
33
34 void cleanup_module(void) {
35 printk("<<< Intruder module uninstalled.\n");
36 }
37
38 MODULE_AUTHOR("ITLine Inc. <furutanian@gmail.com>");
39 MODULE_LICENSE("GPL");
40 MODULE_DESCRIPTION("Kernel Intruding Module");
time_statusとtime_stateはカーネルのグローバル変数であるが、外部に公開されていないので、上記の「0xc0XXXXXX」部分に直接アドレスを書き込む必要がある。書き込むアドレスを知るには/boot/System.mapを参照し、現在稼動中のカーネルのシンボルを確認しなければならない。アドレスを間違えるとカーネルのどこかを適当に書き換えてしまうので、暴走する危険があるので注意だ。そもそもSystem.mapがなかったり、情報が不足している場合は、以下のコマンドでvmlinuxからSystem.mapを生成するとよい。
nm vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map
で、ちゃんとintruder.cの該当部にアドレスを書き込んでコンパイルしたら「# insmod ./intruder.o; rmmod intruder; dmesg | tail」を実行。time_state = 1が表示されたら「うるうビット」が立った証拠である。「date -s "mm/dd 8:59 2005"」を実行、時刻を8時59分にセットして、dateコマンドを連打。うるう秒が発生するのを待とう。
Www Mmm dd 08:59:60 JST 2005……出た? なに!? 出なかった? そう。実は「59秒が2秒間継続する」だけで、60秒という時間は出てこないのである。というのも、UNIXの標準を定めているPOSIX規格では、うるう秒の存在は「ないことになっている」のである。まぁ、確かに数年に一度だけ60秒なんて数字が出てこられたら、アプリケーションが問題を起こしてしまうよな……ん? 待てよ? すると、アレはなんなんだ!? 「man strftime」の中の「%S 秒 (10 進数表記) (00-61)」の記述は!? オイラが見たい未来は、こんな59秒が2秒間続くような未来じゃないんだッ!!
確かにPOSIX規格の中には60秒は存在しない。しかし、タイムゾーンの設定によっては、POSIX規格を敢えて外れ、うるう秒を正しく処理することもできるのである。ちなみに、タイムゾーンの設定はWBEL3(RHEL3)の場合「/usr/share/zoneinfo」の下にある。
# find /usr/share/zoneinfo -name Tokyo -exec ls -l {} \;
-rw-r--r-- 2 root root 125 12月 16 05:44 /usr/share/zoneinfo/Asia/Tokyo
-rw-r--r-- 2 root root 125 12月 16 05:44 /usr/share/zoneinfo/posix/Asia/Tokyo
-rw-r--r-- 2 root root 309 12月 16 05:44 /usr/share/zoneinfo/right/Asia/Tokyo
3つのファイルが見つかった。デフォルトはAsia/Tokyoで、実はAsia/Tokyoとposix/Asia/Tokyoは同じ物である。つまり、デフォルトではPOSIX準拠の動作が行われるために、60秒という時間を刻むワケがないのである。そこで、以下のコマンドを試してみる。
# date; TZ=right/Asia/Tokyo date
2005年 12月 12日 月曜日 09:23:36 JST
2005年 12月 12日 月曜日 09:23:14 JST
同じJSTのハズなのに、後者は22秒遅れている。実はこれこそが、今までに発生したうるう秒を考慮した「right」な時間なのである。このタイムゾーンファイルright/Asia/Tokyoには、今までのすべてのうるう秒が登録されている。しかしながらRHEL3の発売当初には、今年の正月にうるう秒が挿入されるコトなど判明しているワケがない。よって、うるう秒発生の場合にはタイムゾーンファイルを更新する必要があるわけだ。以下の場所から、最新のタイムゾーンファイルをダウンロードしよう。
ftp://ftp.redhat.com/pub/redhat/linux/updates/enterprise/3ES/en/os/SRPMS/tzdata-2005m-1.EL3.src.rpm
# rpm -ivh tzdata-2005m-1.EL3.src.rpm
# rpmbuild -ba /usr/src/redhat/SPECS/tzdata.spec
# rpm -ivh /usr/src/redhat/RPMS/noarch/tzdata-2005m-1.EL3.noarch.rpm
1 #!/usr/bin/ruby
2
3 p ENV['TZ'] = 'Asia/Tokyo'
4 p Time.now.strftime('%F %T')
5
6 p ENV['TZ'] = 'right/Asia/Tokyo'
7 p Time.now.strftime('%F %T')
後はタイムゾーンを「export TZ=right/Asia/Tokyo」で設定して、さっきの「insmod ./intruder.o; rmmod intruder; dmesg | tail」は「やらない」。そして今度は「date -s "01/01 8:59 2006"」を実行し、ホントにうるう秒が発生する時刻の直前にセット。チョイと小洒落て「while true; do ./strftime.rb; echo; sleep 1; done」なんて小細工しちゃったりして、うるう秒が発生するのを小粋に待とう……
# while true; do ./strftime.rb; echo; sleep 1; done
"Asia/Tokyo"
"2006-01-01 09:00:20"
"right/Asia/Tokyo"
"2006-01-01 08:59:58"
"Asia/Tokyo"
"2006-01-01 09:00:21"
"right/Asia/Tokyo"
"2006-01-01 08:59:59"
"Asia/Tokyo"
"2006-01-01 09:00:22"
"right/Asia/Tokyo"
"2006-01-01 08:59:60" ←60秒目が発生!!
"Asia/Tokyo"
"2006-01-01 09:00:23"
"right/Asia/Tokyo"
"2006-01-01 09:00:00"
2013-12-08(Sun) アンドンがほしくなって自作開始だドン
koboを買ってから、毎晩、寝床で読書しながら寝落ちすることを日課としているオイラだが、ふと、その間、煌々と天井灯を点けていることが無駄に思えてきた。kobo gloならば、フロントライト点灯状態がデフォなので、周囲は仄暗い程度で問題ないのだ。むしろ、就寝前はその程度のほうがリラックスできるし、雰囲気がある。
てなわけで、実は昨日ホームセンタに材料を買いに行ったのだが、木材は結構お高い……まぁ、そんなによい材料を使わなくとも、十分なものができるだろう、ということで、ダイソーで売っている910x21x21mmの角棒を使用することに決定した。できるだけ、反っていないものを慎重に選んで6本購入し、ついでに、ダボ用にΦ3mmのたけひご、木材の表面処理のための水性ニス(けやき色)も購入した。
帰宅して、木材加工を開始した。ダイソーの木材から、35cmを4本と20cmを11本、ダイソーの糸鋸で切り出して、ダイソーのスポンジ紙ヤスリ80番で磨き、ダイソーの水性ニスを、ダイソーのハケで塗る……なんとも、恐ろしくなるほどのダイソー率だ。
下側の梁に隠し穴を2箇所づつドリルで穿つ。各面には、別途、障子をハメ込む予定なのだが、その固定のため、下側からタッピングネジで突かせるためのもの。隠し穴はΦ2.5mmとΦ6.0mmで2段になっており、ネジ頭が隠れるようにしてある。
水性ニスは乾いてから重ね塗りする。これがまた、楽しい……白木が徐々に味のある色に変わっていく。木目が強調されて美しさを増していく。木材って、ホント、いいもんだよなぁ。人が木によりそった状態が「休」ってのは心底同感するよ。
2015-12-08(Tue) SwiftにGlanceしてみたい
どうも本格的に使う前に、味見するだけで相当かかりそうなopenstackであるが、イメージサービス「Glance」が、オブジェクトストレージ「Swift」を利用していないことに気づいてしまった。せっかくなので上に載せてみたい。
んが、どうもpackstackによりインストール時からその状態にすることはできないようで、インストール完了後に設定を行う必要がありそうな感じ。openstackについては、まだ右も左もわからない状態なので、そのまんま「glance swift」としてググってみる。すると、容易に「ObjectStorage Service をイメージの保管に使用する方法」などという、ほぼそのまんまっぽい情報が見つかった。
そもそも認証とかエンドポイントの概念について、ほとんどわかってないんだよな。コマンドラインからkeystoneでトークンをもらってみたり、telnetでhttp://xx.xx.xx.xx:5000/v2.0/につないでみたりしてみる。なるほど。エンドポイントとはURIであり、httpのPOSTで認証情報と命令を送り、結果を受取るわけか。
と、極めて初歩的な気付きが得られたものの、Glanceのエラーについてはサッパリわからず。manページを眺めたり、confをgrepしたり……って、ん? schemeって、storeのタイプってこと? もしかして、こうか?
# vi /etc/glance/glance-api.conf
:
# List of stores enabled (list value)
stores=swift,file,http
:
おぉ、glance-apiが上がるようになった。なんだよ、一番大事な設定が抜けてたんじゃねぇか。ちゃんと書いておけよな。ブツブツいいながら、Horizonのadmin権限で、Fedora23のイメージをGlanceに登録してみた。成功。これで、オブジェクトストレージを見に行けば、イメージがオブジェクトとして登録されているはず……と思ったら、登録されてない。なんで?
fileとして登録されているFedora23のイメージが/var/lib/glance/imagesの下に存在していることはわかっている。加えて、swiftとして登録されているであろう同程度のサイズのファイルが、/srv/node/device1/objectsの下にも存在していることもわかっている。
もしかして、Glanceユーザのテナントに配置されている? Glanceユーザでなんて、ログインできるのかしらん。Horizonにログインするにはパスワードいるよな。設定した覚えはないが……って、もしかしてアレか。/etc/glance/glance.conf的な設定ファイルの中に自動設定されているとか? ビンゴ!
# cat /etc/glance/glance-api.conf | grep -i password
admin_password=****************
#===========================================================================
#
# glance のバックエンドを swift に変更
#
kgt.execs([
kgt.systemctl(%w!status openstack-glance-registry!),
kgt.systemctl(%w!status openstack-glance-api!),
])
swift_store_auth_address, swift_store_key =
kgt.get_params('/etc/swift/proxy-server.conf', ['auth_uri', 'admin_password'])
kgt.modify(
'/etc/glance/glance-api.conf', [
kgt.ss_set_param('stores=', 'swift,file,http'),
kgt.ss_set_param('default_store=', 'swift'),
kgt.ss_set_param('swift_store_auth_address=', swift_store_auth_address),
kgt.ss_set_param('swift_store_create_container_on_put=', 'True'),
kgt.ss_set_param('swift_store_user=', 'services:swift'),
kgt.ss_set_param('swift_store_key=', swift_store_key),
])
kgt.execs([
kgt.systemctl(%w!restart openstack-glance-registry!),
kgt.systemctl(%w!restart openstack-glance-api!),
])
2024-12-08(Sun) 続・自分を見詰め、字を詰める
思い返せば、小学校の5年だ。担任が社会の先生なので、社会の授業内容が一風変わっていた。丸1年かけて「瓦」の製造を行う「自動工場」と「伝統手工業」の両者について、見学、インタビューを通じて、比較していくというものだった。それは、今でも強く覚えているくらいに、自分には楽しい取り組みで、追加のインタビューに行くメンバの募集にも手を挙げ、担任の車で「伝統手工業」の作業場を再訪したりした。
次は、中学校の2年だ。音楽の授業で「アイネ・クライネ・ナハトムジーク」の冒頭を探してきてテープにダビング、印象をまとめて発表すること、というチーム課題が出た。自分は、ちょうどSHARPのX1を買ってもらってしばらくした頃だったので「冒頭部分はオレが作るから」とMMLを書いてPSGで鳴らしてテープに録音し、発表に臨んだ。ところが、PSGの音色はファミコン以下なので、曲が流れ始めるなり授業は爆笑の渦。先生は「面白いね、ちょっと間違ってる音もありましたけど」と苦笑い。しかし、忘れられないのは、その直後に誰かに言われた「お前、音楽5だぜ」だ。
別に「やってみたかったからやった」だけで、そんな狙いは一切なかったので、そう言われても「そんなもんかな」としか思わなかったが、気づけば、上述の小学校の時も似たようなことがあった。いま考えると、そういう「点稼ぎ」にフォーカスして生きている人間は、その頃からいたし、その年齢でそうだということは、おそらく親もそういう人間なのだろう。別に「悪いこと」とまでは言わないけれど。
ここ数年「歌」を続けている。カラオケでの練習の際に「機械採点」機能を使っているが、それにフォーカスして歌うことはない。だって歌の本質は「聴衆へのメッセージ」だろ? 誰かに聴かせることは滅多にないけど、その軸足を動かすことはない。絶対に。
例え話だが、自分はいつものストリートで歌っている。少しずつファンもついてきて、みんな楽しんでくれている。中には、歌い出す前から、帽子の中に千円を入れてくれる人もいる。それに対して、今日も最高の歌を聴かせるぜ、と心に誓うことはあっても、明日も千円を入れてくれますか、と尋ねることはない。た・の・ま・れ・て・も、決してそれはしない。それでストリートの管理者からの評価が下がろうが知ったことか。それは信念だ。それが通せないのであれば、そのストリートを去るまでのことだ。ファンには申し訳ないけれど。
さて、昨日の字詰めの続きだ。実は昨日のコードは「AIの回答内容」を折り返して表示させたい、という用途で書いたものなのだが、よく考えたら「幅xx文字で字詰めして答えて」とAIに指示する方法もあるのではないか、と思いついた。
Q. 野球のルールについて、400文字程度、漢字で幅19文字で字詰めして、説明してください。
A. 以下は、幅19文字で字詰めした野球のルール説明です。
野球は九回まで攻守を交
代し得点を競う競技です。
攻撃側は打者が投手から
投じられる球を打ち、一
塁から三塁を回り本塁へ
戻ることで得点します。
一方守備側は三つのアウ
トを取ると攻守交代とな
ります。打者はストライ
クを三つ取られるか、飛
球やゴロを守備側に捕球
されるとアウトとなりま
す。攻撃側は打者が出塁
し次の塁を狙い、走者と
して得点を目指します。
最終的に得点が多いチー
ムが勝者となります。
おい。全部で200文字にも満たないし、幅も11文字じゃねーか。「19文字になっていませんが。」とやり直させたが、なぜか12文字に変更した挙げ句に「各行が正確に19文字になっています。」と断言してきた。どういう勘定なんだ、それは。ダメだこりゃ。ま、そういうのが不得意ってことはわかったよ。
一方で、ここしばらくは手元で動かすAIにも興味を持っている。先日「3.1-Swallow」ってのがいいよ、と聞いたので試してみることに。ところが、ggufをダウンロードできる場所にたどり着いたものの、似たようなファイル名のサイズ違いが20個以上も並んでいるではないか。どうやら量子化という、いわば圧縮レベルを変えたものが、ズラズラと並んでいるらしい。
あまり詳しくはないが、LLMの基本はベクトルの集合。浮動小数点の組で表現されているそれを整数化することにより、圧縮するのが量子化らしい。それにより、精度は低下するが、サイズが小さくなり、演算も速くなる。そういえば、最近のCPUにはNPUというAI向けの演算器が乗っているらしいが、速度のために精度を犠牲にした演算器だと認識している。それと同じことか。
詳しくはわからんが、なんでも、Q5_K_M, Q5_K_S, Q4_K_Mくらいが、バランスのよい量子化レベルらしい。知らずに最初に試したIQ1_Mには「こんにちは。」を無限に繰り返されたしなw。なるほど。
# docker exec -it ollama-alpha ollama run Swallow-8B-Instruct-v0.1-Q5_K_M
>>> 野球のルールについて、400文字程度、漢字で幅19文字で字詰めして、説明してください。
野球は9人ずつの二チームが交互に攻撃と守備を行う競技です。攻撃側は打者1人と走者3人(一塁、二塁、三塁)を出し、相手の守備側から点を得ることを目指します。守備側は投手1人が投球し、他の8人が守備位置につき、打者の打撃を阻止したり、走者をアウトにすることで攻撃を終わらせます。
試合は9イニングで行われ、各イニングは3アウトになるまで続けられます。3アウトになると攻守が交代します。攻撃側は投手の投球を受けて打撃し、走者が出た場合には次の塁を目指して走ります。
守備側は投手にボールを投げさせて打者の打撃を阻止したり、打球を処理してアウトにすることで攻撃を終わらせます。走者がアウトになると攻撃は終了し、次のイニングに移行します。
野球では、守備側が3つのアウトを取るか、攻撃側が得点を挙げて勝利するかのいずれかで試合が決まります。
>>> Send a message (/? for help)
Q. ひろゆきっぽい感じでお願いします。
A. 以下は「ひろゆき」っぽい論調を意識しつつ、19文字幅に整形した野球ルールの説明です。
野球って九回まで攻守交代
して得点を競う競技なんで
すよね。でも、実際これっ
て守備がアウト三つ取った
ら交代になるんですよ。攻
撃側は球を打って一塁から
三塁回って本塁に戻ると得
点になるんですけど、守備
側が飛球とかゴロ捕ったら
アウトなんですよね。で、
最終的に点数が多い方が勝
ちなんですけど、これって
時間かかりすぎじゃないで
すか?もっと効率的な方法
とか考えないんですかね?
……あ、最後に字詰めについてその後に気づいたことだ。まさにこの行のように、しばしば自分は行頭を「……」から始める表現を用いることがある。って、言いつつ、まさにこのように、しばしば自分は文を「っ」から始める表現を用いることもある……これ、どちらも行頭禁則に抵触する表現だ。が、これはアリって気がする。
〜〜を「……」から始める表現を用いることがある。
って、言いつつ、まさにこのように、しばしば〜〜
一方で、昔から笑いながらの発言を示す表現に(笑)がある(笑)。が、それも最近だと草生える、という表現が一般的だw。新しい表現なので、句点の扱いに困るのだが自分は句点の前に付けている。まぁ、正式な表現ではないからルールなんてなく、好きにさせてもらうwww。が、この場合に限って「w」は行頭禁則がふさわしい気がするのだよな。つまり、これはイヤだ。
〜〜いからルールなんてなく、好きにさせてもらう
www。が、この場合に限って「w」は行頭禁則が〜〜