[英]How to use boost::asio with Linux GPIOs
我有一个使用boost :: asio进行异步输入/输出的单线程Linux应用程序。 现在,我需要扩展此应用程序以读取/sys/class/gpio/gpioXX/value
上的GPIO输入。
可以通过在边沿触发的GPIO输入上使用boost :: asio :: posix :: stream_descriptor来做到这一点?
我将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
调用总是立即使用EOF或(如果将文件指针设置回去的话)2个字节的数据立即调用处理程序(当然,仅在io_service.run()
)。
我不是boost::asio
内部专家,但原因可能是boost::asio
epoll反应器是水平触发的,而不是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;
}
据我所知,使用Boost.Asio无法获得这种特殊行为。 尽管内核将procfs和sysfs上的某些文件标记为可轮询,但它们并未提供boost::asio::posix::stream_descriptor
及其操作所期望的类似于流的行为。
Boost.Asio的epoll反应器是边缘触发的(请参阅Boost.Asio 1.43 修订历史记录 )。 在某些条件1下 ,Boost.Asio将在启动函数 (例如async_read()
)的上下文中尝试I / O操作。 如果I / O操作完成(成功或失败),则通过io_service.post()
将完成处理程序视情况发布到io_service
中。 否则,文件描述符将添加到事件多路分解器以进行监视。 该文档暗示了这种行为:
无论异步操作是否立即完成 ,都不会从此函数内调用处理程序。 处理程序的调用将以与使用
boost::asio::io_service::post()
等效的方式执行。
对于诸如async_read()
类的组合操作, EOF被视为错误 ,因为它表明该操作的合同中有违规行为(即,将不再满足完成条件,因为没有更多数据可用)。 在这种情况下,I / O系统调用将在async_read()
初始化函数中发生,从文件的开头(偏移量0)到文件的末尾读取,从而导致操作因boost::asio::error::eof
而失败。 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将此行为称为推测性操作。 这是一个实现细节,但是如果该操作可能不需要事件通知(例如,它可以立即尝试进行非阻塞的I / O调用)并且没有待处理的事件,则将在启动函数中尝试该I / O操作。相同类型的操作或对I / O对象的未决带外操作。 没有自定义钩子可以防止此行为。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.