我可以将文件描述符的地址分配给变量吗?
Can I assign an address of file descriptor to a variable?
我需要使用共享内存来进行进程之间的通信,我徘徊着是否可以将共享内存文件描述符或公共文件描述符的地址分配给指向结构或vector/map的指针?
例如,addr
如果共享内存的地址
Struct A{...};
A* stru = static_cast<A*>(addr); // is this OK?
首先,您可能应该查看 Boost.interprocess 并使用它。
有关手动方法,请继续阅读。
为此,您的结构不得使用任何指针。它也不能有任何虚函数。如果它只是一个具有非指针数据成员的结构(意味着只是内置类型,没有 std::string
或 std::vector
或任何不是普通结构的东西)及其成员函数(如果有的话)只对该数据进行操作,不调用任何非成员函数,那么这样做应该是安全的。
您需要确保地址对齐正确。您可以明确检查:
#include <cstdint>
#include <cassert>
// ...
assert(reinterpret_cast<std::uintptr_t>(addr) % alignof(A) == 0);
如果您用来创建共享内存的方法允许您以字节为单位指定所需的对齐方式,那么 alignof(A)
将为您提供正确的字节数。
内存的大小也必须足够大(必须至少sizeof(A)
。)
如果它具有兼容的对齐方式和大小,则您必须使用新放置在该内存中创建您的对象:
#include <new>
// ...
auto stru = new(addr) A;
这将在 addr
指向的内存中创建对象 stru
。
不要对该对象调用delete
。在通常调用 delete
的位置,您应该手动调用析构函数:
stru->~A();
为了使这更容易,您可能应该将此对象包装在 unique_ptr
中,并使用调用析构函数的自定义 lambda 删除器:
#include <memory>
// ...
auto a_dtor_caller = [](A* obj){ obj->~A(); };
auto stru = std::unique_ptr<A, decltype(a_dtor_caller)>(new(addr) A, a_dtor_caller);
stru
现在是 unique_ptr
并且它管理的 A
实例在超出范围时将被正确销毁。
以上只能做一次。其他只需要访问该对象的进程应该只使用:
A* stru = static_cast<A*>(addr);
不言而喻,他们也不应该 delete
对象或调用其析构函数。只有管理对象的 unique_ptr
才应该这样做。
当您要释放共享内存时,您需要先销毁该对象。如果您的代码在释放共享内存之前不会自然地让 unique_ptr
"expire",那么您必须手动执行此操作:
stru.reset();
这将破坏对象,stru
现在实际上是一个 nullptr
。
总而言之,我真的很推荐Boost.interprocess。不易出错。
我需要使用共享内存来进行进程之间的通信,我徘徊着是否可以将共享内存文件描述符或公共文件描述符的地址分配给指向结构或vector/map的指针?
例如,addr
如果共享内存的地址
Struct A{...};
A* stru = static_cast<A*>(addr); // is this OK?
首先,您可能应该查看 Boost.interprocess 并使用它。
有关手动方法,请继续阅读。
为此,您的结构不得使用任何指针。它也不能有任何虚函数。如果它只是一个具有非指针数据成员的结构(意味着只是内置类型,没有 std::string
或 std::vector
或任何不是普通结构的东西)及其成员函数(如果有的话)只对该数据进行操作,不调用任何非成员函数,那么这样做应该是安全的。
您需要确保地址对齐正确。您可以明确检查:
#include <cstdint>
#include <cassert>
// ...
assert(reinterpret_cast<std::uintptr_t>(addr) % alignof(A) == 0);
如果您用来创建共享内存的方法允许您以字节为单位指定所需的对齐方式,那么 alignof(A)
将为您提供正确的字节数。
内存的大小也必须足够大(必须至少sizeof(A)
。)
如果它具有兼容的对齐方式和大小,则您必须使用新放置在该内存中创建您的对象:
#include <new>
// ...
auto stru = new(addr) A;
这将在 addr
指向的内存中创建对象 stru
。
不要对该对象调用delete
。在通常调用 delete
的位置,您应该手动调用析构函数:
stru->~A();
为了使这更容易,您可能应该将此对象包装在 unique_ptr
中,并使用调用析构函数的自定义 lambda 删除器:
#include <memory>
// ...
auto a_dtor_caller = [](A* obj){ obj->~A(); };
auto stru = std::unique_ptr<A, decltype(a_dtor_caller)>(new(addr) A, a_dtor_caller);
stru
现在是 unique_ptr
并且它管理的 A
实例在超出范围时将被正确销毁。
以上只能做一次。其他只需要访问该对象的进程应该只使用:
A* stru = static_cast<A*>(addr);
不言而喻,他们也不应该 delete
对象或调用其析构函数。只有管理对象的 unique_ptr
才应该这样做。
当您要释放共享内存时,您需要先销毁该对象。如果您的代码在释放共享内存之前不会自然地让 unique_ptr
"expire",那么您必须手动执行此操作:
stru.reset();
这将破坏对象,stru
现在实际上是一个 nullptr
。
总而言之,我真的很推荐Boost.interprocess。不易出错。