「Goならわかるシステムプログラミング」読み始めた。とりあえず1-3章まで読んだのでメモ。
Goならわかるシステムプログラミング(PDF版のみ) – 技術書出版と販売のラムダノート
Kindleで欲しかったんだけどなかったんで公式サイトからPDF形式を購入してみた。
1章
とりあえずGoやVSCodeのセットアップの説明から入って、そこからVSCodeにGoの拡張機能を入れてVSCodeのデバッガを使ってローレベルのコードを追っていく。
Hello Worldのコードを書いてVSCodeのbreakpointを貼ってデバッガを実行してStep IntoするとPrintlnメソッドの実装を確認できる。そこからさらにPrintlnの内部で使っているFprintlnの実装を読んで、、と進んでどんどんOSに近いレイヤーの実装を読むことができると言う内容。すごい。
func main() { fmt.Println("Hello World!") }
↓
func Println(a ...interface{}) (n int, err error) { return Fprintln(os.Stdout, a...) }
↓
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { p := newPrinter() p.doPrintln(a) n, err = w.Write(p.buf) p.free() return }
みたいな感じ。あとVS codeで関数にカーソル当ててF12でコードジャンプできるらしい。これも知らなかった。
2章
2-4章はOSを操作するための抽象化レイヤーの話で、2章がio.Write、3章がio.Reader、4章がchannelの話。
2章はデータの入出力がio.Writeというインターフェイスで抽象化されていると言う話で、まずGo言語の構造体とインターフェイスの説明から入って、io.Writerを利用しているGoの構造体の説明が続く。
インターフェイスはJavaにもあったけどそれと同じものだと考えて良いらしい。
// net.Connがio.Readerインタフェースでもあることを利用して、サーバーから返ってきたレスポンスを io.Copy を使って画面に出力 func main() { conn, err := net.Dial("tcp", "ascii.jp:80") if err != nil { panic(err) } io.WriteString(conn, "GET / HTTP/1.0/r/nHost: ascii.jp\r\nHost: ascii.jp\r\n\r\n") io.Copy(os.Stdout, conn) } // 複数の io.Writerを受け取り、それらすべてに対して、書き込まれた内容を同時に書き込む func main() { file, err := os.Create("multiwriter.txt") if err != nil { panic(err) } writer := io.MultiWriter(file, os.Stdout) io.WriteString(writer, "io.MultiWriter example\n") } // 書き込まれたデータをgzip圧縮して、あらかじめ渡されていたos.Fileに中継 func main() { file, err := os.Create("test.txt.gz") if err != nil { panic(err) } writer := gzip.NewWriter(file) writer.Header.Name = "test.txt" io.WriteString(writer, "gzip.Writer example\n") writer.Close() }
など。io.Writerというインターフェイスに「何ができるか」を定義して、いろんな構造体でデータの入出力を行うメソッドを定義するときに入出力の型がio.Writerの内容を満たしているかを確認していく。
ioutil.WriteFile()とかhttp.Get()もio.Writeが隠蔽されていてデータの入出力を行う関数を使う時はio.Writerやio.Readerを引数に持つように作るみたい。
3章
io.Readerの話。2章の内容の読み込み版だけど画像ファイル(png)、文字列、CSVファイルなどを読み込んでいく。
画像とかバイナリとか普段Web開発で触らないレイヤーの読み込みは楽しいかな。。
// バイナリの読み込み func main() { data := []byte{0x0, 0x0, 0x27, 0x10} var i int32 binary.Read(bytes.NewReader(data), binary.BigEndian, &i) fmt.Printf("data: %d\n", i) } // 画像読み込み func dumpChunk(chunk io.Reader) { var length int32 binary.Read(chunk, binary.BigEndian, &length) buffer := make([]byte, 4) chunk.Read(buffer) fmt.Printf("chunk '%v' (%d bytes)\n", string(buffer), length) } func readChunk(file *os.File) []io.Reader { var chunks []io.Reader file.Seek(8, 0) var offset int64 = 8 for { var length int32 err := binary.Read(file, binary.BigEndian, &length) if err == io.EOF { break } chunks = append(chunks, io.NewSectionReader(file, offset, int64(length)+12)) offset, _ = file.Seek(int64(length+8), 1) } return chunks } func main() { file, err := os.Open("lena.png") if err != nil { panic(err) } defer file.Close() chunks := readChunk(file) for _, chunk := range chunks { dumpChunk(chunk) } }
とりあえずここで区切り