简体   繁体   English

为什么我的 Go 应用程序没有像 busybox `cat` 命令那样从 sysfs 中读取数据?

[英]Why is my Go app not reading from sysfs like the busybox `cat` command?

Go 1.12 on Linux 4.19.93 armv6l . Linux Linux 4.19.93 armv6l上的 Go 1.12。 Hardware is a raspberypi zero w (BCM2835) running a yocto linux image.硬件是运行 yocto linux 映像的树莓派零 w (BCM2835)。

I've got a gpio driven SRF04 proximity sensor driven by the srf04 linux driver.我有一个由 srf04 linux 驱动程序驱动的 gpio 驱动的 SRF04 接近传感器。

It works great over sysfs and the busybox shell.它在 sysfs 和 busybox shell 上工作得很好。

# cat /sys/bus/iio/devices/iio:device0/in_distance_raw
1646

I've used Go before with IIO devices that support triggers and buffered output at high sample rates on this hardware platform.我之前使用过 Go 和支持触发器的 IIO 设备,并在此硬件平台上以高采样率缓冲 output。 However for this application the srf04 driver doesn't implement those IIO features.然而,对于这个应用程序,srf04 驱动程序没有实现这些 IIO 功能。 Drat.德拉特。 I don't really feel like adding buffer / trigger support to the driver myself (at this time) since I do not have a need for a 'high' sample rate.我真的不想自己(此时)为驱动程序添加缓冲区/触发器支持,因为我不需要“高”采样率。 A handful of pings per second should suffice for my purpose.每秒几次 ping 就足以满足我的目的。 I figure I'll calculate mean & std.我想我会计算平均值和标准值。 dev.开发。 for a rolling window of data points and 'divine' the signal out of the noise.对于数据点的滚动 window 并从噪声中“区分”信号。

So with that - I'd be perfectly happy to Read the bytes from the published sysfs file with Go.因此,我非常乐意使用 Go 从已发布的 sysfs 文件中读取字节。

Which brings me to the point of this post.这让我想到了这篇文章。 When I open the file for reading, and try to Read() any number of bytes, I always get a generic -EIO error.当我打开文件进行读取并尝试 Read() 任意数量的字节时,我总是会得到一个通用的-EIO错误。

func (s *Srf04) Read() (int, error) {
    samp := make([]byte, 16)

    f, err := os.OpenFile(s.readPath, OS.O_RDONLY, os.ModeDevice)
    if err != nil {
        return 0, err
    }
    defer f.Close()

    n, err := f.Read(samp)
    if err != nil {
        // This block is always executed.
        // The error is never a timeout, and always 'input/output error' (-EIO aka -5)
        log.Fatal(err)
    }
    ...
}

This seems like strange behavior to me.这对我来说似乎是奇怪的行为。 So I decided to mess with using io.ReadFull .所以我决定搞乱使用io.ReadFull This yielded unreliable results.这产生了不可靠的结果。

func (s *Srf04) Read() (int, error) {
    samp := make([]byte, 16)

    f, err := os.OpenFile(s.readPath, OS.O_RDONLY, os.ModeDevice)
    if err != nil {
        return 0, err
    }
    defer f.Close()

    for {
        n, err := io.ReadFull(readFile, samp)
        log.Println("ReadFull ", n, " bytes.")
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Println(err)
        }
    }

    ...

}

I ended up adding it to a loop, as I found behavior changes from 'one-off' reads to multiple read calls subsequent to one another.我最终将它添加到一个循环中,因为我发现从“一次性”读取到多个读取调用的行为发生了变化。 I have it exiting if it gets an EOF, and repeatedly trying to read otherwise.如果它得到一个 EOF,我让它退出,否则我会反复尝试读取。

The results are straight-up crazy unreliable, seemingly returning random results.结果直接疯狂不可靠,似乎返回随机结果。 Sometimes I get the -5, other times I read between 2 - 5 bytes from the device.有时我得到 -5,有时我从设备读取 2 - 5 个字节。 Sometimes I get bytes without an eof file before the EOF.有时我在 EOF 之前得到没有 eof 文件的字节。 The bytes appear to represent character data for numbers (each rune is a rune between [0-9]) -- which I'd expect.字节似乎代表数字的字符数据(每个符文是 [0-9] 之间的符文) - 这是我所期望的。

