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 写一个方法checkPerson,入参实体类Person,出参布尔值
  • ¥15 我想咨询一下路面纹理三维点云数据处理的一些问题,上传的坐标文件里是怎么对无序点进行编号的,以及xy坐标在处理的时候是进行整体模型分片处理的吗
  • ¥15 CSAPPattacklab
  • ¥15 一直显示正在等待HID—ISP
  • ¥15 Python turtle 画图
  • ¥15 关于大棚监测的pcb板设计
  • ¥15 stm32开发clion时遇到的编译问题
  • ¥15 lna设计 源简并电感型共源放大器
  • ¥15 如何用Labview在myRIO上做LCD显示?(语言-开发语言)