向 exec.Cmd.StdinPipe() 写入超过 4K 字节

Posted

技术标签:

【中文标题】向 exec.Cmd.StdinPipe() 写入超过 4K 字节【英文标题】:Write more than 4K bytes to exec.Cmd.StdinPipe() 【发布时间】:2018-05-01 14:48:59 【问题描述】:

问题

在 Go 中将超过 4096 字节的数据写入 Cmd.StdinPipe 时,Windows 上的程序处理停止。在Linux上运行相同的代码,或者使用goroutine编写进程时,不会出现这种现象。

问题

处理将不会从下面显示的代码中的_, err = in.Write ([] byte '0')(4097 字节)开始。这是为什么呢?

为什么在 goroutine 或 Linux 上不会出现这种情况?

*** Golang参考描述Cmd.StdinPipe使用goroutine为an example,我的问题也解决了。这个问题源于对 Go 的好奇。

package main

import (
    "bytes"
    "fmt"
    "io"
    "log"
    "os/exec"
)

func main() 
    cmd := exec.Command("more")

    pype, err := cmd.StdinPipe()
    if err != nil 
        log.Fatal(err)
    

    bytes4k := generateBytes(1024 * 4) // Works on Linux, but not Windows.
    // bytes4k := generateBytes(1024 * 64) // Don't works on Linux and Windows.
    fmt.Println("bytes generated.")

    // go writeBytes(pype, bytes4k) // Works fine!
    writeBytes(pype, bytes4k) // Can't write. Write is locked.

    err = cmd.Run()
    if err != nil 
        log.Fatal(err)
    

    fmt.Println("finished.")


func generateBytes(num int) []byte 
    byte := bytes.NewBuffer(make([]byte, 0, num))
    for i := 0; i < num; i++ 
        byte.WriteByte('0')
    
    return byte.Bytes()


func writeBytes(in io.WriteCloser, bytes []byte) 
    defer in.Close()

    _, err := in.Write(bytes)
    if err != nil 
        log.Fatal(err)
    
    fmt.Println("written bytes to pipe.")

    _, err = in.Write([]byte'0') // Why this code stops at 4097 bytes?
    if err != nil 
        log.Fatal(err)
    
    fmt.Println("written 1 byte to pipe.")

经过验证的版本

go 版本 go1.10.1 windows/amd64 go 版本 go1.10.1 linux/amd64

【问题讨论】:

只要more 命令从管道中读取更多内容,它应该能够继续写入。 【参考方案1】:

如果管道中没有更多空间,则仅写入块。虽然 Windows 中管道的大小可能是 4k,但在 Linux 中要大得多。来自pipe(7):

... 自 Linux 2.6.11,管道容量为16页(即一个系统中65,536字节 页面大小为 4096 字节)...

因此,在 Linux 上写入没有人在读取的管道时,您可能会得到与在 Windows 上相同的结果,但您需要向管道中写入更多数据,直到达到这种情况。

【讨论】:

感谢您的回答!我明白为什么上面的代码在 Windows 上不能很好地工作。使用 goroutine 可以正常工作的原因是它在管道满之前被读取。我试着在 Linux 上写 65536 字节,然后那个程序就停止了,就像你说的那样!【参考方案2】:

这已经得到解答,如果您遇到此问题,您可能想要使用以下解决方法 - 改用缓冲写入器,可在 bufio 包中找到。这使您可以使用您可以完全控制的更大缓冲区来包装编写器。

【讨论】:

或者不是缓冲两次,而是在 goroutine 中编写它,如文档所示 是的,我用过这两种方法。如果你有大量的 writer,或者如果你正在多次编写,那么管理每个 writer 的 goroutine 可能会很困难——如果你多次快速地在 goroutine 中编写可能会导致竞争条件。此时,您需要改为移动到频道。缓冲区有时更简单。

以上是关于向 exec.Cmd.StdinPipe() 写入超过 4K 字节的主要内容,如果未能解决你的问题,请参考以下文章

关于Java中向文件写入数据的问题

java代码 如何向TXT文件写入内容?

如何用java向指定的Excel中写入数据

java 向数据库写入汉字的时候出现乱码

Android 怎样在应用程序中向文件里写入数据?

如何向Word中写入数据,C#.net