据我所知,使用 Boost.Asio 不可能获得这种特殊行为。虽然内核将 procfs 和 sysfs 上的某些文件标记为可轮询,但它们不提供预期的类似流的行为boost::asio::posix::stream_descriptor http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/reference/posix__stream_descriptor.html及其运营。
Boost.Asio's epoll reactor is edge-triggered (see Boost.Asio 1.43 revision history notes http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/history.html#boost_asio.history.asio_1_4_5___boost_1_43). Under certain conditions1, Boost.Asio will attempt the I/O operation within the context of the initiating function (e.g. async_read() http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/reference/async_read/overload3.html). If the I/O operation completes (success or failure), then the completion handler is posted into the io_service
as-if by io_service.post()
. Otherwise, the file descriptor will be added to the event demultiplexer for monitoring. The documentation alludes to this behavior:
无论是否异步操作立即完成无论是否,都不会从此函数内调用处理程序。处理程序的调用将以相当于使用的方式执行boost::asio::io_service::post()
.
对于组合操作,例如async_read()
, EOF 被视为错误 http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/overview/core/streams.html#boost_asio.overview.core.streams.why_eof_is_an_error,因为它表明违反了操作合同(即永远不会满足完成条件,因为没有更多数据可用)。在这种特殊情况下,I/O 系统调用将发生在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. Internally, Boost.Asio refers to this behavior as speculative operations. It is an implementation detail, but the I/O operation will be attempted within the initiating function if the operation may not need event notification (e.g. it can immediately attempt to a non-blocking I/O call), and and there are neither pending operations of the same type nor pending out-of-band operations on the I/O object. There are no customization hooks to prevent this behavior.