いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-448 (mkfifoメモ)(2)(read)

関連記事

Goメモ-447 (mkfifoメモ)(1)(create) - いろいろ備忘録日記

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。忘れないうちにメモメモ。。。

Go側から名前付きパイプ(mkfifo)を扱うことがあったので、忘れない内にメモしておこうと思いました。

今回は名前付きパイプファイルからの読み込みについて。

なお、標準ライブラリにある syscall パッケージの Mkfifo() でも行えるのですが

サンプルでは sys/unix の方を使っています。

サンプル

//go:build linux

package main

import (
    "bufio"
    "flag"
    "log"
    "os"
    "time"
)

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() にて、モード指定で os.ModeNamedPipe を指定する
    //   os.O_RDOONLYで開くと読み取り専用となるが、この場合書込みが発生するまで
    //   ブロックされる。これはUNIXの名前付きパイプの挙動に従った動作である。
    //
    var (
        f   *os.File
        err error
    )

    log.Println("[Before] os.OpenFile(os.O_RDOONLY)")

    // 対象となる名前付きパイプに書込みが発生していない場合、ここでブロックされる。
    f, err = os.OpenFile(fname, os.O_RDONLY, os.ModeNamedPipe)
    if err != nil {
        return err
    }
    defer f.Close()

    log.Println("[After ] os.OpenFile(os.O_RDOONLY)")

    //
    // データを読み取り
    //
    type (
        data struct {
            value string
            err   error
        }
    )
    var (
        reader  = bufio.NewReader(f)
        lines   = make(chan data)
        timeout = time.Second
    )

    go func() {
        line, err := reader.ReadString('\n')
        lines <- data{line, err}
    }()

    select {
    case line := <-lines:
        if line.err != nil {
            return line.err
        }

        log.Println(line.value)
    case <-time.After(timeout):
        // 上記で記載した通り、os.O_RDOONLYのみで開いている場合
        // ブロッキングモードとなっているため、os.OpenFile()の呼び出しの
        // 時点でブロックされることとなる。つまり、実際にデータを読み出す
        // タイミングでは待たされることが無いため、このタイムアウトは通らない。
        log.Println("timeout")
    }

    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] (sleep 3; echo "helloworld" > ./tmp-fifo) &
task: [run] ./app -fname ./tmp-fifo
05:16:21.430030 [Before] os.OpenFile(os.O_RDOONLY)
05:16:24.430114 [After ] os.OpenFile(os.O_RDOONLY)
05:16:24.430175 helloworld

わざと書き込みを3秒ずらしてから行うようにしているので、os.OpenFile() の戻りが3秒後になっていますね。

参考情報

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のおすすめ書籍


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。