簡體   English   中英

在Go中獲取管道狀態

[英]Getting a pipe status in Go

我無法在Go(1.5)中獲得管道狀態。

在編寫由mkfifo創建的管道時,我嘗試獲取此輸出管道的狀態:

  • 使用Write返回狀態EPIPE
  • 使用Write返回狀態EPIPEsignal.Ignore在SIGPIPE上忽略(以防萬一)
  • 使用signal.Notify在SIGPIPE上signal.Notify

我知道:

  • 永不退還EPIPE
  • 當我使用kill -13 ,信號處理程序稱為:“收到信號:管道損壞”
  • 當我按ctrl-c讀取器時,未調用信號處理程序,並且我的程序退出,並顯示以下輸出:“ signal:broken pipe”

請您指出我的錯誤嗎?

// 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\n"))
        if err == syscall.EPIPE {
            // never called => ko
            fmt.Println("EPIPE error")
        }
    }
}

注意:作為一個簡單的Go練習,我嘗試實現一個幾乎與tee -a <a_file> (將stdin打印到stdout和<a_file> )類似的命令,其特定性如下: 非阻塞在命名管道和可選的reader上寫入

返回的錯誤不是普通的syscall.Error ,而是包裝在*os.PathError如代碼的以下變體所示:

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\n"))
        fmt.Printf("write: n=%v, err=(%T) %[2]v\n", 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\n",
                perr.Op, perr.Path, perr.Err)
            if perr.Err == syscall.EPIPE {
                fmt.Println("os.PathError.Err is EPIPE")
            }
        }
    }
}

在執行mkfifo /tmp/test; head /tmp/test之后運行此程序mkfifo /tmp/test; head /tmp/test mkfifo /tmp/test; head /tmp/test在其他地方給我:

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

在單個文件上返回十個管道錯誤后,Go runtine停止捕獲/阻止SIGPIPE,並將其傳遞到程序中將其殺死。 我不相信Go運行時會在內部使用SIGPIPE來捕獲或忽略它。

有兩件事:一,要查找syscall.EPIPE您需要檢查*os.PathError ,如圖所示;二,當您實際未處理錯誤時,當err != nil時不要繼續操作。

我不知道為什么Go用這種方式處理SIGPIPE的細節。 也許搜索Go的bug跟蹤器和/或go-nuts列表可能有助於回答這一問題。 在Go 1.5的os/signal包中添加了signal.Reset(syscall.SIGPIPE) (在任何signal.Notify調用之前)會更改此行為。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM