gettimeofday

ある日、ふと携帯を確認したら、兄貴より怒涛の着信履歴が。。。携帯と言いつつ常に身に着けている訳ではないので(散歩の時は、迷子札宜しく携帯しろよ。)気がつかなかったよ。

何か重大な事でも有ったのか? 慌てて連絡すると、携帯変えたですって! とうとう身内を語る、オレオレ詐欺がやって来たかと思ったぞ。あんたの生年月日は?とか、小学校の担任の先生の名前は?とかは、あえて確認するの止めたおいた。

なんでも、携帯からスマホにチェンジしたんで、通話出来るかの試験をしたんだって。で、さっぱり応答が無いものだから、何回も電話したとな。気持ち分かりますよ。登録ミスがあったのか、操作を間違えているんではと、不安になりますからね。

暫くして、遊びに来た。孫の所へ行っていたんで、その時の土産と孫の動画を見せにね。下手なアングルでの動画だった。孫の頭のアップなんて見せられても面白くないぞ。もっと修行してユーチューバーぐらいの腕前になったら、見せておくれ。

アンドドロイドの携帯。字が小さくてオイラーは扱えんな。女房はさかんに携帯止めてスマホにしろって、携帯屋の回し者のように勧めてくるけど、いよいよスマホ拒否症だな。使いもしないのに、毎日充電なんてヤナこったい。家にはパソコンは有るしipadも有る。幾ら使っても、ギガが減るってソワソワする必要無し。携帯屋の思惑にはハマりませんよ。

兄貴はLINEで苦労してるみたい。入力方法が分からんですって。それはオイラーは知ってるぞ。 タッチしてからスライドでしょ。あの人がずっと昔に自慢記事を書いてたのを思い出したら、一発で使えたよ。でも、慣れるまで大変そう。逆に慣れたら、キーボードもスライドしちゃったりして。

まあ、頑張って老化現象に歯止めをかけてくださいな。

gettimeofday

最近トランプ野郎の弾劾裁判で無罪を勝ち取った野郎が、それを伝える新聞を掲げて、久しぶりに良い記事を書いたと誉めていた。何時もはフェイクニュースを垂れ流すとメディアを攻撃してるのにね。子供だね、実に分かり易い人だ。

Linuxカーネル5.6、32ビット版で2038年問題への対応が行われるこんなのを前回取り上げた。真偽を確かめるのが大人の仕事です。記事を追って行くと、2038年問題に対応した「OpenBSD 5.5」リリースに達する。ここから先は、自分で調べろだな。

前回の投稿のサンプルをコンパイルしてみる。

OpenBSD o32.localdomain 6.6 GENERIC.MP#4 i386
o32$ cc t.c
o.c:7:29: warning: format specifies type 'long' but the argument has type
      'time_t' (aka 'long long') [-Wformat]
      printf("%ld %06lu\n", tv.tv_sec, tv.tv_usec);
              ~~~           ^~~~~~~~~
              %lld
1 warning generated.

実験環境は、言うまでもない32Bit機だ。コンパイラーから警告が出てきた。修正方法も提示されている。警告なんで、無視しても2038年1月までは、問題が表面化しないんだな。

でも、『冷酒と親の小言は後で効く』って、分かっているんで、早期に剪定しておけば、後は左団扇で暮らせる。最近は私のHPも外国の方が(翻訳しながら)読んで下さっているようなんで、ググル翻訳を困らせるような日本固有の格言は控えた方が良いのかな?

o32$ ./a.out
1581115660 487071
o32$ date -r 1581115660
Sat Feb  8 07:47:40 JST 2020
o32$ date +%s
1581115689

リナではdateコマンドに直交性が無くてイライラするけど、BSDは綺麗なものだ、とディスっておこう。ストレス発散で健康生活!!

o32$ gdb -q a.out
Reading symbols from a.out...done.
(gdb) b main
Breakpoint 1 at 0x12c6: file t.c, line 6.
(gdb) r
Starting program: /tmp/a.out

Breakpoint 1, main (argc=1, argv=0xcf7d9204) at t.c:6
6             gettimeofday(&tv, NULL);
(gdb) p sizeof(struct timeval)
$1 = 12
(gdb) p sizeof(&tv)
$2 = 4

gdbを立ち上げて、構造体のサイズとポインターのサイズを確認してみた。12ってのは、プチ半端な数字だな。まて、12 = 8 + 4 ではなかろうか。

(gdb) p sizeof(tv.tv_sec)
$3 = 8
(gdb) p sizeof(tv.tv_usec)
$4 = 4

ビンゴでしたね。こいつぁ朝から縁起が良い。

o32$ cc -E t.c | grep time_t
typedef __int64_t __time_t;
typedef __time_t time_t;
 time_t tv_sec;
 time_t tv_sec;
   :
