GolangにおけるバッファリングI/Oの確認
GolangにおけるバッファリングI/O
C言語でstdioライブラリを使用した場合、バッファリングされた入出力を行うのだが、goの場合は通常fmtで標準出力に出力するケースが多い。この場合バッファリングが行われるのかこの辺の理解が曖昧で、気になったので調べてみた。(そもそも論として、bufio
があるので大丈夫だとは思うのですが、気になったので調べてみたメモ。あとでdebugして確認したい)
Println
- Fprintlnをos.Stdoutを引数にして呼び出している
func Println(a ...interface{}) (n int, err error) { return Fprintln(os.Stdout, a...) }
Fprintln
- doPrintlnでbufを生成
- w.Writeで書き込み
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 }
os.Stdout
- wはStdoutなので
- osに定義がある
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
NewFile
func NewFile(fd uintptr, name string) *File { kind := kindNewFile if nb, err := unix.IsNonblock(int(fd)); err == nil && nb { kind = kindNonBlock } return newFile(fd, name, kind) }
Write
- f.writeで書き込んでいる
func (f *File) Write(b []byte) (n int, err error) { if err := f.checkValid("write"); err != nil { return 0, err } n, e := f.write(b) if n < 0 { n = 0 } if n != len(b) { err = io.ErrShortWrite } epipecheck(f, e) if e != nil { err = f.wrapErr("write", e) } return n, err }
f.write
- f.pfd.Writeで書き込みをしているようだ
func (f *File) write(b []byte) (n int, err error) { n, err = f.pfd.Write(b) runtime.KeepAlive(f) return n, err }
type file struct { pfd poll.FD name string dirinfo *dirInfo // nil unless directory being read nonblock bool // whether we set nonblocking mode stdoutOrErr bool // whether this is stdout or stderr }
poll.FD
- poll.FDはfile descriptorだとみていい
- os file か networkを扱うようだ
type FD struct { // Lock sysfd and serialize access to Read and Write methods. fdmu fdMutex // System file descriptor. Immutable until Close. Sysfd int // I/O poller. pd pollDesc // Writev cache. iovecs *[]syscall.Iovec // Semaphore signaled when file is closed. csema uint32 // Non-zero if this file has been set to blocking mode. isBlocking uint32 // Whether this is a streaming descriptor, as opposed to a // packet-based descriptor like a UDP socket. Immutable. IsStream bool // Whether a zero byte read indicates EOF. This is false for a // message based socket connection. ZeroReadIsEOF bool // Whether this is a file rather than a network socket. isFile bool }
FD.Write
- ここの
syscall.Write
で書き込んでいると考えられる - おそらくLinuxのsystem callのwriteを呼んでいるはずなのでこれはバッファリングはないはず
func (fd *FD) Write(p []byte) (int, error) { if err := fd.writeLock(); err != nil { return 0, err } defer fd.writeUnlock() if err := fd.pd.prepareWrite(fd.isFile); err != nil { return 0, err } var nn int for { max := len(p) if fd.IsStream && max-nn > maxRW { max = nn + maxRW } n, err := syscall.Write(fd.Sysfd, p[nn:max]) if n > 0 { nn += n } if nn == len(p) { return nn, err } if err == syscall.EAGAIN && fd.pd.pollable() { if err = fd.pd.waitWrite(fd.isFile); err == nil { continue } } if err != nil { return nn, err } if n == 0 { return nn, io.ErrUnexpectedEOF } } }