Как использовать boost :: asio с Linux GPIO

У меня есть однопоточное приложение Linux, использующее boost :: asio для асинхронного ввода / вывода. Теперь мне нужно расширить это приложение для чтения входов GPIO на /sys/class/gpio/gpioXX/value,

Это можно сделать с помощью boost :: asio :: posix :: stream_descriptor на входах GPIO, инициируемых фронтом?

Я настроил вход GPIO следующим образом:

echo XX >/sys/class/gpio/export
echo in >/sys/class/gpio/gpioXX/direction
echo both >/sys/class/gpio/gpioXX/edge

Мне удалось написать epoll тестовое приложение, которое блокирует дескриптор файла GPIO до тех пор, пока не изменится сигнал GPIO, но boost::asio кажется, не в состоянии правильно блокировать. Вызов boost::asio::async_read всегда немедленно вызывает обработчик (конечно, только в пределах io_service.run()) либо с EOF, либо — в случае, если указатель файла был установлен обратно — 2 байта данных.

Я не эксперт в boost::asio внутренности, но может быть причина в том, что boost::asio Эполонный реактор запускается по уровню, а не по фронту в случае posix::stream_descriptor?

Вот мой код:

#include <fcntl.h>

#include <algorithm>
#include <iterator>
#include <stdexcept>

#include <boost/asio.hpp>

boost::asio::io_service io_service;
boost::asio::posix::stream_descriptor sd(io_service);
boost::asio::streambuf streambuf;

void read_handler(const boost::system::error_code& error, std::size_t bytes_transferred)
{
if (error.value() == boost::asio::error::eof) {
// If we don't reset the file pointer we only get EOFs
lseek(sd.native_handle(), 0, SEEK_SET);
} else if (error)
throw std::runtime_error(std::string("Error ") + std::to_string(error.value()) + " occurred (" + error.message() + ")");

std::copy_n(std::istreambuf_iterator<char>(&streambuf), bytes_transferred, std::ostreambuf_iterator<char>(std::cout));
streambuf.consume(bytes_transferred);
boost::asio::async_read(sd, streambuf, &read_handler);
}

int main(int argc, char *argv[])
{
if (argc != 2)
return 1;

int fd = open(argv[1], O_RDONLY);
if (fd < 1)
return 1;

try {
sd.assign(fd);
boost::asio::async_read(sd, streambuf, &read_handler);
io_service.run();
} catch (...) {
close(fd);
return 1;
}

close(fd);
return 0;
}

7

Решение

Насколько я знаю, это не возможно получить это конкретное поведение с Boost.Asio. Хотя ядро ​​помечает некоторые файлы в procfs и sysfs как подлежащие опросу, они не обеспечивают потоковое поведение, ожидаемое от boost::asio::posix::stream_descriptor и его операции.

Эполл-реактор Boost.Asio запускается по фронту (см. Boost.Asio 1.43 примечания к истории изменений). При определенных условиях1, Boost.Asio попытается выполнить операцию ввода-вывода в контексте инициирующая функция (например. async_read()). Если операция ввода / вывода завершается (успех или неудача), то обработчик завершения размещается в io_service как будто io_service.post(), В противном случае файловый дескриптор будет добавлен в демультиплексор событий для мониторинга. Документация ссылается на это поведение:

Независимо от того, работает ли асинхронная операция завершается немедленно или нет, обработчик не будет вызываться из этой функции. Вызов обработчика будет выполнен способом, эквивалентным использованию boost::asio::io_service::post(),

Для составных операций, таких как async_read(), EOF считается ошибкой, поскольку это указывает на нарушение в договоре операции (то есть условие завершения никогда не будет выполнено, потому что больше не будет данных). В этом конкретном случае системный вызов ввода-вывода будет происходить в пределах async_read() инициирующая функция, считывание от начала файла (смещение 0) до конца файла, вызывая сбой операции с boost::asio::error::eof, Поскольку операция завершена, она никогда не добавляется в демультиплексор событий для инициируемого фронтом мониторинга:

boost::asio::io_service io_service;
boost::asio::posix::stream_descriptor stream_descriptor(io_service);

void read_handler(const boost::system::error_code& error, ...)
{
if (error.value() == boost::asio::error::eof)
{
// Reset to start of file.
lseek(sd.native_handle(), 0, SEEK_SET);
}

// Same as below.  ::readv() will occur within this context, reading
// from the start of file to end-of-file, causing the operation to
// complete with failure.
boost::asio::async_read(stream_descriptor, ..., &read_handler);
}

int main()
{
int fd = open( /* sysfs file */, O_RDONLY);

// This would throw an exception for normal files, as they are not
// poll-able.  However, the kernel flags some files on procfs and
// sysfs as pollable.
stream_descriptor.assign(fd);

// The underlying ::readv() system call will occur within the
// following function (not deferred until edge-triggered notification
// by the reactor).  The operation will read from start of file to
// end-of-file, causing the operation to complete with failure.
boost::asio::async_read(stream_descriptor, ..., &read_handler);

// Run will invoke the ready-to-run completion handler from the above
// operation.
io_service.run();
}

1. Внутренне, Boost.Asio относится к этому поведению как умозрительный операции. Это деталь реализации, но операция ввода-вывода будет предпринята в пределах инициирующей функции, если для операции может не потребоваться уведомление о событии (например, она может немедленно предпринять попытку неблокирующего вызова ввода-вывода), и нет ни одного ожидающего операции того же типа или ожидающие внеполосных операций над объектом ввода / вывода. Для предотвращения такого поведения нет настроек.

4

Другие решения


По вопросам рекламы ammmcru@yandex.ru
Adblock
detector