Aside: I expect this is related to file polling and the go blocking IO implementation, but I have no way to really tell.旁白:我希望这与文件轮询和 go 阻止 IO 实现有关,但我无法真正说出。

As a temporary workaround, I decided try using os.exec, and now I get results I'd expect to see.作为临时解决方法,我决定尝试使用 os.exec,现在我得到了我期望看到的结果。

func (s *Srf04)Read() (int, error) {
    out, err := exec.Command("cat", s.readPath).Output()
    if err != nil  {
       return 0, err
    }
    return strconv.Atoi(string(out))
}

But Yick.但是伊克。 os.exec . os.exec Yuck.呸。

I'd try to run that cat whatever encantation under strace and then peer at what read(2) calls cat actually manages to do (including the number of bytes actually read), and then I'd try to re-create that behaviour in Go.我会尝试在strace下运行该cat whatever咒语,然后read(2)调用cat实际执行的操作(包括实际读取的字节数),然后我会尝试重新创建该行为Go。

My own sheer guess at the problem's cause is that the driver (or the sysfs layer) is not too well prepared to deal with certain access patterns.我自己对问题原因的猜测是驱动程序(或 sysfs 层)没有准备好处理某些访问模式。

For a start, consider that GNU cat is not a simple-minded byte shoveler but is rather a reasonably tricky piece of software, which, among other things, considers optimal I/O block sizes for both input and output devices (if available), calls fadvise(2) etc. It's not that any of that gets actually used when you run it on your sysfs-exported file, but it may influence how the full stack (starting with the sysfs layer) performs in the case of using cat and with your code, respectively.首先,考虑一下GNU cat不是一个头脑简单的字节铲子,而是一个相当棘手的软件,除其他外,它考虑了输入和 output 设备(如果可用)的最佳 I/O 块大小,调用fadvise(2)等。当您在 sysfs 导出的文件上运行它时,并没有实际使用其中的任何一个,但它可能会影响整个堆栈(从 sysfs 层开始)在使用cat和分别使用您的代码。

Hence my advice: start with strace -ing the cat and then try to re-create its usage pattern in your Go code;因此我的建议是:从strace开始,然后尝试在您的cat代码中重新创建它的使用模式; then try to come up with a minimal subset of that, which works;然后尝试提出其中的一个最小子集,这是可行的; then profoundly comment your code;-)然后深刻地评论你的代码;-)

I'm sure I've been looking at this too long tonight, and this code is probably terrible.我敢肯定我今晚看这个太久了,这段代码可能很糟糕。 That said, here's the snippet of what I came up with that works just as reliably as the busybox cat , but in Go.也就是说,这是我想出的片段,它与 busybox cat一样可靠,但在 Go 中。

The Srf04 struct carries a few things, the important bits are included below: Srf04 结构包含一些东西,重要的位包括如下:

type Srf04 struct {
    readBuf     []byte `json:"-"`
    readFile    *os.File `json:"-"`
    samples     *ring.Ring `json:"-"`
}


func (s *Srf04) Read() (int, error) {
/** Reliable, but really really slow.
    out, err := exec.Command("cat", s.readPath).Output()

    if err != nil {
        log.Fatal(err)
    }

    val, err := strconv.Atoi(string(out[:len(out) - 2]))
    if err == nil {
        s.samples.Value = val
        s.samples = s.samples.Next()
    }
 */
    // Seek should tell us the new offset (0) and no err.
    bytesRead := 0
    _, err := s.readFile.Seek(0, 0)

    // Loop until N > 0 AND err != EOF && err != timeout.
    if err == nil {
        n := 0
        for {
            n, err = s.readFile.Read(s.readBuf)
            bytesRead += n
            if os.IsTimeout(err) {
                // bail out.
                bytesRead = 0
                break
            }
            if err == io.EOF {
                // Success!
                break
            }
            // Any other err means 'keep trying to read.'
        }
    }

    if bytesRead > 0 {
        val, err := strconv.Atoi(string(s.readBuf[:bytesRead-1]))
        if err == nil {
            fmt.Println(val)
            s.samples.Value = val
            s.samples = s.samples.Next()
        }
        return val, err
    }

    return 0, err
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM