drvonr6573 2015-08-23 14:55
浏览 125
已采纳

在Go中获取管道状态

I'm unable to get a pipe state in Go (1.5).

While writing on a mkfifo created pipe, I try to get the state of this output pipe:

  • using the Write return status EPIPE
  • using the Write return status EPIPE and signal.Ignore on SIGPIPE (just in case)
  • using signal.Notify on SIGPIPE

I can see that:

  • EPIPE is never returned
  • when I use kill -13, the signal handler is called: "Got signal: broken pipe"
  • when I ctrl-c the reader, the signal handler is not called and my program exits with output: "signal: broken pipe"

Would you, please, indicate my error ?

// tee.go
package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"

    sys "golang.org/x/sys/unix"
)

// wait for a signal and print it
func handleSignal(csig chan os.Signal) {
    for {
        fmt.Println("Wait signal")
        s := <-csig
        fmt.Println("Got signal:", s)
    }
}

func main() {
    csig := make(chan os.Signal, 1)

    // `kill -13` outputs "Got signal: broken pipe" => ok
    signal.Notify(csig, sys.SIGPIPE)

    // OR disable the previous `Notify` just to be sure ?
    // maybe it will help to get the EPIPE error status on `Write` ?
    //signal.Ignore(sys.SIGPIPE)

    go handleSignal(csig)

    // open previously created named pipe (`mkfifo /tmp/test`)
    pipe, _ := os.OpenFile("/tmp/test", os.O_WRONLY, 0)

    for {
        _, err := pipe.Write([]byte("foo
"))
        if err == syscall.EPIPE {
            // never called => ko
            fmt.Println("EPIPE error")
        }
    }
}

Note: as a simple Go exercise, I try to implement a command which almost acts like tee -a <a_file> (print stdin to stdout and <a_file>) with the following specificity: non blocking write on a named pipe and optional reader.

  • 写回答

1条回答 默认 最新

  • donglefu6195 2015-08-23 17:23
    关注

    The error returned is not a plain syscall.Error but instead wrapped within *os.PathError as illustrated with the following variation of your code:

    package main
    
    import (
        "fmt"
        "os"
        "syscall"
    )
    
    func main() {
        // open previously created named pipe (`mkfifo /tmp/test`)
        pipe, _ := os.OpenFile("/tmp/test", os.O_WRONLY, 0)
        for {
            n, err := pipe.Write([]byte("foo
    "))
            fmt.Printf("write: n=%v, err=(%T) %[2]v
    ", n, err)
            if err == syscall.EPIPE {
                fmt.Println("EPIPE error")
            } else if perr, ok := err.(*os.PathError); ok {
                fmt.Printf("op: %q; path=%q; err=(%T) %[3]q
    ",
                    perr.Op, perr.Path, perr.Err)
                if perr.Err == syscall.EPIPE {
                    fmt.Println("os.PathError.Err is EPIPE")
                }
            }
        }
    }
    

    Running this after doing mkfifo /tmp/test; head /tmp/test elsewhere gives me:

    write: n=4, err=(<nil>) <nil>
    [… repeated nine more times, as the head command reads ten lines …]
    write: n=0, err=(*os.PathError) write /tmp/test: broken pipe
    op: "write"; path="/tmp/test"; err=(syscall.Errno) "broken pipe"
    os.PathError.Err is EPIPE
    [… above three lines repeated nine more times …]
    signal: broken pipe
    Exit 1
    

    After returning ten pipe errors on an individual file the Go runtine stops catching/blocking SIGPIPE and lets it through to your program killing it. I don't believe the Go runtime lets you catch or ignore SIGPIPE as it uses that signal itself internally.

    So two things: one, to look for syscall.EPIPE you need to check for *os.PathError as shown and two, don't continue on when err != nil when you haven't actually handled the error.

    I don't know the details of why Go handles SIGPIPE in this way; perhaps a search of Go's bug tracker and/or the go-nuts list may help answer that. With Go 1.5's additions to the os/signal package doing signal.Reset(syscall.SIGPIPE) (before any signal.Notify calls) changes this behaviour.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 微带串馈天线阵列每个阵元宽度计算
  • ¥15 关于无人驾驶的航向角
  • ¥15 keil的map文件中Image component sizes各项意思
  • ¥30 BC260Y用MQTT向阿里云发布主题消息一直错误
  • ¥20 求个正点原子stm32f407开发版的贪吃蛇游戏
  • ¥15 划分vlan后,链路不通了?
  • ¥20 求各位懂行的人,注册表能不能看到usb使用得具体信息,干了什么,传输了什么数据
  • ¥15 Vue3 大型图片数据拖动排序
  • ¥15 Centos / PETGEM
  • ¥15 划分vlan后不通了