o32$ cc -E t.c | grep suseconds_t
typedef long __suseconds_t;
typedef __suseconds_t suseconds_t;
 suseconds_t tv_usec;

ヘッダーファイルを紐解いてみると、上記のような定義だった。32Bit環境では、憧れのlongも実体は32Bitなんすね。

所で、man gettimeofday すると

GETTIMEOFDAY(2)               System Calls Manual              GETTIMEOFDAY(2)

冒頭にシステムコールって書いてある。そう、あちゃらの世界とのやり取りをする規約なんだな。間にlibcなんてのを挟まないストレートなやつ。

in kernel

と言う事で、場面は転換して、カーネルソースエリアに突入します。

o32$ cd /sys
o32$ find . -name '*.c' | xargs grep gettimeofday
       :
./kern/kern_time.c:sys_gettimeofday(struct proc *p, void *v, register_t *retval)
./kern/kern_time.c:     struct sys_gettimeofday_args /* {
./kern/syscalls.c:      "gettimeofday",                 /* 67 = gettimeofday */
./kern/syscalls.c:      "#116 (obsolete t32_gettimeofday)", /* 116 = obsolete t32_gettimeofday */

昔の痕跡(化石)が、しっかり残っていました。消してしまわない所が偉いぞ。kern_time.cを見てたら、

        if (tp) {
                memset(&atv, 0, sizeof(atv));
                microtime(&atv);
                if ((error = copyout(&atv, tp, sizeof (atv))))
                        return (error);
#ifdef KTRACE
                if (KTRPOINT(p, KTR_STRUCT))
                        ktrabstimeval(p, &atv);
#endif

カーネル内部では、atvって構造体を用意して、それを引数にしてmicrotimeを呼ぶと、そこに答えがセットされる。そのデータをユーザランド側のtpに転送するんだな。 ktraceを使うとカーネル側の結果をモニター出来るとな。これってSolarisのあれ(名前が出てこない -- 暫くしたら記憶が蘇ってきた。dtrace)にそっくりだな。

o32$ ktrace -tc+ ./a.out
o32$ kdump
       :
 42072 a.out    CALL  gettimeofday(0xcf7bdd68,0)
 42072 a.out    STRU  struct timeval { 1581120065<"Feb  8 09:01:05 2020">.645563 }
 42072 a.out    RET   gettimeofday 0

2038問題の検証

幸いな事に、32Bit版のdebianが有るので、 2038年問題の検証してみる。Y2Kを思い出すなぁ。とにかくunix時間の終末近くに時刻を設定。

debian:tmp$ sudo date -u 011903102038
Tue 19 Jan 2038 03:10:00 AM UTC
debian:tmp$ touch aaa
debian:tmp$ date
Sat 14 Dec 1901 05:51:58 AM JST
debian:tmp$ touch bbb

オーバーフローすると、1901年と言う過去へ行っちゃうのね。

debian:tmp$ ls -l aaa bbb
-rw-r--r-- 1 sakae sakae 0 Jan 19  2038 aaa
-rw-r--r-- 1 sakae sakae 0 Dec 14  1901 bbb

それぞれの時代?のスタンプを残しておいたので、年代を鑑定してみた。

debian:tmp$ date +%s
-2147482801
debian:tmp$ date +%s
-2147482787

epoc timeは、マイナスですよ。時間軸の原点は、言うまでもなく、1970/01/01 00:00:00 UTCです。

さて、現代に戻ってくるかな。

debian:tmp$ sudo ntpdate ntp.nict.jp
 8 Feb 15:22:08 ntpdate[4674]: step time server 133.243.238.244 offset -566341822.235377 sec
debian:tmp$ date
Sat 08 Feb 2020 03:22:28 PM JST

と言う事で、18年程の未来へ旅をし、そこからワープさせられて1901年まで飛ばされ、やっとこさ現代に復帰。どう足掻いたって、この時空に閉じ込められているからね。

この試験をやった翌日に、debian 10.3が降ってきたけど、改善はされていないんだろうね。

date

再びOpneBSDに戻って、大きな秒を与えたらどうなるか、検証(お遊び)。宇宙が果てると思われる秒数で試験

ob$ bc
2^60
1152921504606846976
ob$ date -r 1152921504606846976
date: conversion error

そんな大きな数字は想定してないってか。じゃ、小さめな数値

ob$ bc
2^32
4294967296
ob$ date -r 4294967296
Sun Feb  7 15:28:16 JST 2106
ob$ date -r -4294967296
Mon Nov 25 02:50:43 LMT 1833

未来はJSTで頷けるけど、江戸時代はLMTってか。一体それは何?

ob$ cc -g date.c -lutil
ob$ ./a.out
Mon Feb 10 05:26:49 JST 2020

取り合えず試運転しておいて、gdbにかける。

(gdb)
130             tp = localtime(&tval);
(gdb) p tval
$1 = -4294967296
(gdb) n
131             if (tp == NULL)
(gdb)
133             (void)strftime(buf, sizeof(buf), format, tp);
(gdb)
134             (void)printf("%s\n", buf);
(gdb)
Mon Nov 25 02:50:43 LMT 1833

ふーん、strftimeの仕業なのね。あっ違った。本当の変換をやってるのはlocalsubだ。

(gdb) bt
#0  localsub (timep=0xa72a09e7008 <tval>, offset=0, tmp=0xa75284109c0 <tm>)
    at /usr/src/lib/libc/time/localtime.c:1181
#1  0x00000a75283c94ce in _libc_localtime_r (timep=<optimized out>,
    p_tm=0xa75284109c0 <tm>) at /usr/src/lib/libc/time/localtime.c:1273
#2  _libc_localtime (timep=0xa72a09e7008 <tval>)
    at /usr/src/lib/libc/time/localtime.c:1287
#3  0x00000a72a09e46ea in main (argc=0, argv=0x7f7ffffd2fb0) at date.c:130

この関数を抜けると、値がセットされた。

(gdb) p *p_tm
$9 = {
  tm_sec = 43,
  tm_min = 50,
  tm_hour = 2,
  tm_mday = 25,
  tm_mon = 10,
  tm_year = -67,
  tm_wday = 1,
  tm_yday = 328,
  tm_isdst = 0,
  tm_gmtoff = 33539,
  tm_zone = 0xa7508caf248 "LMT"
}

man localtimeすると

                   int tm_sec;     /* seconds (0 - 60) */
                   int tm_min;     /* minutes (0 - 59) */
                   int tm_hour;    /* hours (0 - 23) */
                   int tm_mday;    /* day of month (1 - 31) */
                   int tm_mon;     /* month of year (0 - 11) */
                   int tm_year;    /* year - 1900 */
                   int tm_wday;    /* day of week (Sunday = 0) */
                   int tm_yday;    /* day of year (0 - 365) */
                   int tm_isdst;   /* is summer time in effect? */
                   long tm_gmtoff; /* offset from UTC in seconds */
                   char *tm_zone;  /* abbreviation of timezone name */

こんなのが出てきた。そして、/usr/share/zoneinfoこんな関係者も参照してるとな。ここで、うるう秒の調整もしてるとな。

この関数に入ってすぐの所で、zoneinfoのdbを呼び出しているっぽい。

(gdb) p *lclptr
      :
  chars = "LMT\000JDT\000JST", '\000' <repeats 500 times>,

こんな文字列が伺えたよ。

そんな訳で、zoneinfoのソースを閲覧

ob$ pwd
/usr/src/share/zoneinfo/datfiles
ob$ lv leapseconds
# If the leap second is Rolling (R) the given time is local time (unused here).
Leap    1972    Jun     30      23:59:60        +       S
Leap    1972    Dec     31      23:59:60        +       S
Leap    1973    Dec     31      23:59:60        +       S
Leap    1974    Dec     31      23:59:60        +       S
 :
Leap    2005    Dec     31      23:59:60        +       S
Leap    2008    Dec     31      23:59:60        +       S
Leap    2012    Jun     30      23:59:60        +       S
Leap    2015    Jun     30      23:59:60        +       S
Leap    2016    Dec     31      23:59:60        +       S

何時、うるう秒が挿入されたかと言う、地球の公転の記録がファイルになってました。

それから、timezoneの名前は、地域別になってて、Japan はAsiaに属しているんで、それを見ると、

# Zone  NAME            STDOFF  RULES   FORMAT  [UNTIL]
Zone    Asia/Tokyo      9:18:59 -       LMT     1887 Dec 31 15:00u
                        9:00    Japan   J%sT

こんな記録になってました。各国の事情が反映されてる訳ですな。 尚、LMTってのは、Local Mean Time の略で、この場合は、名無しの権兵衛と言った所でしょうか。明治21年から日本固有の時間(JST)だよと、喜んで宣言したんだな。

zone.tab

#country-
#code   coordinates     TZ                      comments
AD      +4230+00131     Europe/Andorra
AE      +2518+05518     Asia/Dubai
AF      +3431+06912     Asia/Kabul
AG      +1703-06148     America/Antigua
:
JP      +353916+1394441 Asia/Tokyo

これ、リナとかのOSをインストールする時に(裏で)お世話になってるテーブル。そう、時刻を設定する時に、地図が出てくるじゃないですか。そのデータだ。

東京の北緯は、35度41分、東経139度45分(@千代田区役所)らしいですから、まあそんなものでしょう。 あっ、日本標準時に比べて、19分進んでいるそうですよ。国内の基準は兵庫県明石市です。

札幌と沖縄では、時差が1時間近くも有るのね。

etc

日本の東西南北端点の経度緯度について

フーリエ級数実演

任意の波形が、sin/cos波で合成出来るという検証環境。なかなか面白い。