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

macOS下aio_write执行失败:Inappropriate ioctl for device

macOS下Posix AIO异步读写Poll返回"Inappropriate ioctl for device"错误排查

在macOS环境下基于Posix AIO库实现异步读写,IO请求提交环节正常,但调用Poll方法获取执行结果时,返回的finish code不为零,报错信息为"Inappropriate ioctl for device"。已知Posix AIO是用户态异步IO引擎,理论支持跨平台运行,不清楚该错误的触发原因。


异步读写实现代码

#include "io_engine.h"

namespace demo {

Status PosixAIOEngine::AsyncWrite(int fd, uint64_t offset, const char* buffer, uint64_t size,
                                  char** cb) {
  // TODO Check io_depth
  requests_.push_back({.aio_req_ = {}, .type_ = kAsyncWrite});
  auto& request = requests_.back();
  memset(&request.aio_req_, 0, sizeof(struct aiocb));
  request.aio_req_.aio_offset = (off_t)offset;
  request.aio_req_.aio_buf = (void*)buffer;
  request.aio_req_.aio_nbytes = size;
  request.aio_req_.aio_fildes = fd;

  int ret = aio_write(&request.aio_req_);
  if (ret == -1) {
    auto msg = "aio_write failed, err msg: " + std::string(strerror(errno));
    LOG(ERROR, msg);
    return Status::IOError(msg);
  }

  return Status::OK();
}

Status PosixAIOEngine::AsyncRead(int fd, uint64_t offset, char* buffer, uint64_t size, char** cb) {
  requests_.push_back({.aio_req_ = {}, .type_ = kAsyncRead});
  auto& request = requests_.back();
  memset(&request.aio_req_, 0, sizeof(struct aiocb));
  request.aio_req_.aio_offset = (off_t)offset;
  request.aio_req_.aio_buf = (void*)buffer;
  request.aio_req_.aio_nbytes = size;
  request.aio_req_.aio_fildes = fd;

  int ret = aio_read(&request.aio_req_);
  if (ret == -1) {
    LOG(ERROR, "aio_read failed, err: {}, offset: {}, sz: {}", strerror(errno), offset, size);
    requests_.pop_back();
    return Status::IOError("aio read failed!");
  }
  return Status::OK();
}

// TODO Probably we should check the event by their submission order?
uint32_t PosixAIOEngine::Poll() {
  uint32_t cnt = 0;
  auto it = requests_.begin();
  while (it != requests_.end()) {
    auto& req = *it;
    int ret = aio_error(&req.aio_req_);
    if (ret == 0) {
      LOG(INFO, IORequest::GetTypeName(req.type_) +
                    ", callback offset: " + std::to_string(req.aio_req_.aio_offset));
      uint64_t finish_code = aio_return(&req.aio_req_);
      if (finish_code != 0) {
        LOG(ERROR, "return code error : {}, offset: {}", strerror(errno), req.aio_req_.aio_offset);
      }
      it = requests_.erase(it);
      cnt++;
    } else {
      ++it;
    }
  }
  return cnt;
}

}

测试代码

#include "io_engine.h"

#include <fcntl.h>
#include <gtest/gtest.h>
#include <unistd.h>

#include <memory>

#include "utils.h"

namespace demo {
class IOEngineTest : public ::testing::Test {
 public:
  std::string file_prefix = "io_engine_test_file_";
  std::string test_file_;
  int write_fd_{};
  int read_fd_{};
  uint64_t file_size_ = 10 << 20;
  std::unique_ptr<IOEngine> io_engine_;

  void SetUp() override {
    test_file_ = CreateRandomFile(file_size_);
    io_engine_ = std::make_unique<PosixAIOEngine>();
    write_fd_ = open(test_file_.c_str(), O_RDWR | O_CREAT, 0777);
    if (write_fd_ == -1) {
      LOG(ERROR, "Open writable file failed, error: {}", std::strerror(errno));
      abort();
    }
    read_fd_ = open(test_file_.c_str(), O_RDONLY);
    if (read_fd_ == -1) {
      LOG(ERROR, "Open readonly file failed, error: {}", std::strerror(errno));
      abort();
    }
  }

