関連記事
Goメモ-447 (mkfifoメモ)(1)(create) - いろいろ備忘録日記
Goメモ-448 (mkfifoメモ)(2)(read) - いろいろ備忘録日記
Goメモ-449 (mkfifoメモ)(3)(write) - いろいろ備忘録日記
Goメモ-450 (mkfifoメモ)(4)(read-nonblocking) - いろいろ備忘録日記
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。忘れないうちにメモメモ。。。
Go側から名前付きパイプ(mkfifo)を扱うことがあったので、忘れない内にメモしておこうと思いました。
今回は名前付きパイプファイルからの書き込み(ノンブロッキングモード)について。
なお、標準ライブラリにある syscall
パッケージの Mkfifo()
でも行えるのですが
サンプルでは sys/unix の方を使っています。
サンプル
//go:build linux package main import ( "bufio" "errors" "flag" "fmt" "log" "os" "sync" "syscall" "time" "golang.org/x/sys/unix" ) var ( fname string ) func init() { log.SetFlags(log.Lmicroseconds) flag.StringVar(&fname, "fname", "", "FIFO file name") flag.Parse() } func main() { if err := run(); err != nil { log.Fatal(err) } } func run() error { // // 名前付きパイプをノンブロッキングモードで開く // 明示的なノンブロッキングモードの指定は os.OpenFile() では行えないため // golang.org/x/sys/unix を利用する // var ( fd int f *os.File err error ) log.Println("[Before] unix.Open(unix.O_WRONLY|unix.O_NONBLOCK)") // 名前付きパイプを書込みでノンブロッキングモードで開く場合に O_WRONLY を指定すると // 読み込み側が開かれていない場合 `ENXIO(no such device or address)` が発生する。 // // 逆に読み込みでノンブロッキングモードで開く場合はここではエラーとならず // 実際に読み込む際に書込みが行われていない場合に即EOFが返ることになる。 // ノンブロッキングリードする処理は ../read-nonbloking/ を参照のこと。 // // 読み込みと書込みでエラーが発生する箇所が異なる点に注意。 // // REFERENCES // - https://qiita.com/seriru13/items/39ed2431dfd959ad512e for { fd, err = unix.Open(fname, unix.O_WRONLY|unix.O_NONBLOCK, 0666) if err != nil { var sysErr syscall.Errno if errors.As(err, &sysErr) && sysErr == unix.ENXIO { log.Printf("[ENXIO] %s", sysErr) <-time.After(200 * time.Millisecond) continue } return err } break } f = os.NewFile(uintptr(fd), fname) if f == nil { return fmt.Errorf("invalid file descriptor") } defer f.Close() // ここで f.Close() しているので、上で unix.Close(fd) は不要 log.Println("[After ] unix.Open(unix.O_WRONLY|unix.O_NONBLOCK)") // // データを書き込み // type ( data struct { numWrites int err error } ) var ( writer = bufio.NewWriter(f) results = make(chan data) timeout = 1500 * time.Millisecond done = make(chan struct{}) wg sync.WaitGroup ) wg.Add(1) go func() { defer wg.Done() n, err := writer.WriteString("helloworld\n") if err == nil { err = writer.Flush() } results <- data{n, err} }() select { case r := <-results: if r.err != nil { return r.err } log.Printf("Write %d byte(s)", r.numWrites) case <-time.After(timeout): log.Println("timeout") } close(done) wg.Wait() return nil }
実行すると以下のようになります。
$ task task: [build] go build -o app . task: [create-fifo] rm -f ./tmp-fifo task: [create-fifo] mkfifo ./tmp-fifo -m0666 task: [run] ./app -fname ./tmp-fifo & task: [run] sleep 1 05:32:05.279144 [Before] unix.Open(unix.O_WRONLY|unix.O_NONBLOCK) 05:32:05.279327 [ENXIO] no such device or address 05:32:05.479725 [ENXIO] no such device or address 05:32:05.680111 [ENXIO] no such device or address 05:32:05.880493 [ENXIO] no such device or address 05:32:06.080974 [ENXIO] no such device or address task: [run] cat ./tmp-fifo 05:32:06.281373 [ENXIO] no such device or address 05:32:06.483128 [After ] unix.Open(unix.O_WRONLY|unix.O_NONBLOCK) 05:32:06.483383 Write 11 byte(s) helloworld
ノンブロッキングモードの場合、読み取りと書き込みでエラー判定を行う場所が異なることに注意が必要ですね。
参考情報
6.3 Named Pipes (FIFOs - First In First Out)
Ubuntu Manpage: mkfifo, mkfifoat - FIFOスペシャルファイル(名前付きパイプ)を作成する
Master the Linux ‘mkfifo’ Command: A Comprehensive Guide | by Peter Hou | Medium
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。