You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ARM平台ASIO用udmabuf DMA缓冲区async_write()随机报EFAULT错误

使用Boost ASIO async_write写入udmabuf映射内存时随机出现"Bad Address"错误

环境与场景

运行于ARM Cortex-A53平台,通过g交叉编译的C程序:

  • 从udmabuf管理的65536字节(64KiB)内存映射缓冲区读取数据,该缓冲区物理地址固定,通过/dev/udmabuf0暴露给用户空间,以O_SYNC方式映射(禁用CPU缓存)
  • 硬件通过DMA填充缓冲区后,调用write_data函数,使用Boost 1.74 ASIO的async_write将数据发送至远端客户端(客户端处理速度快于主机发送速度)
  • io_context为单线程运行:
boost::asio::io_context io_context_ {1};
...
io_context_.run();

核心代码

Session类中负责发送缓冲区数据的函数:

class Session: public std::enable_shared_from_this<Session> {
private:
    boost::asio::ip::tcp::socket socket_;

...

public:
    void write_data(void * buffer_mm,
                    std::size_t total_bytes_transferred) {
        auto buffer = boost::asio::const_buffer(static_cast<const uint8_t *>(buffer_mm));

        boost::asio::async_write(
            socket_,
            buffer,
            [total_bytes_transferred, me = shared_from_this()]
                    (const error_code & ec, std::size_t bytes_transferred) {
                if (ec) {
                    BOOST_LOG_TRIVIAL(error) << "write_data [" << ec << "] " << ec.message();
                    // stop sending data.
                } else {
                    BOOST_LOG_TRIVIAL(debug) << "write_data " << bytes_transferred << " bytes";
                    // continue writing data...
                    me->write_footer(total_bytes_transferred + bytes_transferred);
                }
        });
    }
};

程序中还有write_header()和write_footer()函数用于发送静态数据,这两个异步写操作从未出现错误。

错误现象

循环执行「DMA填充缓冲区→写入Socket」流程数百次(无额外延迟,尽可能快执行),随机次数迭代后(最少5次,最多50或偶尔100次),async_write的完成Lambda会返回*"Bad Address"错误(对应系统错误码14,即EFAULT*,表示系统调用收到无效用户空间内存指针)。

但每次传入async_write的都是同一缓冲区地址,并未发生变更,socket状态看似正常。

排查细节

  • 延迟影响:在async_write前添加45ms延迟,错误不再出现;40ms延迟会降低错误出现频率;35ms延迟无效果。
  • 数据大小相关性:错误仅在传输大小处于[65524, 65536]区间且为4字节对齐时触发;多4字节或少16字节均无错误,似乎仅与传入async_write的数据大小有关,与底层缓冲区大小无关。
  • 同步/异步一致性:使用同步write()替代async_write(),仍会出现相同错误。
  • 性能特征:udmabuf禁用CPU缓存,数据无需经过CPU即可传输至Socket,吞吐量可达近900 Mbps。

补充信息

数月前最初编写代码时,曾尝试将头部、数据、尾部通过std::vector<boost::asio::const_buffer>整合为一个async_write调用(分散/聚集写入),当时若头部长度不是4字节的倍数,调用会偶尔返回*"Bad Address"*错误,无明确触发规律。后来改为三次独立的async_write调用,问题暂时消失,直到现在复现。

新发现

  • 当65536字节写入触发错误时,bytes_transferred始终为32737字节(比32768少31),客户端也能读取到对应字节数。
  • 若将所有Socket写操作的长度改为8字节的倍数,问题完全解决,但会破坏协议(需额外填充数据)。
  • 错误触发无确定性:相同输入的测试有时触发(概率约50%),有时多次运行无错误,推测与系统内部状态有关。

疑问

  1. 同一缓冲区地址为何会突然变为无效?
  2. async_write的调用方式是否存在根本问题?Session对象的生命周期是否正确?
  3. 单线程io_context场景下是否需要使用strand?原以为strand仅用于多线程io_context场景。
  4. 32737这个数字的意义是什么?

内容的提问来源于stack exchange,提问作者davidA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 19:12:32