Boost.Interprocess crashes when destroy an object in a mpi process - windows
I create a shm in parent process and then start mpiexec by system.
In these mpi booted processes, the shm is opened. It can find objects by find<Type>("name"), but when it tries destroy<Type>(name), there is an error.
void Foo::DumpInitialData()
{
using namespace boost::interprocess;
shared_memory_object::remove("MySHM");
typedef allocator<char, managed_shared_memory::segment_manager>
ShmCharAllocator;
typedef std::basic_string<char, std::char_traits<char>, ShmCharAllocator>
ShmStr;
string try_str;
{
ostringstream try_ss;
Foo split(*this, ...);
BinarySerialization ser(split);
ser.Dump(try_ss);
try_str = try_ss.str();
}
size_t try_size = sizeof(ShmStr) + 512
+ (try_str.capacity() + 1) * sizeof(char);
managed_shared_memory segment(create_only, "MySHM",
try_size * (split_size + 1));
ShmCharAllocator alloc_chars(segment.get_segment_manager());
segment.construct<ShmStr>(
("Mpi" + to_string(0)).c_str(
))(try_str, alloc_chars);
try {
for (int i = 1; i < split_size; ++i) {
stringstream ss;
Foo split(*this, ...);
BinarySerialization ser(split);
ser.Dump(ss);
segment.construct<ShmStr>(("Mpi" + to_string(i)).c_str())
(ss.str(), alloc_chars);
}
} catch (interprocess_exception &e) {
throw ...Exception(
"No enough shared memeroy to passing split data."
);
}
}
...
void Foo::PumpMpiInitialData(int i)
{
auto& world = mpi->world();
vector<string> send(world.size());
decltype(send)::value_type recv;
if (i == 0) {
using namespace boost::interprocess;
typedef allocator<char, managed_shared_memory::segment_manager>
ShmCharAllocator;
typedef std::basic_string<
char, std::char_traits<char>, ShmCharAllocator
> ShmStr;
{
managed_shared_memory segment(
open_only, "MySHM"
);
for_each(send | ba::indexed(), [&](const auto& s_it) {
auto name = "Mpi" + to_string(s_it.index());
const char* c_name = name.c_str();
ShmStr* data = segment.find<ShmStr>(c_name).first;
s_it.value() = string(*data);
// This will failed.
bool succ = segment.destroy<ShmStr>(c_name);
});
}
shared_memory_object::remove("MySHM");
}
bm::scatter(world, send, recv, 0);
BinarySerialization ser(*this);
istringstream ss(recv);
ser.Pump(ss);
}
The error is happened with the following call stacks:
> ....exe!std::_Container_base12::_Orphan_all_unlocked_v3() Line 1243 C++
....exe!std::_Container_base12::_Orphan_all_locked_v3() Line 1098 C++
....exe!std::_Container_base12::_Orphan_all() Line 1260 C++
....exe!std::basic_string<char,std::char_traits<char>,boost::interprocess::allocator<char,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>::_Tidy_deallocate() Line 4618 C++
....exe!std::basic_string<char,std::char_traits<char>,boost::interprocess::allocator<char,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>::~basic_string<char,std::char_traits<char>,boost::interprocess::allocator<char,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>() Line 3005 C++
....exe!std::basic_string<char,std::char_traits<char>,boost::interprocess::allocator<char,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>::`scalar deleting destructor'(unsigned int) C++
....exe!boost::interprocess::ipcdetail::placement_destroy<std::basic_string<char,std::char_traits<char>,boost::interprocess::allocator<char,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>>::destroy_n(void * mem, unsigned __int64 num, unsigned __int64 & destroyed) Line 61 C++
....exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>::priv_generic_named_destroy<char>(const char * name, boost::interprocess::iset_index<boost::interprocess::ipcdetail::index_config<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>>> & index, boost::interprocess::ipcdetail::in_place_interface & table, boost::interprocess::ipcdetail::bool_<1> is_intrusive_index) Line 992 C++
....exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>::destroy<std::basic_string<char,std::char_traits<char>,boost::interprocess::allocator<char,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>>(boost::interprocess::ipcdetail::char_ptr_holder<char> name) Line 553 C++
....exe!boost::interprocess::ipcdetail::basic_managed_memory_impl<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index,8>::destroy<std::basic_string<char,std::char_traits<char>,boost::interprocess::allocator<char,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>>(const char * name) Line 568 C++
....exe!<lambda_da719d1c74e37fe397eecaa291110c18>::operator()<boost::range::index_value<std::string &,__int64>>(const boost::range::index_value<std::string &,__int64> & s_it) Line 222 C++
...
A similar situation without mpi can work correctly as following.
int main(int argc, char *argv[])
{
using namespace boost::interprocess;
typedef allocator<char, managed_shared_memory::segment_manager>
ShmCharAllocator;
typedef std::basic_string<char, std::char_traits<char>, ShmCharAllocator>
ShmStr;
if(argc == 1){ //Parent process
//Remove shared memory on construction and destruction
struct shm_remove
{
shm_remove() { shared_memory_object::remove("MySharedMemory"); }
~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
} remover;
{
//Construct managed shared memory
managed_shared_memory segment(create_only, "MySharedMemory", 65536);
const ShmCharAllocator alloc_inst(segment.get_segment_manager());
//Create an object of MyType initialized to {0.0, 0}
segment.construct<ShmStr>("MyType0")(string("abc"), alloc_inst);
}
//Launch child process
std::string s(argv[0]); s += " child ";
if(0 != std::system(s.c_str()))
return 1;
} else {
{
using namespace std::chrono_literals;
std::this_thread::sleep_for(10s);
}
//Open managed shared memory
managed_shared_memory segment(open_only, "MySharedMemory");
const string str = "MyType0";
auto* res = segment.find<ShmStr>(str.c_str()).first;
string a = string(*res);
//Length should be 1
//if(res.second != 1)
// return 1;
segment.destroy<ShmStr>(str.c_str());
}
return 0;
}
It's hard to see what all the magic numbers mean. It's a big red flag that you're dealing with sizeof(ShmStr) because it's not a trivial type.
It's also "weird" that you're using a managed segment, but try to do all the memory management manually. Why not store an actual queue inside the segment?
Note that you're seemingly emulating the array support using named allocations, but array support is built-in to segment managers.
Finally, I don't use MPI but the way I remember it has builtin support for boost serialization, which means you don't need to copy all data into a string stream, then into a string, that into a shared memory object etc. That's not optimal.
Next up, you're using the shared memory segment from multiple processes (MPI processes) but you're not locking anything. For example, destroy<> would modify the shared object that other processes are still reading. Also, if you are destroying from all processes then you will be destroying things multiple times (Undefined Behaviour).
Finally, if you're fine with
bip::shared_memory_object::remove("MySHM");
in the end, don't bother destroying individual split elements?
Related
How to trigger fops poll function from the kernel driver
I am working on a kernel driver which logs some spi data in a virtual file using debugfs. My main goal is to be able to "listen" for incomming data from userspace using for example $ tail -f /sys/kernel/debug/spi-logs which is using select to wait for new data on the debugfs file. I've implemented the fops poll function in the driver and when I am trying to get the data from the userspace, the poll function is never called even though there is new data available in the kernel to be read. I assume that the poll function never gets called because the debugfs file never gets actually written. My question is, is there a way to trigger the poll function from the kernel space when new data is available? EDIT: Added an example #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/debugfs.h> #include <linux/wait.h> #include <linux/poll.h> struct module_ctx { struct wait_queue_head wq; }; struct module_ctx module_ctx; static ssize_t debugfs_read(struct file *filp, char __user *buff, size_t count, loff_t *off) { // simulate no data left to read for now return 0; } static __poll_t debugfs_poll(struct file *filp, struct poll_table_struct *wait) { struct module_ctx *module_hdl; __poll_t mask = 0; module_hdl = filp->f_path.dentry->d_inode->i_private; pr_info("CALLED!!!"); poll_wait(filp, &module_hdl->wq, wait); if (is_data_available_from_an_external_ring_buffer()) mask |= POLLIN | POLLRDNORM; return mask; } loff_t debugfs_llseek(struct file *filp, loff_t offset, int orig) { loff_t pos = filp->f_pos; switch (orig) { case SEEK_SET: pos = offset; break; case SEEK_CUR: pos += offset; break; case SEEK_END: pos = 0; /* Going to the end => to the beginning */ break; default: return -EINVAL; } filp->f_pos = pos; return pos; } static const struct file_operations debugfs_fops = { .owner = THIS_MODULE, .read = debugfs_read, .poll = debugfs_poll, .llseek = debugfs_llseek, }; static int __init rb_example_init(void) { struct dentry *file; init_waitqueue_head(&module_ctx.wq); file = debugfs_create_file("spi_logs", 0666, NULL, &module_ctx, &debugfs_fops); if (!file) { pr_err("qm35: failed to create /sys/kernel/debug/spi_logs\n"); return 1; } return 0; } static void __exit rb_example_exit(void) { } module_init(rb_example_init); module_exit(rb_example_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mihai Pop"); MODULE_DESCRIPTION("A simple example Linux module."); MODULE_VERSION("0.01"); Using tail -f /sys/kernel/debug/spi_logs, the poll function never gets called
Semantic of poll is to return whenever encoded operations (read and/or write) on a file would return without block. In case of read operation, "block" means: If read is called in nonblocking mode (field f_flags of the struct file has flag O_NONBLOCK set), then it returns -EAGAIN. If read is called in blocking mode, then it puts a thread into the waiting state. As you can see, your read function doesn't follow that convention and returns 0, which means EOF. So the caller has no reason to call poll after that. Semantic of -f option for tail: ... not stop when end of file is reached, but rather to wait ... is about the situation, when read returns 0, but the program needs to wait. As you can see, poll semantic is not suitable for such wait. Instead, such programs use inotify mechanism.
Smart pointer operator[] "no match" when type is array with known length
Given the below code: typedef std::unique_ptr<uint8_t[SHA256::DIGEST_SIZE]> sha256hash; std::ostream& operator<<(std::ostream& os, const sha256hash &hash) { // Save old formatting std::ios oldFormat(nullptr); oldFormat.copyfmt(os); // Set up formatting os << std::setfill('0') << std::setw(2) << std::hex; // Do our printing for (int i = 0;i < SHA256::DIGEST_SIZE; i++) os << hash[i]; // Restore formatting os.copyfmt(oldFormat); } I get the following error: In function ‘std::ostream& operator<<(std::ostream&, const sha256hash&)’: error: no match for ‘operator[]’ (operand types are ‘const sha256hash {aka const std::unique_ptr<unsigned char [32]>}’ and ‘int’) os << hash[i]; I thought that the typedef would give me a smart pointer containing a pointer to an array of uint8_t and so operator[] should be indexing into that array. My best guesses at what's happening is that I'm instead saying that I want a unique_ptr to a pointer to an array of uint8_t. I think I see a couple of ways out of this but I'm not sure which is best typedef std::unique_ptr<uint8_t[]> sha256hash; compiles, but I'm not entirely sure that my overloaded operator won't try to print any unique_ptr to an array of ints. I make a container struct for the int array, and put a unique_ptr around that.
Due to #PeterT's input I ended up going with my second option. A custom deleter seemed too far out of my way, and this was fairly easy to integrate into my already existing code. Here are my changes: //! Light wrapper around SHA256 digest class SHA256Hash { //! The actual digest bits. uint8_t buff[SHA256::DIGEST_SIZE]; public: //! Pointer to a hash. typedef std::unique_ptr<SHA256Hash> ptr; //! Default constructor SHA256Hash() : buff() { } //! Operator to offer convenient buffer access uint8_t &operator[](const uint8_t i) { return buff[i]; } //! Operator to offer convenient buffer access const uint8_t &operator[](const uint8_t i) const { return buff[i]; } //! Offers access to the underlying digest uint8_t *get() { return (uint8_t *) &buff; } }; // Delegate to the version that prints references std::ostream &operator<<(std::ostream &os, const SHA256Hash::ptr &hashp) { os << *hashp; return os; } std::ostream &operator<<(std::ostream &os, const SHA256Hash &hash) { // Save old formatting std::ios oldFormat(nullptr); oldFormat.copyfmt(os); // Set up formatting os << std::setfill('0') << std::setw(2) << std::hex; // Do our printing for (int i = 0;i < SHA256::DIGEST_SIZE; i++) os << (int) hash[i]; // Restore formatting os.copyfmt(oldFormat); return os; }
running boost: lock free queues globally across multiple files
I would like to run a variant of example 46.3 from this website http://theboostcpplibraries.com/boost.lockfree. I am on a linux system. I would like to have the queue q be defined in a header file. I would like to have the produce and consume functions be in different files. So I would like to have global.h contain static boost::lockfree::queue<int> q{100}; static std::atomic<int> sum{0}; void *produce (void*); void *consume (void*); I would then like to have a produce.cpp contain: void *produce( void*) { for (int i = 1; i <= 10000; ++i) q.push(i); } and I would like to have a consume.cpp contain void *consume (void*) { int i; while (q.pop(i)) sum += i; } I would then like to have my main.cpp contain #include iosteam #include iomanip #include global #include pthread int main () {pthread_t t1; pthread_t t2; pthread_t t3; int t1_iret; t1_iret = pthread_create( &t1, NULL, produce, NULL); if(t1_iret) { fprintf(stderr,"Error - pthread_create() return code: %d\n",t1_iret); exit(EXIT_FAILURE); } int t2_iret; t2_iret = pthread_create( &t2, NULL, consume, NULL); if(t2_iret) { fprintf(stderr,"Error - pthread_create() return code: %d\n",t2_iret); exit(EXIT_FAILURE); } int t3_iret; t3_iret = pthread_create( &t3, NULL, consume, NULL); if(t3_iret) { fprintf(stderr,"Error - pthread_create() return code: %d\n",t3_iret); exit(EXIT_FAILURE); } pthread_join( t1, NULL); pthread_join( t2, NULL); pthread_join( t3, NULL); return 0; } Additionally, I was wondering if it would be possible to do what I have described with strings rather then integers. edit1: when I try and make the queue be queue of strings I get:: /usr/local/include/boost/lockfree/queue.hpp: In instantiation of ‘class boost::l ockfree::queue >’: /home/ubuntu/Project/src/main.cpp:15:37: required from here /usr/local/include/boost/lockfree/queue.hpp:87:5: error: static assertion failed : (boost::has_trivial_destructor::value) BOOST_STATIC_ASSERT((boost::has_trivial_destructor::value)); ^ /usr/local/include/boost/lockfree/queue.hpp:91:5: error: static assertion failed : (boost::has_trivial_assign::value) BOOST_STATIC_ASSERT((boost::has_trivial_assign::value)); ^ In file included from /usr/local/include/boost/lockfree/queue.hpp:21:0, from /home/ubuntu/Project/src/main.cpp:5: /usr/local/include/boost/lockfree/detail/copy_payload.hpp: In instantiation of ‘ static void boost::lockfree::detail::copy_constructible_and_copyable::copy(T&, U &) [with T = std::basic_string; U = int]’: /usr/local/include/boost/lockfree/detail/copy_payload.hpp:49:25: required from ‘void boost::lockfree::detail::copy_payload(T&, U&) [with T = std::basic_string ; U = int]’ /usr/local/include/boost/lockfree/queue.hpp:402:61: required from ‘bool boost: :lockfree::queue::pop(U&) [with U = int; T = std::basic_string; A0 = boost::parameter::void_; A1 = boost::parameter::void_; A2 = boost::par ameter::void_]’ /home/ubuntu/Project/src/main.cpp:21:24: required from here /usr/local/include/boost/lockfree/detail/copy_payload.hpp:38:11: error: invalid cast from type ‘std::basic_string’ to type ‘int’ u = U(t);
You need to declare, but not define, your variables in global.h: extern boost::lockfree::queue<int> q; extern std::atomic<int> sum; Then you need to define them in a separate file, global.cpp: boost::lockfree::queue<int> q{100}; std::atomic<int> sum{0}; I think this should fix your issue. For details, see How do I use extern to share variables between source files? As for the second part, asking why you can't make a lock-free queue of strings, well, that is answered by the error message: has_trivial_destructor is false for std::string, because it's a dynamically-sized string which allocates memory. You won't be able to use it in this sort of lock-free queue. You can try using a fixed-size string class instead, or std::array<char, N>.
How to dump/list all kernel symbols with addresses from Linux kernel module?
In a kernel module, how to list all the kernel symbols with their addresses? The kernel should not be re-compiled. I know "cat /proc/kallsyms" in an interface, but how to get them directly from kernel data structures, using functions like kallsyms_lookup_name.
Example Working module code: #include <linux/module.h> #include <linux/kallsyms.h> static int prsyms_print_symbol(void *data, const char *namebuf, struct module *module, unsigned long address) { pr_info("### %lx\t%s\n", address, namebuf); return 0; } static int __init prsyms_init(void) { kallsyms_on_each_symbol(prsyms_print_symbol, NULL); return 0; } static void __exit prsyms_exit(void) { } module_init(prsyms_init); module_exit(prsyms_exit); MODULE_AUTHOR("Sam Protsenko"); MODULE_DESCRIPTION("Module for printing all kernel symbols"); MODULE_LICENSE("GPL"); Explanation kernel/kallsyms.c implements /proc/kallsyms. Some of its functions are available for external usage. They are exported via EXPORT_SYMBOL_GPL() macro. Yes, your module should have GPL license to use it. Those functions are: kallsyms_lookup_name() kallsyms_on_each_symbol() sprint_symbol() sprint_symbol_no_offset() To use those functions, include <linux/kallsyms.h> in your module. It should be mentioned that CONFIG_KALLSYMS must be enabled (=y) in your kernel configuration. To print all the symbols you obviously have to use kallsyms_on_each_symbol() function. The documentation says next about it: /* Call a function on each kallsyms symbol in the core kernel */ int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *, unsigned long), void *data); where fn is your callback function that should be called for each symbol found, and data is a pointer to some private data of yours (will be passed as first parameter to your callback function). Callback function must have next signature: int fn(void *data, const char *namebuf, struct module *module, unsigned long address); This function will be called for each kernel symbol with next parameters: data: will contain pointer to your private data you passed as last argument to kallsyms_on_each_symbol() namebuf: will contain name of current kernel symbol module: will always be NULL, just ignore that address: will contain address of current kernel symbol Return value should always be 0 (on non-zero return value the iteration through symbols will be interrupted). Supplemental Answering the questions in your comment. Also, is there a way to output the size of each function? Yes, you can use sprint_symbol() function I mentioned above to do that. It will print symbol information in next format: symbol_name+offset/size [module_name] Example: psmouse_poll+0x0/0x30 [psmouse] Module name part can be omitted if symbol is built-in. I tried the module and see the result with "dmesg". But a lot of symbols are missing such as "futex_requeue". The output symbol number is about 10K, while it is 100K when I use "nm vmlinux". This is most likely because your printk buffer size is insufficient to store all the output of module above. Let's improve above module a bit, so it provides symbols information via miscdevice. Also let's add function size to the output, as requested. The code as follows: #include <linux/device.h> #include <linux/fs.h> #include <linux/kallsyms.h> #include <linux/module.h> #include <linux/miscdevice.h> #include <linux/sizes.h> #include <linux/uaccess.h> #include <linux/vmalloc.h> #define DEVICE_NAME "prsyms2" /* 16 MiB is sufficient to store information about approx. 200K symbols */ #define SYMBOLS_BUF_SIZE SZ_16M struct symbols { char *buf; size_t pos; }; static struct symbols symbols; /* ---- misc char device definitions ---- */ static ssize_t prsyms2_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { return simple_read_from_buffer(buf, count, pos, symbols.buf, symbols.pos); } static const struct file_operations prsyms2_fops = { .owner = THIS_MODULE, .read = prsyms2_read, }; static struct miscdevice prsyms2_misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &prsyms2_fops, }; /* ---- module init/exit definitions ---- */ static int prsyms2_store_symbol(void *data, const char *namebuf, struct module *module, unsigned long address) { struct symbols *s = data; int count; /* Append address of current symbol */ count = sprintf(s->buf + s->pos, "%lx\t", address); s->pos += count; /* Append name, offset, size and module name of current symbol */ count = sprint_symbol(s->buf + s->pos, address); s->pos += count; s->buf[s->pos++] = '\n'; if (s->pos >= SYMBOLS_BUF_SIZE) return -ENOMEM; return 0; } static int __init prsyms2_init(void) { int ret; ret = misc_register(&prsyms2_misc); if (ret) return ret; symbols.pos = 0; symbols.buf = vmalloc(SYMBOLS_BUF_SIZE); if (symbols.buf == NULL) { ret = -ENOMEM; goto err1; } dev_info(prsyms2_misc.this_device, "Populating symbols buffer...\n"); ret = kallsyms_on_each_symbol(prsyms2_store_symbol, &symbols); if (ret != 0) { ret = -EINVAL; goto err2; } symbols.buf[symbols.pos] = '\0'; dev_info(prsyms2_misc.this_device, "Symbols buffer is ready!\n"); return 0; err2: vfree(symbols.buf); err1: misc_deregister(&prsyms2_misc); return ret; } static void __exit prsyms2_exit(void) { vfree(symbols.buf); misc_deregister(&prsyms2_misc); } module_init(prsyms2_init); module_exit(prsyms2_exit); MODULE_AUTHOR("Sam Protsenko"); MODULE_DESCRIPTION("Module for printing all kernel symbols"); MODULE_LICENSE("GPL"); And here is how to use it: $ sudo insmod prsyms2.ko $ sudo cat /dev/prsyms2 >symbols.txt $ wc -l symbols.txt $ sudo rmmod prsyms2 File symbols.txt will contain all kernel symbols (both built-in and from loaded modules) in next format: ffffffffc01dc0d0 psmouse_poll+0x0/0x30 [psmouse] It seems that I can use kallsyms_lookup_name() to find the address of the function, can then use a function pointer to call the function? Yes, you can. If I recall correctly, it's called reflection. Below is an example how to do so: typedef int (*custom_print)(const char *fmt, ...); custom_print my_print; my_print = (custom_print)kallsyms_lookup_name("printk"); if (my_print == 0) { pr_err("Unable to find printk\n"); return -EINVAL; } my_print(KERN_INFO "### printk found!\n");
boost::interprocess Containers of containers NOT in shared memory
I have the example demo program with a boost::interprocess Containers of containers type. But I like to use the class also a normal class within my process memory. Can someone help me to write a constructor which takes no arguments to have the class initialized in my current process memory. #include <boost/interprocess/containers/vector.hpp> #include <boost/interprocess/allocators/allocator.hpp> #include <boost/interprocess/managed_shared_memory.hpp> #include <boost/archive/xml_oarchive.hpp> #include <boost/archive/xml_iarchive.hpp> #include <shmfw/serialization/interprocess_vector.hpp> #include <stdlib.h> /* srand, rand */ #include <time.h> /* time */ using namespace boost::interprocess; //Alias an STL-like allocator of ints that allocates ints from the segment typedef allocator<int, managed_shared_memory::segment_manager> ShmemAllocator; //Alias a vector that uses the previous STL-like allocator typedef vector<int, ShmemAllocator> MyVector; typedef allocator<void, managed_shared_memory::segment_manager > void_allocator; class MyStruct { public: MyVector myVector; //Since void_allocator is convertible to any other allocator<T>, we can simplify //the initialization taking just one allocator for all inner containers. MyStruct ( const void_allocator &void_alloc ) : myVector ( void_alloc ) {} // Thats what I like to have //MyStruct () // : myVector ( ?? ) //{} }; int main () { // I would like to have something like that working and also the shm stuff below // MyStruct x; managed_shared_memory segment; //A managed shared memory where we can construct objects //associated with a c-string try { segment = managed_shared_memory( create_only, "MySharedMemory", 65536 ); } catch (...){ segment = managed_shared_memory( open_only, "MySharedMemory" ); } //Initialize the STL-like allocator const ShmemAllocator alloc_inst ( segment.get_segment_manager() ); MyStruct *myStruct_src = segment.find_or_construct<MyStruct> ( "MyStruct" ) ( alloc_inst ); srand (time(NULL)); myStruct_src->myVector.push_back ( rand() ); MyStruct *myStruct_des = segment.find_or_construct<MyStruct> ( "MyStruct" ) ( alloc_inst ); for ( size_t i = 0; i < myStruct_src->myVector.size(); i++ ) { std::cout << i << ": " << myStruct_src->myVector[i] << " = " << myStruct_des->myVector[i] << std::endl; if(myStruct_src->myVector[i] != myStruct_des->myVector[i]) { std::cout << "Something went wrong!" << std::endl; } } //segment.destroy<MyVector> ( "MyVector" ); return 0; }
If you change the allocator type, you change the container (such is the nature of compile-time template instantiation). Technically, you could devise a type-erased allocator (à la std::function or boost::any_iterator) but this would probably result in abysmal performance. Also, it would still require all the allocators to correspond in all the statically known properties, reducing flexibility. In reality, I suggest just templatizing MyStruct on the Allocator type to be used for any embedded containers. Then specifically take such an allocator in the constructor: // Variant to use on the heap: using HeapStruct = MyStruct<std::allocator>; // Variant to use in shared memory: using ShmemStruct = MyStruct<BoundShmemAllocator>; Demo Program: #include <boost/interprocess/containers/vector.hpp> #include <boost/interprocess/allocators/allocator.hpp> #include <boost/interprocess/managed_shared_memory.hpp> #include <boost/range/algorithm.hpp> #include <iostream> #include <cassert> namespace bip = boost::interprocess; template <typename T> using BoundShmemAllocator = bip::allocator<T, bip::managed_shared_memory::segment_manager>; /////////////////////////////////////////////////////////////// // Your MyStruct, templatized for an Allocator class template template <template<typename...> class Allocator> class MyStruct { public: bip::vector<int, Allocator<int> > ints; bip::vector<double, Allocator<double> > doubles; MyStruct(const Allocator<void>& void_alloc = {}) : ints(void_alloc), doubles(void_alloc) {} }; // Variant to use on the heap: using HeapStruct = MyStruct<std::allocator>; // Variant to use in shared memory: using ShmemStruct = MyStruct<BoundShmemAllocator>; // /////////////////////////////////////////////////////////////// int main() { srand(time(NULL)); // You can have something like this working: HeapStruct x; // and also the shm stuff below std::generate_n(std::back_inserter(x.ints), 20, &std::rand); std::generate_n(std::back_inserter(x.doubles), 20, &std::rand); // A managed shared memory where we can construct objects bip::managed_shared_memory segment = bip::managed_shared_memory(bip::open_or_create, "MySharedMemory", 65536); BoundShmemAllocator<int> const shmem_alloc(segment.get_segment_manager()); auto src = segment.find_or_construct<ShmemStruct>("MyStruct")(shmem_alloc); src->ints.insert(src->ints.end(), x.ints.begin(), x.ints.end()); src->doubles.insert(src->doubles.end(), x.doubles.begin(), x.doubles.end()); auto des = segment.find_or_construct<ShmemStruct>("MyStruct")(shmem_alloc); std::cout << "-------------------------"; boost::copy(src->ints, std::ostream_iterator<int>(std::cout << "\nsrc ints: ", "; ")); boost::copy(des->ints, std::ostream_iterator<int>(std::cout << "\ndes ints: ", "; ")); std::cout << "\n-------------------------"; boost::copy(src->doubles, std::ostream_iterator<double>(std::cout << "\nsrc doubles: ", "; ")); boost::copy(des->doubles, std::ostream_iterator<double>(std::cout << "\ndes doubles: ", "; ")); assert(src->ints.size() == des->ints.size()); assert(src->doubles.size() == des->doubles.size()); assert(boost::mismatch(src->ints, des->ints) == std::make_pair(src->ints.end(), des->ints.end())); assert(boost::mismatch(src->doubles, des->doubles) == std::make_pair(src->doubles.end(), des->doubles.end())); segment.destroy<ShmemStruct>("MyStruct"); }