  void TearDown() override {
    int total = FileUtils::DeleteFilesByPrefix(".", file_prefix);
    LOG(INFO, "{} files was deleted", total);
  }

  std::string CreateRandomFile(uint32_t sz) {
    auto filename = FileUtils::GenerateRandomFile(file_prefix, sz);
    filenames_.emplace_back(filename);
    return filename;
  }

 private:
  std::vector<std::string> filenames_;
};

TEST_F(IOEngineTest, MultipleAIOWriteTest) {
  uint64_t page_size = 4UL << 10;
  uint32_t total_requests = 6;

  // Write Request
  for (int i = 0; i < total_requests; ++i) {
    std::string data = std::to_string(i);
    auto s = io_engine_->AsyncWrite(write_fd_, i * page_size, data.c_str(), data.size(), nullptr);
    EXPECT_TRUE(s.ok());
  }

  // POLL result out
  uint32_t cnt = 0;
  while (cnt < total_requests) {
    cnt += io_engine_->Poll();
  }
  EXPECT_EQ(cnt, total_requests);
  EXPECT_EQ(io_engine_->GetInFlightRequests(), 0);

  // Read Request
  char* buf = nullptr;
  posix_memalign((void**)&buf, page_size, total_requests * page_size);
  for (int i = 0; i < total_requests; ++i) {
    auto s = io_engine_->AsyncRead(read_fd_, i * page_size, buf, page_size, nullptr);
    ASSERT_TRUE(s.ok());
  }
  EXPECT_EQ(io_engine_->GetInFlightRequests(), total_requests);

  // POLL result out
  cnt = 0;
  while (cnt < total_requests) {
    cnt += io_engine_->Poll();
  }

  EXPECT_EQ(cnt, total_requests);
  for (int i = 0; i < total_requests; ++i) {
    ASSERT_EQ(buf[i], static_cast<char>(i));
  }
  delete buf;
}
}

日志信息

