Question:
How to change non-PoD fields in case of using shared memory? As far as I understand, for PoD types such an approach "head-on" works and the class field changes in the memory allocated in mmap. However, the field of type string will remain unchanged in this case. How can it be changed? Ps The question is purely educational, so if you bring some theoretical basis under the answer, it will be great.
UPD: non-PoD field remains unchanged in the parent process. It does change in the child process, but this raises even more questions 🙂
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
using namespace std;
class human
{
public:
string name;
int age;
human()
{
name = '\0';
age = 0;
}
~human() {}
void set()
{
cout << "Enter name" << endl;
cin >> name;
cout << "Enter age" << endl;
cin >> age;
}
void print()
{
cout << name << " " << age << endl;
}
};
main(int argc, char * argv[])
{
pid_t cpid;
char buf;
human* shared;
human non;
shared = (human*)mmap(NULL, sizeof(*shared), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
*shared = non;
cpid = fork(); // duplicate the current process
if (cpid == 0) // if I am the child then
{
shared->set();
exit(EXIT_SUCCESS);
}
else // if I am the parent then
{
wait(NULL); // wait for the child process to exit before I do the same
cout << "Human:" << endl;
shared->print();
exit(EXIT_SUCCESS);
}
return 0;
}
Answer:
You can't do that. Serialize the data to shared memory and then deserialize it on output.
std::string
can contain a pointer to external memory, so the fact that you have allocated memory for the string object itself in shared memory does not mean anything – the data is in a completely different place.
Of course, it can coincide so that by leaving the code unchanged and changing the line in one process, this change will be visible in another, but this is a special case. It is caused by the fact that almost (?) All popular implementations of std::string
use the so-called. SSO (small string optimization), in which the entire string is stored in the object itself, at its small size. But if you write more to a string than the internal buffer allows, the entire string will necessarily be moved to the process heap.
After that, it won't be so easy to get through to this line. And, trying to read or write something to a string in the second process, you get UB, because the address of the string contained in std::string
points to the memory area allocated in the first process. In general, in the second process this address will not be occupied by anyone, or it will be occupied by a completely different object – in any of these situations, the address to this address as to std::string
will be UB (if it does not match, of course, that there there will be another line, at exactly the same address and the same size, but I will not take into consideration such "miracles".
If we abstract from the example, then there is a simple rule: if you want to exchange data with any external entity (be it a process or something else), first save it in some format common to both entities so that the receiving party can accurately determine what came to her. It is impossible to use simple objects of the C ++ language in such interaction, since too many places to "slip".