Почему мое приложение Go не читает из sysfs, как команда busybox `cat`? - PullRequest
0 голосов
/ 27 апреля 2020

Go 1,12 Linux 4.19.93 armv6l. Аппаратное обеспечение - это расперипи ноль Вт (BCM2835) с изображением yocto linux.

У меня есть датчик приближения SRF04, управляемый gpio, управляемый драйвером srf04 linux.

Работает отлично подходит для sysfs и оболочки busybox.

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

Ранее я использовал Go с устройствами IIO, которые поддерживают триггеры и буферизованный вывод с высокой частотой дискретизации на этой аппаратной платформе. Однако для этого приложения драйвер srf04 не реализует эти функции IIO. Убирайся. Мне не очень хочется добавлять поддержку буфера / триггера к самому драйверу (в настоящее время), поскольку мне не нужна «высокая» частота дискретизации. Горстка пингов в секунду должно быть достаточно для моей цели. Я полагаю, я вычислю среднее и стандартное. девиация для скользящего окна точек данных и «божественного» сигнала из шума.

Итак, я был бы совершенно счастлив прочитать байты из опубликованного файла sysfs с помощью Go.

Что подводит меня к сути этого поста. Когда я открываю файл для чтения и пытаюсь прочитать () любое количество байтов, я всегда получаю общую ошибку c -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)
    }
    ...
}

Это похоже на странное поведение для меня. Поэтому я решил возиться с использованием io.ReadFull. Это дало ненадежные результаты.

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)
        }
    }

    ...

}

Я закончил тем, что добавил его в al oop, так как обнаружил изменения в поведении от одноразовых операций чтения к нескольким вызовам чтения после друг друга. У меня есть выход, если он получает EOF, и неоднократно пытаюсь читать иначе.

Результаты - безумные, ненадежные, казалось бы, возвращающие случайные результаты. Иногда я получаю -5, иногда я читаю от 2 до 5 байт с устройства. Иногда я получаю байты без файла eof перед EOF. Байты, по-видимому, представляют символьные данные для чисел (каждая руна - это руна между [0-9]) - что я и ожидал.

В сторону: я ожидаю, что это связано с опросом файлов и go блокирование реализации ввода-вывода, но я не могу точно сказать.

В качестве временного решения я решил попробовать использовать os.exe c, и теперь я получаю результаты, которые ожидаю увидеть.

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))
}

Но Йик. os.exec. Тьфу.

Ответы [ 2 ]

0 голосов
/ 30 апреля 2020

Я уверен, что слишком долго смотрел на это сегодня вечером, и этот код, вероятно, ужасен. Тем не менее, вот фрагмент из того, что я придумал, который работает так же надежно, как busybox cat, но в Go.

Структура 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
}
0 голосов
/ 27 апреля 2020

Я бы попытался запустить это cat whatever заклинание под strace, а затем взглянуть на то, что read(2) вызывает cat на самом деле удается сделать (включая количество фактически прочитанных байтов), и затем я бы попытался чтобы воссоздать это поведение в Go.

Я сам догадываюсь, что причина проблемы в том, что драйвер (или уровень sysfs) не слишком хорошо подготовлен для работы с определенными шаблонами доступа.

Для начала, рассмотрим, что GNU cat - не простой байтовый преобразователь, а довольно сложная часть программного обеспечения, которая, помимо прочего, учитывает оптимальные размеры блоков ввода / вывода для обоих устройств ввода и вывода (если доступно), звоните fadvise(2) et c. Не то, чтобы что-то из этого фактически использовалось, когда вы запускаете его в экспортированном sysfs-файле, но это может повлиять на работу полного стека (начиная со слоя sysfs) в случае использования cat и вашего кода соответственно .

Отсюда мой совет: начните с strace -ing cat, а затем попробуйте заново создать шаблон его использования в вашем коде Go; затем попытайтесь найти минимальное подмножество того, что работает; тогда глубоко прокомментируйте ваш код; -)

...