more golang
inetd of FreeBSD
前回、Windowsでダエモン君になる方法を調べていて、半ば匙を投げてしまっ た感がある、FreeBSDの時刻同期だが、inetd側だけでも完成しておきたい。
/etc/services
trgt 59595/udp # trgger time
サービスを登録。取りあえず、ゴクゴクゴとパケットを吸収してくれる様にポー ト番号を決めた。名前も番号も、他とバッティングしていなければ何でも良い。
/etc/inet.conf
trgt dgram udp wait root internal
次は、上記のサービスをinetdの内蔵処理とする様に設定。
sakae@fb:/usr/src/usr.sbin/inetd $ diff builtins.c.org builtins.c 60a61 > static void trgt_dg(int, struct servtab *); 74a76,78 > /* Trgt received data */ > { "trgt", SOCK_DGRAM, 0, 1, trgt_dg }, > 816a821,836 > static void > trgt_dg(int s, struct servtab *sep __unused) > { > char buffer[65536]; /* Should be sizeof(max datagram). */ > int i; > socklen_t size; > struct sockaddr_storage ss; > > size = sizeof(ss); > if ((i = recvfrom(s, buffer, sizeof(buffer), 0, > (struct sockaddr *)&ss, &size)) < 0) > return; > > execl("/usr/sbin/ntpdate", "ntpdate", "-s", "ntp.nict.jp", NULL); > }
OpenBSDと違って、内蔵の処理は、独立したファイルになっていた。登録する
関数のシグネチャを宣言。コマンド・テーブルへの登録。そして最後は、その
関数の定義。*sepは関数内で使用していないので、その旨を __unused
宣言
しておかないとコンパイルエラーになる。
その癖、受信したパケットは利用していないのに、お目こぼしに預かっている。 本来は、パケットを数値に変換してからctime()あたりで得た数値との差分を 取り、余りにもずれていたら、補正をしろ、だろう。
今回は、それを省略しちゃって、いきなり時刻補正コマンドを起動してる。-s オプションは、記録をsyslogに転送。だって、ダエモン君は端末が無いですか ら。時刻の供給元は、コード内に決め打ちと言う暴挙です。本来なら、それ用 の設定ファイルから取り込むべきでしょうね。
それをやらなかったのは、手抜きです。いや、国家が運用してるサーバーで、 ちゃんと複数台の専用ハードになっていますんで安心したんです。 NICT公開NTP FAQ
sakae@fb:~ $ host ntp.nict.jp ntp.nict.jp has address 133.243.238.163 ntp.nict.jp has address 133.243.238.244 ntp.nict.jp has address 133.243.238.164 ntp.nict.jp has address 133.243.238.243 ntp.nict.jp has address 61.205.120.130 ntp.nict.jp has IPv6 address 2001:df0:232:eea0::fff4 ntp.nict.jp has IPv6 address 2001:df0:232:eea0::fff3 ntp.nict.jp has IPv6 address 2001:ce8:78::2
/var/log/messages
Apr 10 16:37:56 fb ntpdate[1260]: adjust time server 133.243.238.164 offset +0.114025 sec
sakae@fb:~ $ ls /usr/obj/usr/src/i386.i386/usr.sbin/inetd/ builtins.o inetd.8.gz inetd.full* inetd* inetd.debug* inetd.o
尚、FreeBSDの場合は、コンパイル中のオブジェクトは、こんな場所に作成さ れ、ソース・ツリー内は、クリーンに保たれてます。
new error
inetdも準備できたんで、冬眠からの起床実験。
C:\Users\sakae>wtick.exe 2024-04-10 16:37:49.7925334 +0900 JST m=+60.010312201 2024-04-11 05:49:28.2277946 +0900 JST m=+47558.445573501 panic: dial udp4: lookup fb: getaddrinfow: The requested name is valid, but no data of the requested type was found. goroutine 1 [running]: main.tell_fb(0x6616fb58) /home/sakae/go/src/tick/tick.go:12 +0x13a main.main() /home/sakae/go/src/tick/tick.go:29 +0x15a
何と言う事か、新種のエラーがWindows側で発生。現場はここ。純然たる golang側の問題だな。
9 func tell_fb(t int64) { 10 conn, err := net.Dial("udp4", "fb:59595") 11 if err != nil { 12 panic(err) 13 }
src/net/lookup_windows.go
あたりが震源地ぽいけど、どうよ? このアプ
リの起動時には、発生していない。しかし、Windowsが休眠から覚めた時って
言う微妙な期間だからなあ。
そもそも、こういう微妙な事は止めて、素直に時刻を投げるだけのアプリに変 更する事だな。そして、それを1分間隔の、cronで実行するのが良かろう。そ うすれば、1回失敗したって、1分後の安定な時には、成功するだろう。
これ以上やっても、意味なさそうなんで、これにて中止。
work
golangのコードは、emacsで書いている。したら、emacs上からコンパイルやら 実行をしたい。そんな時は、makeですよ。Makefileですよ。どうせ巨大なコー ドを書く訳ではないので、ファイル名は、固定にした。おまけで、リリース用 にdebug情報を削除したバイナリーも作成できる様にした。
ob$ cat Makefile BINARY_NAME=a.out ob: main.go go build -o ${BINARY_NAME} -gcflags=all="-N -l" main.go run: ob ./${BINARY_NAME} rel: main.go go build -o REL -ldflags="-s -w" -trimpath clean: go clean rm -f ${BINARY_NAME} REL
下記は、実験コード
ob$ cat main.go package main import ( "fmt" _ "net" "time" ) func main(){ n := time.Now() a := n.Unix() fmt.Printf("%v\n%v\n", n, a) }
do make
sakae@atom:~/go/src/work$ make go build -o a.out -gcflags=all="-N -l" main.go sakae@atom:~/go/src/work$ make run go build -o a.out -gcflags=all="-N -l" main.go ./a.out 2024-04-11 16:05:48.786148901 +0900 JST m=+0.000112501 1712819148 sakae@atom:~/go/src/work$ make rel go build -o REL -ldflags="-s -w" -trimpath sakae@atom:~/go/src/work$ ls -l a.out REL -rwxr-xr-x 1 sakae sakae 2167284 Apr 11 16:04 a.out* -rwxr-xr-x 1 sakae sakae 1413380 Apr 11 16:04 REL*
普段は、makeしてコンパイルのエラーを修正。ちゃんと出来たら、ファイルの 贅肉を落とす。効果ありますねぇ。
sakae@atom:~/go/src/work$ file a.out a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=Fm-bqpkjCUplgLKyx6II/pAtDYkYDFQ-wkIgNODay/P....op_6pUD3zpHFAT, with debug_info, not stripped sakae@atom:~/go/src/work$ GOOS=windows make go build -o a.out -gcflags=all="-N -l" main.go sakae@atom:~/go/src/work$ file a.out a.out: PE32+ executable (console) x86-64, for MS Windows, 15 sections
Debina/WSL2だと、Windowsと地続きなので、簡単にWindows用のバイナリーが 作成できる。そして、実行も簡単。
sakae@atom:/mnt/c/Users/sakae$ mv ~/go/src/work/a.out xxxx.exe sakae@atom:/mnt/c/Users/sakae$ ./xxxx.exe 2024-04-11 16:16:32.7107262 +0900 JST m=+0.003971801 1712819792
with gdb
emacsから、直接gdbしちゃうのが今風だけど、あえてコマンド・プロンプトか ら起動してみる。
sakae@atom:~/go/src/work$ gdb -q a.out Reading symbols from a.out... Loading Go Runtime support. (gdb) b main.main Breakpoint 1 at 0x4c1433: file /home/sakae/go/src/work/main.go, line 9. (gdb) r Starting program: /home/sakae/go/src/work/a.out [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". [New Thread 0x7fffb13466c0 (LWP 3401)] [New Thread 0x7fffb0b456c0 (LWP 3402)] [New Thread 0x7fffab7fe6c0 (LWP 3404)] [New Thread 0x7fffabfff6c0 (LWP 3403)] [New Thread 0x7fffaaffd6c0 (LWP 3405)] [New Thread 0x7fffaa7fc6c0 (LWP 3406)] Thread 1 "a.out" hit Breakpoint 1, main.main () at /home/sakae/go/src/work/main.go:9 9 func main(){ (gdb) s 10 n := time.Now() (gdb) time.Now (~r0=...) at /usr/local/go/src/time/time.go:1131 1131 func Now() Time { (gdb) 1132 sec, nsec, mono := now() (gdb) time.now () at /usr/local/go/src/runtime/time_linux_amd64.s:14 14 TEXT time·now<ABIInternal>(SB),NOSPLIT,$16-24 (gdb) 15 MOVQ SP, R12 // Save old SP; R12 unchanged by C code. (gdb) 17 MOVQ g_m(R14), BX // BX unchanged by C code
runtimeの中は、アセンブラーも厭わないのか。
sakae@atom:/usr/local/go/src/runtime$ ls *linux* | grep amd64 defs_linux_amd64.go memmove_linux_amd64_test.go rt0_linux_amd64.s signal_linux_amd64.go sys_linux_amd64.s time_linux_amd64.s vdso_linux_amd64.go
我OpenBSDは、どうなっている。
sakae@atom:/usr/local/go/src/runtime$ wc `ls *openbsd* | grep 386` 181 419 2981 defs_openbsd_386.go 17 63 454 rt0_openbsd_386.s 47 233 1623 signal_openbsd_386.go 990 3630 20890 sys_openbsd_386.s 1235 4345 25948 total
ソースが付属してるって、楽しみがいが有るなあ。
go cache
色々ググってて、golangにもキャッシュが有る事を知った。
/home/sakae/.cache/go-build/README
This directory holds cached build artifacts from the Go build system. Run "go clean -cache" if the directory is getting too large. Run "go clean -fuzzcache" to delete the fuzz cache. See golang.org to learn more about Go.
余り使い込んでいないので、成長していない。
ob$ du -sh go-build/ 110M go-build/
これがrustになると、方針の違いで標準ライブラリィーは、極控え目。ユーザー に開発させて、盛り上げ様って方針。だから、何をやるにも、外部のクレート だかを取り寄せてコンパイル。お陰で、キャッシュが直ぐに肥大化する。
その点、golangは標準ライブラリィーが充実しているせいか、余り外部の物に 頼らなくても、何とかなりそう。
Gemini
ググるで思い出した。AIも進化してるらしいので現状確認。
Gemini google (旧 Bard)
Q: golangで、エンディアンの変換はどうやれば良いですか
A: Go言語でエンディアン変換を行う方法はいくつかあります。以下に代表的な方法をいくつか紹介します。
1. `encoding/binary` パッケージを使う
Go言語の標準ライブラリである `encoding/binary` パッケージには、エンディアン変換を行うための機能が提供されています。このパッケージを使う方法は以下のとおりです。
package main import ( "encoding/binary" "fmt" ) func main() { // エンディアン変換を行うデータ data := []byte{0x10, 0x27, 0x0, 0x0} // ビッグエンディアンからリトルエンディアンに変換 var value uint32 binary.Read(bytes.NewReader(data), binary.LittleEndian, &value) // 変換結果を出力 fmt.Println(value) // 出力: 10000 }
この例では、`binary.Read` 関数を使って、ビッグエンディアン形式で格納されたデータ `data` をリトルエンディアン形式に変換しています。
2. ビット演算を使う
エンディアン変換は、ビット演算を使って手動で行うこともできます。以下の例では、ビットシフト演算を使って、16ビットのビッグエンディアンデータをリトルエンディアンに変換しています。
package main import ( "fmt" ) func main() { // エンディアン変換を行うデータ data := uint16(0x2710) // ビットシフト演算を使ってエンディアン変換 value := (data>>8) | (data<<8) // 変換結果を出力 fmt.Println(value) // 出力: 10000 }
この他に、公開されてる私的なパッケージを使う方法が有るよ、と言いながら、 そのURLは無効でしたって。そんな説明は最初からオミットしてよね。全く機械仕掛け の、頭の回らない奴だ。誰か、日本のオ・モ・テ・ナ・シの心を教えてやれ。
それから、私的なパッケージが無効ってのが気になる。最近リナ方面を賑わし ている、xzパッケージみたいな事例も有るんで、なるべく私的な奴を使わない 事が大事だな。
そんじゃ、 前回ChatGPTに質問したものの、セカンド・オピニオンを実施。
Q: golangのコードについて、質問します。 下記は、int64の引数を、文字列にしてパケット送信する関数です。 これをバイナリーデータの転送にしたいです。どう書き換えたら 良いですか? テストコードも含めて、提示してください。
A: ## Golangコード:int64引数をバイナリーデータで送信
以下のコードは、` tell_fb
`関数を修正し、int64引数をバイナリーデータで送信するように書き換えたものです。さらに、テストコードも追加しています。
package main import ( "fmt" "net" ) func tellFbBinary(t int64) { conn, err := net.Dial("udp4", "fb:8888") if err != nil { panic(err) } defer conn.Close() // int64をバイナリーデータに変換 data := make([]byte, 8) binary.BigEndian.PutInt64(data, t) // バイナリーデータを送信 _, err = conn.Write(data) if err != nil { panic(err) } } func main() { // 送信するint64値 testValue := int64(1234567890) // バイナリーデータで送信 tellFbBinary(testValue) fmt.Println("送信完了:", testValue) }
変更点:
- `
tell_fb
` 関数を `tellFbBinary` に名前変更 - `fmt.Sprintf` を使用して文字列に変換する代わりに、`binary.BigEndian.PutInt64` を使用して int64 をバイナリーデータに変換
- `conn.Write([]byte(str))` を `conn.Write(data)` に変更して、バイナリーデータを送信
テストコード:
- `main` 関数で、送信する int64 値 (`testValue`) を定義
- `tellFbBinary` を呼び出して int64 値を送信
- 送信完了メッセージを出力
実行方法:
- 上記のコードを `
tell_fb_binary.go
` などのファイルに保存 以下のコマンドを実行してコードをコンパイル
go
build tell_fb_binary.go
以下のコマンドを実行してプログラムを実行
./tell_fb_binary
このコードを実行すると、int64 値がバイナリーデータで "fb:8888" アドレスに送信され、"送信完了: 1234567890" というメッセージが出力されます。
補足:
- ネットワークの輻輳やエラー処理などの考慮事項は省略されています。
- 実際の運用環境では、必要に応じてエラー処理やタイムアウト処理を追加する必要があります。
- 送信するデータ量が多い場合は、バッファリングやシリアル化などの手法を用いることも検討できます。
code中にビッグエンディアンが出てくるけど、それに対する強調説明が無いな。 また、受け取ったパケットの処理に対しても言及が無い。まあ、頼んでいない から、余計な事は言わないドライな回答。
関数名はスネーク・ケースじゃなくて、ラクダ式を推奨なのかな。さりげなく 修正されてる。
debug
ob$ make go build -o a.out -gcflags=all="-N -l" main.go # command-line-arguments ./main.go:17:5: undefined: binary
"encoding/binary" を、忘れているよ。
make -k go build -o a.out -gcflags=all="-N -l" main.go # command-line-arguments ./main.go:18:20: binary.BigEndian.PutInt64 undefined (type binary.bigEndian has no field or method PutInt64)
そんなの無いってさ。ChatGPTが提示してきた、 binary.BigEndian.PutUint64(data, uint64(t)) に変更して、やっと成功。ま だまだググルのAIは信用ならんね。
ChatGPT
久し振りにオブジェクト指向をやったら、言語学の事を知りたくなったので、 質問してみる。
Q: 言語学の質問です。日本語はその語順が、主語、目的語、述語です。それ に対して、英語では、主語、述語、目的語のようになっています。何故、語順 が言語によって違うのですか? 3000文字程度で、説明してください。
A: 言語の語順は、文の構造を形成する上での言語特有の構文的な要素であり、異なる言語間での差異は文法構造や情報伝達の方法に関連しています。このような語順の違いは、言語が発展してきた文化的、歴史的、社会的な要因、および認知的な要素に影響される可能性があります。以下に、日本語と英語の語順の違いについて考察します。
- 語族と文法構造の影響:
- 日本語は主にSOV型(Subject-Object-Verb)の語順を採用しています。これは、文の主語(が)、目的語(を)、そして述語(動詞)の順序です。これは日本語がアジアの言語で広く見られる語族である「日本語・朝鮮語・モンゴル諸語」に属することが関連しています。
- 英語は主にSVO型(Subject-Verb-Object)の語順を持っています。この語順は、英語がインド・ヨーロッパ語族に属し、この語族に属する多くの言語がSVO型を採用していることに関連しています。
- 文化的要因:
- 言語の発展には文化的な要素も関与しており、文化が言語の構造や使用法に影響を与えることがあります。日本の伝統的な文化や価値観は、物事を述べる際に主題や文脈を重視し、情報を後に置く傾向があります。これがSOV型語順を支えています。
- 英語圏では、情報を直截的に伝えることが重視される傾向があり、SVO型語順がその要諦に合致しています。
- 歴史的な変遷:
- 言語の変遷は、歴史的な出来事や他の言語との接触によっても影響を受けます。たとえば、日本語は過去に中国文化との接触があり、これが漢字の導入や語彙の影響を通じて言語に変容をもたらしました。しかし、語順に関しては、日本独自の特徴を保っています。
- 英語もまた、ラテン語や古フランス語との接触、およびノルマン・コンクエストなどの歴史的な出来事が、語彙や発音などの面で影響を与えましたが、基本的なSVO型語順は維持されています。
- 認知的要因:
- 言語は思考と深く結びついており、特定の語順は思考の流れにも影響を与える可能性があります。SOV型語順は、情報を順序立てて提供する際に、話し手が情報を整理しやすい特性を持っています。一方で、SVO型語順は直接的で分かりやすい伝達に適しています。
以上のような要因が組み合わさり、言語の語順が異なる形を取ると考えられま す。言語はその文化や歴史的背景と共に進化しており、その結果として異なる 言語体系が形成されています。
Q: 違う語順の言語って有るんですか?
A: はい、異なる語順を持つ言語は非常に多く存在します。言語の語順は、主語(S)、動詞(V)、目的語(O)を示すSVO、SOV、VSO、VOS、OVS、OSVなど、さまざまなパターンで表現されます。以下に、いくつかの異なる語順を持つ言語の例を挙げてみましょう。
- SOV型言語:
- 日本語、トルコ語、タガログ語など。
- SVO型言語:
- 英語、スペイン語、フランス語、ドイツ語など。
- VSO型言語:
- アイルランド語、アラビア語、ヘブライ語など。
- VOS型言語:
- マレー・ポリネシア語、ジブウェ語など。
- OVS型言語:
- ウアラム語、モホーク語など。
- OSV型言語:
- フランス領ポリネシアのマルキーズ諸島の一部の言語など。
これらの語順の違いは、言語が文を構築する際の基本的なルールや文法構造の一環です。これにより、言語が異なる情報の伝達や文の構築方法を選択することが可能となります。語順の違いは、文法的な意味論やコミュニケーションスタイルに影響を与え、文化や歴史的背景とも密接に結びついています。