[2024-03-11 T11:01:36.786][132731804][info][utils.h:35] Generate random file, filename: io_engine_test_file_4245618977
[2024-03-11 T11:01:36.786][132731804][info][io_engine_test.cc:34] write fd: 4, read fd: 5
[2024-03-11 T11:01:36.787][132731804][info][io_engine.cc:62] kAsyncWrite, callback offset: 4096
[2024-03-11 T11:01:36.787][132731804][error][io_engine.cc:66] return code error : Inappropriate ioctl for device, offset: 4096, fd : 4
[2024-03-11 T11:01:36.787][132731804][info][io_engine.cc:62] kAsyncWrite, callback offset: 8192
[2024-03-11 T11:01:36.787][132731804][error][io_engine.cc:66] return code error : Inappropriate ioctl for device, offset: 8192, fd : 4
[2024-03-11 T11:01:36.787][132731804][info][io_engine.cc:62] kAsyncWrite, callback offset: 12288
[2024-03-11 T11:01:36.787][132731804][error][io_engine.cc:66] return code error : Inappropriate ioctl for device, offset: 12288, fd : 4
[2024-03-11 T11:01:36.787][132731804][info][io_engine.cc:62] kAsyncWrite, callback offset: 0
[2024-03-11 T11:01:36.787][132731804][error][io_engine.cc:66] return code error : Inappropriate ioctl for device, offset: 0, fd : 4
[2024-03-11 T11:01:36.787][132731804][info][io_engine.cc:62] kAsyncWrite, callback offset: 16384
[2024-03-11 T11:01:36.787][132731804][error][io_engine.cc:66] return code error : Inappropriate ioctl for device, offset: 16384, fd : 4
[2024-03-11 T11:01:36.787][132731804][info][io_engine.cc:62] kAsyncWrite, callback offset: 20480
[2024-03-11 T11:01:36.787][132731804][error][io_engine.cc:66] return code error : Inappropriate ioctl for device, offset: 20480, fd : 4
[2024-03-11 T11:01:36.787][132731804][info][io_engine.cc:62] kAsyncRead, callback offset: 4096
[2024-03-11 T11:01:36.787][132731804][error][io_engine.cc:66] return code error : Inappropriate ioctl for device, offset: 4096, fd : 5
[2024-03-11 T11:01:36.787][132731804][info][io_engine.cc:62] kAsyncRead, callback offset: 12288
[2024-03-11 T11:01:36.787][132731804][error][io_engine.cc:66] return code error : Inappropriate ioctl for device, offset: 12288, fd : 5
[2024-03-11 T11:01:36.787][132731804][info][io_engine.cc:62] kAsyncRead, callback offset: 16384
[2024-03-11 T11:01:36.787][132731804][error][io_engine.cc:66] return code error : Inappropriate ioctl for device, offset: 16384, fd : 5
[2024-03-11 T11:01:36.787][132731804][info][io_engine.cc:62] kAsyncRead, callback offset: 20480
[2024-03-11 T11:01:36.787][132731804][error][io_engine.cc:66] return code error : Inappropriate ioctl for device, offset: 20480, fd : 5
[2024-03-11 T11:01:36.787][132731804][info][io_engine.cc:62] kAsyncRead, callback offset: 0
[2024-03-11 T11:01:36.787][132731804][error][io_engine.cc:66] return code error : Inappropriate ioctl for device, offset: 0, fd : 5
[2024-03-11 T11:01:36.787][132731804][info][io_engine.cc:62] kAsyncRead, callback offset: 8192
[2024-03-11 T11:01:36.787][132731804][error][io_engine.cc:66] return code error : Inappropriate ioctl for device, offset: 8192, fd : 5

问题原因及解决方法

核心原因

  1. macOS对Posix AIO的兼容性限制:macOS的Posix AIO实现并非完全遵循POSIX标准,仅提供有限支持,常规文件场景下容易出现兼容性问题。
  2. 错误的结果判断逻辑:aio_return返回的是实际读写的字节数,而非错误码。当前代码误将非零值判定为错误,实际上只有当aio_error返回-1时,才需要通过errno获取错误信息。

修正步骤

1. 修复Poll方法的错误判断逻辑

将aio_return的非零值判定逻辑改为仅当返回-1时才判定为错误,同时正确处理aio_error的返回值:

uint32_t PosixAIOEngine::Poll() {
  uint32_t cnt = 0;
  auto it = requests_.begin();
  while (it != requests_.end()) {
    auto& req = *it;
    int ret = aio_error(&req.aio_req_);
    if (ret == 0) {
      LOG(INFO, IORequest::GetTypeName(req.type_) +
                    ", callback offset: " + std::to_string(req.aio_req_.aio_offset));
      ssize_t finish_code = aio_return(&req.aio_req_);
      if (finish_code == -1) {
        LOG(ERROR, "return code error : {}, offset: {}", strerror(errno), req.aio_req_.aio_offset);
      } else {
        LOG(INFO, "{} bytes processed, offset: {}", finish_code, req.aio_req_.aio_offset);
      }
      it = requests_.erase(it);
      cnt++;
    } else if (ret == EINPROGRESS) {
      ++it;
    } else {
      LOG(ERROR, "Request failed: {}, offset: {}", strerror(ret), req.aio_req_.aio_offset);
      it = requests_.erase(it);
      cnt++;
    }
  }
  return cnt;
}

2. 替换macOS下的异步IO实现

如果需要稳定的异步IO能力,建议改用macOS原生支持的方案:

  • 使用libdispatch(GCD)的异步文件操作接口,如dispatch_io_create系列函数。
  • 使用kqueue监听文件描述符的读写事件,实现异步IO逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 15:29:51