【问题标题】:Boost ASIO, SSL: How do strands help the implementation?Boost ASIO、SSL:strands 如何帮助实现?
【发布时间】:2016-01-07 14:25:46
【问题描述】:

TLDR:Strands 序列化跨完成处理程序共享的资源:这如何防止 ssl::stream 实现并发访问 SSL 上下文(内部使用)以进行并发读/写请求(stream::ssl 不是全双工) ?请记住,strand 仅序列化完成处理程序调用或读/写请求的原始队列。 [感谢 sehe 帮助我更好地表达这一点]


我花了一天的大部分时间阅读有关 ASIO、SSL 和 strands 的信息;主要是在 stackoverflow(其中有一些非常详细且表达良好的解释,例如Why do I need strand per connection when using boost::asio?)和 Boost 文档;但有一点还不清楚。

显然,strand 可以在同一 strand 中序列化调用回调,因此也可以序列化对这些 strand 共享资源的访问。

但在我看来 boost::asio::ssl::stream 的问题不在于完成处理程序回调,因为它不是在 SSL 上下文上同时运行的回调,而是 ssl::stream就是实现。

我不能确定在调用 async_read_some 和 async_write_some 时使用 strands,或者在完成处理程序中使用 strands 会阻止 io 引擎在不同线程中同时在 SSL 上下文上运行。

在调用 async_read_some 或 async_write_some 时显然使用 strand 将意味着读取和写入不能在同一时刻排队,但我不明白这如何阻止内部实现同时执行读取和写入操作如果封装的 tcp::socket 同时准备好读写,则在不同线程上的时间。

在这个问题boost asio - SSL async_read and async_write from one thread 的最后一个答案末尾的评论声称并发写入 ssl::stream 可能会出现段错误,而不仅仅是交错,这表明实现没有采用必要的锁来防止并发访问。

除非实际延迟的套接字写入绑定到排队的线程/链(我看不出这是真的,否则会破坏工作线程的有用性),我怎么能确信它是可能的在同一个 ssl::stream 上排队读取和写入,或者这种方式可能是什么?

也许 async_write_some 立即使用 SSL 上下文处理所有数据,以生成加密数据,然后变成普通套接字写入,因此不能与同一链上的读取完成处理程序冲突,但它不会'这并不意味着在完成处理程序在链上排队之前它不能与内部实现套接字读取和解密冲突。不要介意可能发生的透明 SSL 会话重新协商......

我注意到:Why do I need strand per connection when using boost::asio?“组合操作的独特之处在于,对流的中间调用是在处理程序的链中调用的,如果存在的话,而不是启动组合操作的链。”但我不确定我指的是“对流的中间调用”。这是否意味着:“该流实现中的任何后续处理”?我怀疑不是

最后,为什么 - 哦 - 为什么,为什么 ssl::stream 实现不使用 futex 或其他在没有冲突时便宜的锁?如果遵循链规则(隐式或显式),则成本几乎不存在,但否则会提供安全性。我之所以问,是因为我刚刚将 Sutter、Stroustrup 和其他人的宣传转变为 ssl::stream,它似乎很容易遵循某些咒语,但几乎不可能知道你的代码是否真的安全.

【问题讨论】:

    标签: c++ multithreading ssl boost


    【解决方案1】:

    答案是 boost ssl::stream 实现在内部使用 strands 进行 SSL 操作。

    例如,async_read_some() 函数创建一个 openssl_operation 实例,然后调用 strand_.post(boost::bind(&openssl_operation::start, op))。 [http://www.boost.org/doc/libs/1_57_0/boost/asio/ssl/old/detail/openssl_stream_service.hpp]

    假设所有必要的内部 ssl 操作都在这个内部链上执行似乎是合理的,从而序列化对 SSL 上下文的访问。

    【讨论】:

    • 你确定吗?该文件不再存在。路径包括单词“old”。我搜索了最新的 Boost.Asio(在 Boost 1.67 中),但没有看到任何线索。
    • @VinnieFalco -- 好点。而在 5 年多以前,谁能确定任何事情......?
    • @VinnieFalco,实际上,没有内部链。此外,根据我的经验,同时安排对ssl::stream 的读取和写入可能会导致内部竞争,表现为 SSL 通信错误或更糟糕的是,OpenSSL 库中的无限循环挂断。所有这一切都考虑到操作是从单链发出的,这显然没有帮助。这导致了一个可悲的事实,即某些异步协议无法在多线程环境中的 asio 之上实现,例如 HTTP2。
    • 实际上最新的 ssl::stream 文档在线程安全部分中说:应用程序还必须确保所有异步操作都在相同的隐式或显式链中执行。所以我猜想在 ssl 流上同时从不同线程调用 async_ 操作是不安全的
    【解决方案2】:

    Q. 但我不确定我指的是“对流的中间调用”。这是否意味着:“该流实现中的任何后续处理”?我怀疑不是

    文档详细说明:

    此操作通过对流的 async_read_some 函数的零次或多次调用来实现,称为组合操作。程序必须确保流不执行其他读取操作(例如 async_read、流的 async_read_some 函数或执行读取的任何其他组合操作),直到此操作完成。 doc


    最后,对于为什么-哦-为什么,为什么 ssl::stream 实现不使用 futex 或其他在没有冲突时便宜的锁?

    您不能在异步操作中持有 futex,因为任何线程都可能执行完成处理程序。所以,你仍然需要这里的链,使 futex 变得多余。


    此问题的最后一个答案末尾的评论提升了 asio - SSL async_readasync_write 来自一个线程声称并发写入 ssl::stream 可能会出现段错误,而不仅仅是交错,这表明实现没有采取必要的锁来防止并发访问。

    参见上一个条目。不要忘记多个服务线程。数据竞赛是Undefined Behaviour


    TL;DR

    长话短说:异步编程是不同的。这是有充分理由的。不过,您必须调整自己的想法。

    Strands 通过抽象异步调度程序上的顺序执行来帮助实现。

    这样您就不必知道调度是什么,有多少服务线程正在运行等。

    【讨论】:

    • 我期待 futex 在 ssl::stream 实现中运行,同时在 SSL 上下文上运行。这将解决 ssl::stream 具有但 tcp 的单双工程序::socket 没有。我很欣赏你在链和异步编程方面的一般 cmet;我认为他们没有回答这样的问题,即在调用 async_read_some 或 async_write_some 或在完成处理程序回调的表达式中使用链如何解决双工问题。
    • 我会链接到您所指的相同答案。老实说,我不确定缺少什么难题(您是否发现“不要忘记多个服务线程”?)
    • 您正在帮助我更清楚地表达问题,对此我表示感谢。 Strands 序列化跨完成处理程序共享的资源:这如何防止 ssl::stream 实现并发访问 SSL 上下文(内部使用)以进行并发读/写请求?请记住,strand 仅序列化完成处理程序调用或读/写请求的原始队列。
    • “仅序列化完成处理程序调用或读/写请求的原始队列”是什么意思。这对我来说听起来不对。此外,通过“序列化”,您应该是指“确保序列中的步骤之间的先发生关系”
    • “你的意思是“strands 只序列化完成处理程序调用或读/写请求的原始队列”这对我来说是错误的。”我的意思是,如果我发布异步读取或异步写入请求,它们是串行执行的,从不并发执行,并且完成处理程序是串行执行而不是并发执行。 Yu 是对的,strands 不序列化资源,我的意思是序列化对资源的访问。但是我的问题不是关于这些,而是​​关于 ssl::stream 内部发生的事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-14
    • 2016-08-03
    • 2011-08-27
    • 2012-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多