2015-05-26 5 views
7

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

Это можно сделать с помощью 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 внутренности, но может ли причина быть в том, что реактор epoll 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; 
} 
+1

["boost использует кроссировочный способ epoll"] (http://lists.boost.org/boost-users/2013/05/78931.php) –

+0

Ну, спасибо за ссылку, но как один может видеть в grep-выходе EPOLLET не используется в любом случае. – Florian

+0

как это «не используется в любом случае»? EPOLLET используется в разных местах. –

ответ

4

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

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

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

Для сложенных операций, таких как async_read(), EOF is treated as an error, так как это указывает на нарушение в договоре Операции (т.е. условие завершения не будет удовлетворен, потому что нет больше данных будет доступна). В этом конкретном случае системный вызов ввода/вывода будет выполняться в функции инициализации 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 относится к этому поведению как спекулятивных операций. Это деталь реализации, но операция ввода-вывода будет предпринята в рамках инициирующей функции, если для операции может не потребоваться уведомление о событии (например,он может сразу же попытаться вызвать неблокирующий вызов ввода/вывода), а также нет ожидающих операций одного и того же типа и ожидающих внеполосных операций над объектом ввода-вывода. Для предотвращения такого поведения нет адаптеров настройки.

+0

Спасибо за ваш ответ, это помогло мне лучше понять некоторые концепции boost :: asio. Я понял, что случай EOF можно избежать, используя [null_buffers] (http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/overview/core/reactor.html). – Florian

+0

Однако, выкапывая немного глубже в источниках реактора epios asios, я обнаружил, что причина описанного поведения не является спекулятивной операцией, а скорее тем, что реактор epoll вызывает «epoll_ctl» для каждой асинхронной операции, которая делает «epoll_wait» немедленно возвращайтесь. – Florian

+0

Я доказал, что реализация 'epoll', использующая' epoll_ctl' только один раз в самом начале, делает последующие вызовы 'epoll_wait' (за исключением сразу следующего) блока до тех пор, пока не произойдет событие GPIO. К сожалению, как вы сказали, кажется, что невозможно получить это конкретное поведение от boost :: asio. – Florian