一次关于aio的debug

aio:

aio - POSIX asynchronous I/O overview


问题描述

io_submit() return -9


原因分析:

询问chatgpt

The io_submit() function is typically used in Linux for submitting asynchronous I/O operations to the kernel for processing. When io_submit() returns -9, it indicates an error, specifically -EINVAL, which stands for “Invalid Argument.”

This error code suggests that there might be an issue with one or more of the arguments passed to the io_submit() function. It could be due to various reasons, such as:

  1. Incorrectly formatted or invalid data structures passed as arguments.
  2. Invalid file descriptors or file handles.
  3. Memory allocation problems.
  4. Other specific requirements or constraints that weren’t met.

To resolve this issue, you should carefully review the arguments you’re passing to io_submit() and ensure they conform to the requirements specified by the function. Double-check the data structures, file descriptors, and other parameters being used. It might also be helpful to consult the documentation or man pages related to io_submit() for further insights into its proper usage and requirements.。

chatgpt似乎很确定地告诉我是参数的问题。
该怎么办呢?暴力打印**io_submit()**传入的参数。

int res = io_submit(ioctxt_, 1, &io_cb);
std::cout << "io_cb->aio_fildes = " << io_cb->aio_fildes << std::endl;
std::cout << "io_cb->aio_lio_opcode = " << io_cb->aio_lio_opcode << std::endl;
std::cout << "io_cb->aio_reqprio = " << io_cb->aio_reqprio << std::endl;
std::cout << "io_cb->aio_rw_flags = " << io_cb->aio_rw_flags << std::endl;
std::cout << "io_cb->data = " << io_cb->data << std::endl;
std::cout << "io_cb->key = " << io_cb->key << std::endl;
std::cout << "res = " << res << std::endl;

编译执行

 c++ aio_tool.cc -laio -g -o aio_tool
 sudo ./aio_tool

结果

io_cb->aio_fildes = 1584530700
io_cb->aio_lio_opcode = 0
io_cb->aio_reqprio = 0
io_cb->aio_rw_flags = 0
io_cb->data = 0x7fef48000b70
io_cb->key = 0
res = -9

注意这里的io_cb->aio_fildes是文件描述符。所以是文件描述初始化错误的问题。

所以为什么会出现这个问题呢?我不可能直接犯这种低级错误的。所以用简单的代码复习一下这个问题。

分析下面的代码

#include <memory>
#include <iostream>
#include <thread>
#include <vector>

class Work {
  private:
    int fd_;
  public:
    Work(int fd) {
      fd_ = fd;
    }
    void run() {
        std::cout << "fd_ = " << fd_ << std::endl;
    }
};

int main() {
    std::vector<std::thread> vec_thread;
    {
        auto ptr = std::make_unique<Work>(1);
        vec_thread.push_back(std::thread(&Work::run, ptr.get()));
    }

    for (auto& t : vec_thread) {
        t.join();
    }
    return 0;
}

执行结果

./a.out
fd_ = 1552279632

不难理解ptr,已经被free啦。

看看chatgpt的分析

在您提供的代码中,您尝试创建了一个 std::unique_ptr,并且在创建 std::thread 时使用了 std::move 来传递 Work 对象的所有权。然而,在这种情况下,您可能会遇到一些问题。

让我们来解释一下问题所在:

  1. Ownership Transfer with std::move: std::move(ptr.get()) 将会调用 ptr.get() 来获取 Work 对象的原始指针,然后使用 std::move 将其转移到 std::thread 构造函数中。但这不是推荐的用法。std::move 并不会转移 std::unique_ptr 自身的所有权,而只是将其内部指针进行了移动操作。这样做会导致潜在的悬空指针问题。

  2. Lifetime of the Object: 当 ptr 超出作用域时(在 std::vector 的大括号结束时),ptr 持有的 Work 对象将会被销毁,而在线程中可能仍在访问这个对象,这会导致未定义行为(Undefined Behavior)。

为了避免这些问题,您可以使用 std::shared_ptr 以确保对象的生命周期延长至所有线程结束。

针对下面代码的讨论

vec_thread.push_back(std::thread(&Work::run, ptr));
vec_thread.push_back(std::thread(&Work::run, ptr.get()));

让我们讨论一下这两种方法:

  1. vec_thread.push_back(std::thread(&Work::run, ptr));
    这种情况下,ptr 是一个 std::shared_ptr。在使用 std::thread 构造函数时,直接将 ptr 作为参数传递。std::thread 的构造函数接受了一个可调用对象(函数或函数对象)以及它的参数。在这里,&Work::run 是一个成员函数指针,而 ptr 是一个指向 Work 对象的智能指针。由于 ptr 是 std::shared_ptr,std::thread 的构造函数将其作为参数传递,它将引用计数增加一次,确保 Work 对象的生命周期延长至线程的结束。

  2. vec_thread.push_back(std::thread(&Work::run, ptr.get()));
    这里 ptr 是一个 std::unique_ptr。ptr.get() 返回指向 Work 对象的原始指针,然后将这个指针作为参数传递给 std::thread 的构造函数。这种方式并不安全,因为ptr.get() 返回的是原始指针,而 std::unique_ptr 没有转移所有权,这意味着在这种情况下,没有任何机制确保 Work 对象的生命周期延长至线程的结束。当 ptr 超出作用域时,Work 对象将被销毁,而在线程可能仍在访问这个已销毁的对象,导致未定义行为。

总的来说,第一个方法使用 std::shared_ptr 是安全的,因为 std::shared_ptr 会管理对象的生命周期,确保对象在最后一个引用被释放之前不会被销毁。而第二种方法使用 std::unique_ptr 返回的原始指针是不安全的,因为无法保证对象的生命周期,可能导致悬空指针和未定义行为。

最终的代码

#include <memory>
#include <iostream>
#include <thread>
#include <vector>
#include <utility>

class Work {
  private:
    int fd_;
  public:
    Work(int fd) {
      fd_ = fd;
    }
    void run() {
        std::cout << "fd_ = " << fd_ << std::endl;
    }
};

int main() {
    std::vector<std::thread> vec_thread;
    {
        auto ptr = std::make_shared<Work>(1);
        vec_thread.push_back(std::thread(&Work::run, ptr));
    }

    for (auto& t : vec_thread) {
        t.join();
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值