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:
- Incorrectly formatted or invalid data structures passed as arguments.
- Invalid file descriptors or file handles.
- Memory allocation problems.
- 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 对象的所有权。然而,在这种情况下,您可能会遇到一些问题。
让我们来解释一下问题所在:
-
Ownership Transfer with std::move: std::move(ptr.get()) 将会调用 ptr.get() 来获取 Work 对象的原始指针,然后使用 std::move 将其转移到 std::thread 构造函数中。但这不是推荐的用法。std::move 并不会转移 std::unique_ptr 自身的所有权,而只是将其内部指针进行了移动操作。这样做会导致潜在的悬空指针问题。
-
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()));
让我们讨论一下这两种方法:
-
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 对象的生命周期延长至线程的结束。 -
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;
}