関連記事
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 linuxpackage 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/39ed2431dfd959ad512efor { 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(chanstruct{}) wg sync.WaitGroup ) wg.Add(1) gofunc() { 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() returnnil }
実行すると以下のようになります。
$ 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]sleep1 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のおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。