関連記事
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。
syscallパッケージやunixには SyscallNoError という関数が用意されています。
これはsyscall(2)のラッパーで、かつ、エラーを返しません。
エラーが発生しないと分かっている場合は利用できますね。
サンプル(間接システムコールの呼び出し)
main.go
package main import ( "encoding/binary" "log" "net" "time" "unsafe" "golang.org/x/sys/unix" ) func main() { log.SetFlags(0) if err := run(); err != nil { log.Fatal(err) } } func run() error { // // TCPリスナーの起動までを Syscall() で実装 // なお、利用する関数は SyscallNoError (エラーを返さないバージョン) を意図的に利用 // const ( zero = uintptr(0) // 不要な引数値を表す ) // socket(2) var ( domain = uintptr(unix.AF_INET) sockType = uintptr(unix.SOCK_STREAM) protocol = uintptr(0) sFd, _ = unix.SyscallNoError(unix.SYS_SOCKET, domain, sockType, protocol) ) defer func(fd uintptr) { _, _ = unix.SyscallNoError(unix.SYS_CLOSE, fd, zero, zero) }(sFd) // setsockopt(2) (SO_REUSEADDR) var ( level = uintptr(unix.SOL_SOCKET) optname = uintptr(unix.SO_REUSEADDR) optval = 1 optvalPtr = uintptr(unsafe.Pointer(&optval)) optlen = uintptr(unsafe.Sizeof(optval)) ) _, _, _ = unix.Syscall6(unix.SYS_SETSOCKOPT, sFd, level, optname, optvalPtr, optlen, zero) // ソケットアドレス生成 // アドレスを表現する構造体として unix.SockaddrInet4 と unix.RawSockaddrInet4 の2つがあるが // unix.Syscall()を利用して直接システムコールを呼び出す場合は、カーネルが期待するメモリレイアウトを // そのまま表現する unix.RawSockaddrInet4 を利用する。(要はCの構造体と同じ形の方を使う) // unix.RawSockaddrInet4の方は、ネットワークバイトオーダーで値を保持する。 var ( sAddr unix.RawSockaddrInet4 sAddrPtr uintptr sAddrLen uintptr ) sAddr = unix.RawSockaddrInet4{ Family: unix.AF_INET, Port: htons(8888), Addr: [4]byte{127, 0, 0, 1}, } sAddrPtr = uintptr(unsafe.Pointer(&sAddr)) sAddrLen = uintptr(unix.SizeofSockaddrInet4) // bind(2) _, _ = unix.SyscallNoError(unix.SYS_BIND, sFd, sAddrPtr, sAddrLen) // listen(2) _, _ = unix.SyscallNoError(unix.SYS_LISTEN, sFd, uintptr(5), zero) for { // accept(2) var ( cAddr unix.RawSockaddrInet4 cAddrPtr = uintptr(unsafe.Pointer(&cAddr)) cAddrLen uint32 = unix.SizeofSockaddrInet4 cAddrLenPtr = uintptr(unsafe.Pointer(&cAddrLen)) cFd, _ = unix.SyscallNoError(unix.SYS_ACCEPT, sFd, cAddrPtr, cAddrLenPtr) ) log.Printf("[accept] EP: %v:%d", net.IP(cAddr.Addr[:]), ntohs(cAddr.Port)) go func(fd uintptr) { time.Sleep(1 * time.Second) _, _ = unix.SyscallNoError(unix.SYS_CLOSE, fd, zero, zero) }(cFd) } } // ホストバイトオーダーからネットワークバイトオーダーに変換(Host to Network Short) func htons(port uint16) uint16 { bytes := make([]byte, 2) binary.LittleEndian.PutUint16(bytes, port) return binary.BigEndian.Uint16(bytes) } // ネットワークバイトオーダーからホストバイトオーダーに変換(Network to Host Short) func ntohs(port uint16) uint16 { bytes := make([]byte, 2) binary.BigEndian.PutUint16(bytes, port) return binary.LittleEndian.Uint16(bytes) }
Taskfile.yml
# https://taskfile.dev version: '3' tasks: default: cmds: - task: run_noerror run_noerror: cmds: - rm -f ./app_noerror - go build -o app_noerror main.go - go build -o app_client client/main.go - ./app_noerror & - sleep 1 - ./app_client - pkill app_noerror ignore_error: true
実行結果
$ task task: [run_noerror] rm -f ./app_noerror task: [run_noerror] go build -o app_noerror main.go task: [run_noerror] go build -o app_client client/main.go task: [run_noerror] ./app_noerror & task: [run_noerror] sleep 1 task: [run_noerror] ./app_client [accept] EP: 127.0.0.1:53418 task: [run_noerror] pkill app_noerror
参考情報
try-golang/examples/syscall/syscallnoerror at main · devlights/try-golang · GitHub
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。