less golang
Table of Contents
OpenBSD上のgolang
OpenBSD 7.5にアップグレードした。パッケージもアップレード。golangが最 新式に追従するかと思ったら、go1.21.1 openbsd/386 だった。 この際だから、goも最新式にしておく。まあ、CVE番号を貰うとversionがイン クリメントされるんで、何時までも、いたちごっこは続くんですけどね。
それより気になるのは、portsから入れたgoに利点は有るのか?
# We cannot assume that the machine running the built code will have SSE, # even though the machine building the package has SSE. As such, we need # to explicitly disable SSE on i386 builds. MAKE_ENV += GO386=softfloat
ふむ、貧乏な石でも動く様に配慮しましたとな。逆に言えば本家の奴は、そこ まで親切にするよりパフォーマンス重視で、評判を落としたくない方針か。こ れもまあ、本家と相談の上、相補関係の契約でも締結したのかな。
また、portsの方は、runtime,syscallに大量のパッチが当ててある。後、目立 ったこととして、go,gofmtが、/usr/local/bin配下に設置されてる事。わざわ ざPATHを通さなくてもgoが利用できる気配りがされている。
FreeBSDでケチケチ作戦
goのワークエリアを/tmpに追いやってDISKの保護に努める。
sakae@fb:~ $ cat .profile : export GOCACHE='/tmp/.cache/go-build' export GOPATH='/tmp/go'
どうせFreeBSDを起動したら、そのままの状態ですから。。/tmpはRAMDISKの運 用をしてるしね。
go doc/asm.html
ふとgoの塊を見ていたら、doc/asm.htmlなんてのを発見してしまった。面白そ うなので、main.goなんてのを造って戯れ。変数を作成しておいて、利用しな いと、コンパイルエラーになるんで、やむなくPrintfしといたけど、この頑固 さ、どうにかならないか。importしといて、利用しないのもしかりだ。
import "time"
import "fmt"
func main(){
foo := time.Now()
fmt.Printf("%v\n", foo)
}
この方法が上手くいった。
sakae@fb:/tmp/hoge $ go build -gcflags -S main.go
# command-line-arguments
main.main STEXT size=172 args=0x0 locals=0x44 funcid=0x0 align=0x0
0x0000 00000 (/tmp/hoge/main.go:4) TEXT main.main(SB), NOFRAME|ABIInternal, $68-0
0x0000 00000 (/tmp/hoge/main.go:4) MOVL (TLS), CX
0x0007 00007 (/tmp/hoge/main.go:4) CMPL SP, 8(CX)
0x000a 00010 (/tmp/hoge/main.go:4) PCDATA $0, $-2
0x000a 00010 (/tmp/hoge/main.go:4) JLS 162
0x0010 00016 (/tmp/hoge/main.go:4) PCDATA $0, $-1
0x0010 00016 (/tmp/hoge/main.go:4) SUBL $68, SP
0x0013 00019 (/tmp/hoge/main.go:4) FUNCDATA $0, gclocals·J5F+7Qw7O7ve2QcWC7DpeQ==(SB)
0x0013 00019 (/tmp/hoge/main.go:4) FUNCDATA $1, gclocals·ZUu3FE2uQqxA2rLzUxfsnw==(SB)
0x0013 00019 (/tmp/hoge/main.go:4) FUNCDATA $2, main.main.stkobj(SB)
0x0013 00019 (/tmp/hoge/main.go:5) PCDATA $1, $0
0x0013 00019 (/tmp/hoge/main.go:5) CALL time.Now(SB)
:
objdumpとかも、golang専用なのか。
sakae@fb:/tmp/hoge $ go tool objdump -s main.main main TEXT main.main(SB) /tmp/hoge/main.go main.go:4 0x80d2bd0 658b0dfcffffff MOVL GS:0xfffffffc, CX main.go:4 0x80d2bd7 3b6108 CMPL SP, 0x8(CX) main.go:4 0x80d2bda 0f8692000000 JBE 0x80d2c72 main.go:4 0x80d2be0 83ec44 SUBL $0x44, SP main.go:5 0x80d2be3 e8c816feff CALL time.Now(SB) main.go:5 0x80d2be8 8d7c2428 LEAL 0x28(SP), DI main.go:5 0x80d2bec 8d3424 LEAL 0(SP), SI :
いや、そんな事は無いだろう。多分Windows系とか、リナでも開発系を入れて いない人達向けに用意しましたって思える。
おまけで、以前作ったパケットを飛ばす奴を遡上に載せてみる。
sakae@fb:~/go/src/work $ nm a.out|grep main 081f3680 D go:main.inittasks 08139be0 T main.main 08139870 T main.tell_fb 08139b90 T main.tell_fb.deferwrap1 08111710 T net.absDomainName 081113f0 T net.isDomainName 0808a170 T runtime.main 080b61d0 T runtime.main.func1 0808a550 T runtime.main.func2 08170634 R runtime.mainPC 08223f0f B runtime.mainStarted 081f7b70 B runtime.main_init_done
sakae@fb:~/go/src/work $ nm a.out | wc
3902 11855 143796
sakae@fb:~/go/src/work $ nm a.out | grep runtime | wc
2273 6895 81847
どこでも動くの代償が、バイナリーの半分を占める実行環境に凝縮されている のかな。
まあ、こんなのが有りましたって事で、にわかに役立つ事は無さそうだ。
go doc binary
それに、対して、このコマンドは便利そう。題材をbinaryパッケージにして試 してみる。
sakae@fb:~ $ go doc binary
package binary // import "encoding/binary"
Package binary implements simple translation between numbers and byte sequences
and encoding and decoding of varints.
const MaxVarintLen16 = 3 ...
var BigEndian bigEndian
var LittleEndian littleEndian
var NativeEndian nativeEndian
:
func Write(w io.Writer, order ByteOrder, data any) error
type AppendByteOrder interface{ ... }
type ByteOrder interface{ ... }
特定の関数を詳しく確認する事も出来る。
sakae@fb:~ $ go doc binary.Write
package binary // import "encoding/binary"
func Write(w io.Writer, order ByteOrder, data any) error
Write writes the binary representation of data into w. Data must be a
fixed-size value or a slice of fixed-size values, or a pointer to such
data. Boolean values encode as one byte: 1 for true, and 0 for false.
Bytes written to w are encoded using the specified byte order and read from
successive fields of the data. When writing structs, zero values are written
for fields with blank (_) field names.
関数だけじゃなくて、typeなんかもOK。
package binary // import "encoding/binary"
type ByteOrder interface {
Uint16([]byte) uint16
Uint32([]byte) uint32
Uint64([]byte) uint64
PutUint16([]byte, uint16)
PutUint32([]byte, uint32)
PutUint64([]byte, uint64)
String() string
}
A ByteOrder specifies how to convert byte slices into 16-, 32-, or 64-bit
unsigned integers.
It is implemented by LittleEndian, BigEndian, and NativeEndian.
隠れている奴も晒してみる。
sakae@fb:/tmp $ go doc -u binary.PutUint64 package binary // import "encoding/binary" func (bigEndian) PutUint64(b []byte, v uint64) func (littleEndian) PutUint64(b []byte, v uint64) func (nativeEndian) PutUint64(b []byte, v uint64)
Geminiの名誉回復
前回ググル提供のAIであるgeminiを使ってみた。そして、けんもほろろな扱か いを受けた。心を入れ替えて、より具体的な質問をしてみる。なお、質問文に URLを入れちゃうと、私GUI判りませんと、拒絶されるので注意。
Q: golangについて質問します。 go doc binary すると、func Write(w io.Writer, order ByteOrder, data any) error とかは出現します。所がByteOrder型のメソッド(例、PutUint64)は表示されません。 何故ですか?
A1: Go言語の `encoding/binary` パッケージにおける `ByteOrder` 型は、メソッドを持たないインターフェースとして定義されています。そのため、`go doc binary` で表示されるのは、インターフェースを実装する具体的な型(`BigEndian` や `LittleEndian` など)のメソッドのみとなります。
`ByteOrder` 型自体は、以下の役割を担っています。
- バイトスライスを 16 ビット、32 ビット、64 ビットの符号なし整数に変換する方法を定義します。
- バイトスライスから整数を読み込む際のバイト順序を決定します。
具体的な処理は、`BigEndian` や `LittleEndian` などの型によって実装されます。これらの型は、`ByteOrder` インターフェースを実装することで、`PutUint64` や `GetUint64` などのメソッドを提供します。
つまり、`ByteOrder` 型自体はメソッドを持たないものの、`BigEndian` や `LittleEndian` などの具体的な型を通して、必要な処理を実行することができます。
以下に、`BigEndian` 型を用いた例を示します。
package main
import (
"encoding/binary"
"fmt"
)
func main() {
var n uint64 = 0x1234567890abcdef
buf := make([]byte, 8)
// BigEndian 型を使用して n を buf に書き込む
binary.BigEndian.PutUint64(buf, n)
// buf から n を読み込む
n2 := binary.BigEndian.Uint64(buf)
fmt.Println(n, n2) // 出力: 1234567890abcdef 1234567890abcdef
}
上記のように、`ByteOrder` 型自体はメソッドを持たないものの、具体的な型を通して、バイト順序の変換や入出力処理を行うことができます。
A2: ## Go言語におけるByteOrder型のメソッドが表示されない理由
`go doc binary` コマンドで `func Write(w io.Writer, order ByteOrder, data any) error` などの関数は表示されるのに、`ByteOrder` 型のメソッド (例: `PutUint64`) は表示されない理由は、`ByteOrder` 型が インターフェース だからです。
インターフェースは、**型** ではなく 型の集合 を定義します。つまり、`ByteOrder` 型を実装する型は複数存在し、それぞれ異なるメソッドを持つ可能性があるのです。
そのため、`go doc` コマンドは、`ByteOrder` 型の 具体的なメソッド を特定して表示することができないのです。
### メソッドを確認する方法
`ByteOrder` 型のメソッドを確認するには、以下のいずれかの方法があります。
- 具体的な型の実装を確認する
`ByteOrder` 型を実装する具体的な型 (例: `bigEndian`) のドキュメントを確認することで、その型が持つメソッドを確認できます。
``` go doc encoding/binary.bigEndian ```
- インターフェースのすべてのメソッドを列挙する
`reflect` パッケージを利用することで、`ByteOrder` 型が持つすべてのメソッドを列挙することができます。
package main
import (
"encoding/binary"
"fmt"
"reflect"
)
type ByteOrder interface {
// ...
}
func main() {
var order ByteOrder = binary.BigEndian
t := reflect.TypeOf(order)
fmt.Println("ByteOrder interface methods:")
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
fmt.Println("-", m.Name)
}
}
### 補足
Go言語において、インターフェースと型の関係は以下の通りです。
- インターフェース: 型の集合を定義するもの。メソッドの宣言のみを含む。
- 型: インターフェースを実装するもの。インターフェースで宣言されたメソッドを実装する必要がある。
`ByteOrder` 型はインターフェースであり、`bigEndian` 型や `littleEndian` 型などの型が `ByteOrder` 型を実装しています。
これらの型はそれぞれ異なるメモリレイアウトでバイト列を解釈するため、異なるメソッドを提供しているのです。
sakae@fb:/tmp/go/src/work $ ./a.out ByteOrder interface methods: - AppendUint16 - AppendUint32 - AppendUint64 - GoString - PutUint16 - PutUint32 - PutUint64 - String - Uint16 - Uint32 - Uint64
何か深みにはまりそうだな。 ByteOrder interface{ … } なんてのが出てきてるけど、これってアレで言 う高階関数ぢゃん。
package main
import (
"bytes"
"encoding/binary"
"fmt"
)
func main() {
var buf bytes.Buffer
var value uint64 = 1234567890123456789
err := binary.Write(&buf, binary.BigEndian, value)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("%x\n", buf.Bytes())
}
gauche for FreeBSD
と言うことで、アレですよ。 久し振りに本家を確認。 Gauche
新らしくなってる。portsを確認すると
PORTNAME= gauche
PORTVERSION= 0.9.14
CATEGORIES= lang scheme
BROKEN_i386= "list.c", line 827 (ScmExtendedPairDescriptor *Scm__GetExtendedPairDescriptor(ScmObj)): Assertion failed: (z->hiddenTag&0x7) == 0x7
TLS_LIB_DEPENDS= libmbedtls.so:security/mbedtls
TLS_RUN_DEPENDS= ${LOCALBASE}/share/certs/ca-root-nss.crt:security/ca_root_nss
TLS_CONFIGURE_ON= --with-ca-bundle=${LOCALBASE}/share/certs/ca-root-nss.crt
TLS_CONFIGURE_OFF= --with-tls=no
残念な事に、i386版ではエラーになるみたい。ならば、どの版まで追従してる?
sakae@fb:~ $ pkg search gauche gauche-0.9.12 Scheme script interpreter with multibyte character handling gauche-gl-0.6_5 OpenGL binding for Gauche gauche-makiki-0.4 Simple multithreaded HTTP server written in Gauche gauche-readline-0.1_2 Pure gauche/scheme implementation of the Readline library
gauche on Debian
Goならわかるシステムプログラミング と対比させながら、gaucheならわかる システムプログラミング しましょ。
その前に、gaucheのインストール。./configure –with-tls=no で、面倒な奴 は、とりあえず除外した。
#!/usr/bin/env gosh
(use gauche.net)
(define (usage)
(display "Usage: swget url\n" (current-error-port))
(exit 1))
(define (parse-url url)
(rxmatch-let (rxmatch #/^http:\/\/([-A-Za-z\d.]+)(:(\d+))?(\/.*)?/ url)
(#f host #f port path)
(values host port path)))
(define (get url)
(receive (host port path) (parse-url url)
(call-with-client-socket
(make-client-socket 'inet host (string->number (or port "80")))
(lambda (in out)
(format out "GET ~a HTTP/1.0\r\n" path)
(format out "host: ~a\r\n\r\n" host)
(flush out)
(copy-port in (current-output-port))))))
(define (main args)
(if (= (length args) 2)
(get (cadr args))
(usage))
0)
ごちゃごちゃしてるけど、見所は、getって手続ね。
guile3.0 on OpenBSD
残念ながらOpenBSDでgaucheはコンパイル出来ない。何か他のものを。。。 7.2.11 Networking こんな案内が出てたので、guileをチョイスしたのさ。
Geiser + emacs 昔は、ターゲットが全部インストールされてしまったけれど、 世の中の流行SDGsに沿って、使う物だけ個別にインストールする様になってた。
(use-modules (ice-9 rdelim)) ;; for read-line
(let ((s (socket PF_INET SOCK_STREAM 0)))
(connect s AF_INET (inet-pton AF_INET "127.0.0.1") 8080)
(display "GET / HTTP/1.0\r\n\r\n" s)
(do ((line (read-line s) (read-line s)))
((eof-object? line))
(display line)
(newline)))
最初起動した時、read-lineが未定義って言われた。そんな馬鹿なと思ったけ ど、きっとモジュール定義が必要なんだろうと検討をつけた。どのモジュール かな? そんな時は、C-c C-d i すれば、infoから見付け出して説明が出てく る。そしたら、それを前に辿っていけば、モジュール名が出てくる。なかなか 便利な仕掛けを提供してるな。geiser偉いぞ。
Linux vs. *BSD
Q: Linuxが流行して、*BSDの人気が無いのは、何故ですか?
A: Linuxと*BSD(Berkeley Software Distribution)は、オープンソースのUnix系オペレーティングシステムであり、両者は多くの面で似ていますが、Linuxが圧倒的に人気があるのはいくつかの理由があります。以下に、その主な要因を詳しく説明します。
- カーネルの普及度とサポート:
- Linuxは、1991年にLinus Torvaldsによって開発されたカーネルで、その後、GNUプロジェクトのツールと組み合わせてGNU/Linuxとして発展しました。この歴史的背景により、Linuxは多数のディストリビューション(ディストロ)を持ち、広範囲なサポートとコミュニティが存在します。
- 一方で、*BSDはもともと1980年代にBSD UNIXとして開発が始まりましたが、その後のライセンス問題や開発の停滞などで、Linuxほどの普及度やサポートを得られていません。
- ライセンスの違い:
- LinuxはGPL(GNU General Public License)やMITライセンスなどのフリーなライセンスの下で配布されています。これにより、商用・非商用を問わず自由に利用、改変、配布することができます。
- 一方、*BSDはBSDライセンスを採用しています。これはオープンソースライセンスですが、商用製品に組み込む際のライセンス条項が比較的緩やかであるため、ソースコードを閉じたまま利用する企業が増えることもあります。
- マーケティングとブランドイメージ:
- Linuxは多数の大手企業(例:Red Hat, Canonical)によるサポートやマーケティングがあり、一般的な知名度が高いです。これに対して、*BSDはこれらの面でのサポートが限られており、一般にはあまり知られていません。
- ハードウェアサポート:
- Linuxは広範なハードウェアに対するサポートを持っており、新しいデバイスやアーキテクチャにも迅速に対応します。
- *BSDも確かに広範なサポートを持っていますが、Linuxほどの開発速度やハードウェアサポートは期待しにくい面があります。
- コミュニティと開発の活発さ:
- Linuxコミュニティは非常に大きく、開発者やユーザーが世界中に広がっています。新しい技術やアイディアが日々生まれ、開発が活発に行われています。
- *BSDのコミュニティも熱心ですが、規模が小さく、開発のペースも比較的遅めです。
- 教育とトレーニングの提供:
- Linuxには多くの教育資料、書籍、オンラインコースが存在し、初心者からエキスパートまでの学習が可能です。
- *BSDの教育資料は限られており、学習のためのリソースが少ないことが一因となっています。
結論として、Linuxの普及度と人気は、その開発の歴史、ライセンス、マーケティング、ハードウェアサポート、コミュニティ、教育資料など、多くの要因によって形成されています。*BSDは技術的には優れたオペレーティングシステムであり、特定の用途やニーズには非常に適していますが、これらの要因が組み合わさることで、Linuxが一般的により広く使われていると言えます。