Shared Memory between two processes after exec - c

Parent:
shm_id = shmget(IPC_PRIVATE, (1 << 16), IPC_CREAT | IPC_EXCL | 0777);
setenv("SOME_ENV_VAR",stringof(shm_id);
if(fork()=0){
execve(some_path,argv);
}
Child:
int shm_id = atoi(getenv("SOME_ENV_VAR"));
int *shared_mem = (int*)shmat(shm_id,0,NULL);
if(!shared_mem)
return;
shared_mem[0]++;
I want to edit the shared memory in the child. Any reasons why this should not work? I am allocating the shared mem block via shmget in the Parent.Im placing the shm_id as an env variable for the child to read it after the fork and exec.
In the child, I am reading the proper shm_id then trying to get a pointer to the shared memory via shmat. In my code I have verified the shm_id in Parent and Child are the same... Any ideas?

The key_t argument to shmget is not the same as the identifier that that function returns. It’s not sensible to substitute one for the other.
However, if you change that and communicate the shmid instead of the key, your basic approach will work.
The shmid is a system-wide global identifier, and shmat will succeed if you have the appropriate process permissions, even if you are an unrelated process. (And, even if you are related, an execve will detach any shared memory segments, requiring an explicit re-attach.)
Note that the spec is not terribly explicit about this, saying that "[e]ach individual shared memory segment ... shall be identified by a unique positive integer, called ... a shared memory identifier, shmid.".

On OS level segments are identified by the key, the ID is local to a process only. Each process needs to do a get (passing them same key) and an at to use the memory.
An example here: http://www.csl.mtu.edu/cs4411.ck/www/NOTES/process/shm/shmat.html

Related

Is it possibility that one shared memory segment will attach multiple time to same parent pid?

I have created 200 child processes of a parent which communicate through share memory IPC mechanism:
Parent <-> SHM <-> child
But the observation is STRANGE.
I found 4 processes are attached to the same SHM id in which 2 are parent pid and 2 are child pid.(Unexpected Behaviour).
and somewhere 2 processes are attached to one SHM id (Expected behaviour).
Please found below output-
-bash-4.2# grep 123076652 /proc/*/maps
/proc/27750/maps:7f1323576000-7f1323577000 rw-s 00000000 00:04 123076652 /SYSV2c006eff (deleted)
/proc/27750/maps:7f1323676000-7f1323677000 rw-s 00000000 00:04 123076652 /SYSV2c006eff (deleted)
/proc/27827/maps:7f87ac3c0000-7f87ac3c1000 rw-s 00000000 00:04 123076652 /SYSV2c006eff (deleted)
/proc/28090/maps:7f9d33b8b000-7f9d33b8c000 rw-s 00000000 00:04 123076652 /SYSV2c006eff (deleted)
As we can easily see that PID-27750(Parent) attached two times to one segment.
How is it possible ?
Is it Centos Bug?
To answer your question: Yes of course. You have the evidence right there in your question.
How does it happen? If you call mmap() on the same file multiple times it maps it multiple times.
To avoid that happening the answer is: Don't do that.
I'm purely guessing but my bet is that one of your fork() calls failed and you never did any error checking, and the code continued on to execute the child code in the parent process. That would explain having two maps on one PID.
I found the problem i was using same id to generate the ftok() key for two child processes.
A key_t is 32 bits, and can have any value we want.
ftok is just a "convenience" function to generate a unique key_t value to be used in a call to shmget (See below).
If we use IPC_PRIVATE for this key_t value, we get a private descriptor that any child of a single parent process can use. It shows up in ipcs as a key_t of 0, with a unique shmid [which is like a file descriptor].
So, if have a single parent and we're just doing fork, we can use this, because the children will inherit this from the parent. This is the preferred method. So, in this case, ftok isn't needed.
With private keys, when all attached processes terminate, the shared areas are automatically removed by the kernel.
If we use a non-zero key_t value, we are creating a permanent area. It will remain (with the data still there).
To remove this, the final process (i.e. the parent process) must do shmctl(shmid,IPC_RMID,NULL) for all shmid gotten from the shmget calls.
If the parent process dies before doing this, the area remains. Such areas will still show up in ipcs
Here is some sample code that illustrates the use of IPC_PRIVATE. It can be adapted to use a non-zero key_t value, but for your application, that may not be warranted:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#define NCHILD 200
#define SIZE 4096
// child process control
typedef struct {
int tsk_cldidx; // child index
int tsk_shmid; // shmid for process
pid_t tsk_pid; // pid of process
void *tsk_shmadr; // address of attached area
} tsk_t;
tsk_t cldlist[NCHILD];
void
dofork(int cldidx)
{
tsk_t *tskself = &cldlist[cldidx];
do {
tskself->tsk_pid = fork();
// parent
if (tskself->tsk_pid != 0)
break;
// detach from all areas that are not ours
for (cldidx = 0; cldidx < NCHILD; ++cldidx) {
tsk_t *tsk = &cldlist[cldidx];
if (tsk->tsk_cldidx != tskself->tsk_cldidx)
shmdt(tsk->tsk_shmadr);
}
// do something useful ...
exit(0);
} while (0);
}
int
main(void)
{
int cldidx;
tsk_t *tsk;
// create private shared memory ids for each child
for (cldidx = 0; cldidx < NCHILD; ++cldidx) {
tsk = &cldlist[cldidx];
tsk->tsk_cldidx = cldidx;
tsk->tsk_shmid = shmget(IPC_PRIVATE,SIZE,0);
tsk->tsk_shmadr = shmat(tsk->tsk_shmid,NULL,0);
}
// start up all children
for (cldidx = 0; cldidx < NCHILD; ++cldidx)
dofork(cldidx);
// do something useful with children ...
// wait for all children
while (wait(NULL) >= 0);
// remove all segments
// NOTE: with IPC_PRIVATE, this may not be necessary -- it may happen
// automatically when we exit
for (cldidx = 0; cldidx < NCHILD; ++cldidx) {
tsk = &cldlist[cldidx];
shmctl(tsk->tsk_shmid,IPC_RMID,NULL);
}
return 0;
}
If we have separate programs that have no parent/child relationship, we need a non-zero key_t. It can be hard to generate a unique key_t that does not collide/conflict with another, possibly from a totally unrelated group of programs (e.g. another user)
Can you please explain how much maximum unique keys can be generated using ftok().
AFAIK only last low 8 bits are significant. Can we use can we use 2 byte integer like "300" to generate the key . What is the chance for keys duplicate here?
For a given [existing] file and eight bit proj_id, there are [as you've noticed] only 256 unique keys that can be generated. We'd need a different file argument to get the next 256.
It might be better to dispense with ftok altogether [I've never used it when using shmget]. I've done 0xAA000000 as the base key_t value. I've replaced the zeroes with whatever unique sub-key value I need (there are ~24 million possible combinations).
If we control all programs that will access the shared memory areas, it isn't necessary to have multiple areas.
It may be sufficient, and more desirable to have a single shared memory area. In that case, we only do one shmget and one shmat. Then, ftok(myfile,0) could produce a nice key.
If the size of the needed to communicate with a child is (e.g.) a page (PER_CHILD = 4096), and we have NCHILD children to create, we can just create a single area of TOTAL_SIZE = PER_CHILD * NCHILD in size. Then, for a given child X, its area pointer is shmaddr + (X * PER_CHILD)
UPDATE:
Can I use IPC_CREAT flag and do exec() call to child?
I think what you mean is using the non-zero key with shmget in conjunction with this.
An exec call will close any mappings: shared memory after exec()
It will also close the file descriptor returned by shmget [or shm_open].
So, using the non-zero key is the only [practical] way to ensure that it works across execvp et al.
Will it cause any problem. AFAIK if we use exec() then a process will have a different address space. Will it cause any problem?
The child program will have to (re)establish its own mapping via shmget and shmat.
But, if we use shm_open [instead of shmget], we can keep the file descriptor open if we use fcntl to clear the FD_CLOEXEC flag on the descriptor before calling execvp.
However, this may be of little use as the child program (target of execvp) will [probably] not know the file descriptor number that the parent opened with shm_open, so it's a bit of a moot point.

How to create shared memory after fork or in child process?

How to create shared memory after fork or in child process?
I want to first make a global pointer in shared memory, then in child process create multiple node dynamically and add the node to this global pointer.
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
struct shm_t {
int data;
struct shm_t *next;
};
void main() {
struct shm_t *shm = (struct shm_t *)mmap(NULL, sizeof(*shm), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
shm->data = 1;
shm->next = NULL;
int pid = fork();
if (pid == 0) {
printf("Child \n");
// How to create a shared shm_t here, and assign to *shm->next?
} else {
printf("Parent \n");
}
}
In order for two processes to share memory, they have to have something in common.
Your first allocation is shared because you allocated it with MAP_SHARED, and the child process inherited it from the parent. Once the fork already finished, there is no way for that to happen automatically (just one example: which process does the child share this with? The parent is just a random process with no special significance, as far Linux is concerned).
The way to do this properly is be sharing a file descriptor. The number of pitfalls here, however, is staggering.
Let's start with the solution first, and then understand why it won't work:
// Before the fork
int shared_fd = open("/dev/shm/somefile", O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600);
// Check for errors
unlink("/dev/shm/somefile");
// check for errors
// Now you can fork
If you now use mmap from this file, it is possible to share it between the processes (i.e. - one process' writes will be visible to the other process).
Like I said, the number of pitfalls here is huge:
The file is created empty, and you cannot mmap past the end of a file. In order for this to work, you will need to increase the file using ftruncate. Here is a problem, though. The calls to ftruncate will need to be synchronized, or you will have the lost update problem, where both processes think they are increasing the file to the same size together.
Even after you do that, you still cannot store pointers inside the memory. The reason is that the memory is shared, in the sense that one process' writes are immediately visible to the other. There is no guarantee, however, that they will be mapped to the same addresses.
I'm not sure what you want to do here (if you want to share a linked list between two processes, you have huge synchronization issues to handle here as well). If you can provide a-priori limit on how much memory you're going to need, just pre-allocate it with the original mmap.
In all likelihood, however, what you're trying to achieve here isn't possible in the way you're trying to achieve it (not in any sane way).

Cygwin/Cygserver Shared memory

I was trying to migrate some shared memory code from CENTOS(3.5) to CYGWIN(2.8.1, win10).
the shared memory generally work like this:
Spawn a shared memory at a process by shmget.
Map the shared memory on this process by the shmat and record the location, then fill some information into the memory.
Map the shared memory on another process by the "shmat", pass the location of last process recorded, because we expect that both processes will mapping the shared memory at the same address.
Here are some code to explain:
// one process
size_t size = 1024 * 1024;//1M
int id = shmget(IPC_PRIVATE, size, 0660);
char *madr = 0;
char *location = shmat(id, madr, 0);
// another process
char *location1 = shmat(id, location , 0);
// !!!we hope location1 and location should be the same!!!
On Centos it works well.
On Cygwin one process mapped the shared memory at 0xffd90000, another process is not same with it but mapped at oxffdb0000. we check that the memory 0xffd90000 is available on that process.
Wrong expectation also on Linux, see
https://linux.die.net/man/2/shmat
Be aware that the shared memory segment attached in this way may be
attached at different addresses in different processes. Therefore, any
pointers maintained within the shared memory must be made relative
(typically to the starting address of the segment), rather than
absolute.

how memory area is shared between processes [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
How memory is shared in following scenarios?
Between Parent and child Processes
Between two irrelevant Processes
In which part of the physical memory does the shared memory (or) any other IPC used for communicating between processes exists?
Here it the program with explanation of Memory management between Parent and Child Process..
/*
SHARING MEMORY BETWEEN PROCESSES
In this example, we show how two processes can share a common
portion of the memory. Recall that when a process forks, the
new child process has an identical copy of the variables of
the parent process. After fork the parent and child can update
their own copies of the variables in their own way, since they
dont actually share the variable. Here we show how they can
share memory, so that when one updates it, the other can see
the change.
*/
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h> /* This file is necessary for using shared
memory constructs
*/
main()
{
int shmid. status;
int *a, *b;
int i;
/*
The operating system keeps track of the set of shared memory
segments. In order to acquire shared memory, we must first
request the shared memory from the OS using the shmget()
system call. The second parameter specifies the number of
bytes of memory requested. shmget() returns a shared memory
identifier (SHMID) which is an integer. Refer to the online
man pages for details on the other two parameters of shmget()
*/
shmid = shmget(IPC_PRIVATE, 2*sizeof(int), 0777|IPC_CREAT);
/* We request an array of two integers */
/*
After forking, the parent and child must "attach" the shared
memory to its local data segment. This is done by the shmat()
system call. shmat() takes the SHMID of the shared memory
segment as input parameter and returns the address at which
the segment has been attached. Thus shmat() returns a char
pointer.
*/
if (fork() == 0) {
/* Child Process */
/* shmat() returns a char pointer which is typecast here
to int and the address is stored in the int pointer b. */
b = (int *) shmat(shmid, 0, 0);
for( i=0; i< 10; i++) {
sleep(1);
printf("\t\t\t Child reads: %d,%d\n",b[0],b[1]);
}
/* each process should "detach" itself from the
shared memory after it is used */
shmdt(b);
}
else {
/* Parent Process */
/* shmat() returns a char pointer which is typecast here
to int and the address is stored in the int pointer a.
Thus the memory locations a[0] and a[1] of the parent
are the same as the memory locations b[0] and b[1] of
the parent, since the memory is shared.
*/
a = (int *) shmat(shmid, 0, 0);
a[0] = 0; a[1] = 1;
for( i=0; i< 10; i++) {
sleep(1);
a[0] = a[0] + a[1];
a[1] = a[0] + a[1];
printf("Parent writes: %d,%d\n",a[0],a[1]);
}
wait(&status);
/* each process should "detach" itself from the
shared memory after it is used */
shmdt(a);
/* Child has exited, so parent process should delete
the cretaed shared memory. Unlike attach and detach,
which is to be done for each process separately,
deleting the shared memory has to be done by only
one process after making sure that noone else
will be using it
*/
shmctl(shmid, IPC_RMID, 0);
}
}
/*
POINTS TO NOTE:
In this case we find that the child reads all the values written
by the parent. Also the child does not print the same values
again.
1. Modify the sleep in the child process to sleep(2). What
happens now?
2. Restore the sleep in the child process to sleep(1) and modify
the sleep in the parent process to sleep(2). What happens now?
Thus we see that when the writer is faster than the reader, then
the reader may miss some of the values written into the shared
memory. Similarly, when the reader is faster than the writer, then
the reader may read the same values more than once. Perfect
i /*
SHARING MEMORY BETWEEN PROCESSES
In this example, we show how two processes can share a common
portion of the memory. Recall that when a process forks, the
new child process has an identical copy of the variables of
the parent process. After fork the parent and child can update
their own copies of the variables in their own way, since they
dont actually share the variable. Here we show how they can
share memory, so that when one updates it, the other can see
the change.
*/
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h> /* This file is necessary for using shared
memory constructs
*/
main()
{
int shmid. status;
int *a, *b;
int i;
/*
The operating system keeps track of the set of shared memory
segments. In order to acquire shared memory, we must first
request the shared memory from the OS using the shmget()
system call. The second parameter specifies the number of
bytes of memory requested. shmget() returns a shared memory
identifier (SHMID) which is an integer. Refer to the online
man pages for details on the other two parameters of shmget()
*/
shmid = shmget(IPC_PRIVATE, 2*sizeof(int), 0777|IPC_CREAT);
/* We request an array of two integers */
/*
After forking, the parent and child must "attach" the shared
memory to its local data segment. This is done by the shmat()
system call. shmat() takes the SHMID of the shared memory
segment as input parameter and returns the address at which
the segment has been attached. Thus shmat() returns a char
pointer.
*/
if (fork() == 0) {
/* Child Process */
/* shmat() returns a char pointer which is typecast here
to int and the address is stored in the int pointer b. */
b = (int *) shmat(shmid, 0, 0);
for( i=0; i< 10; i++) {
sleep(1);
printf("\t\t\t Child reads: %d,%d\n",b[0],b[1]);
}
/* each process should "detach" itself from the
shared memory after it is used */
shmdt(b);
}
else {
/* Parent Process */
/* shmat() returns a char pointer which is typecast here
to int and the address is stored in the int pointer a.
Thus the memory locations a[0] and a[1] of the parent
are the same as the memory locations b[0] and b[1] of
the parent, since the memory is shared.
*/
a = (int *) shmat(shmid, 0, 0);
a[0] = 0; a[1] = 1;
for( i=0; i< 10; i++) {
sleep(1);
a[0] = a[0] + a[1];
a[1] = a[0] + a[1];
printf("Parent writes: %d,%d\n",a[0],a[1]);
}
wait(&status);
/* each process should "detach" itself from the
shared memory after it is used */
shmdt(a);
/* Child has exited, so parent process should delete
the cretaed shared memory. Unlike attach and detach,
which is to be done for each process separately,
deleting the shared memory has to be done by only
one process after making sure that noone else
will be using it
*/
shmctl(shmid, IPC_RMID, 0);
}
}
/*
POINTS TO NOTE:
In this case we find that the child reads all the values written
by the parent. Also the child does not print the same values
again.
1. Modify the sleep in the child process to sleep(2). What
happens now?
2. Restore the sleep in the child process to sleep(1) and modify
the sleep in the parent process to sleep(2). What happens now?
Thus we see that when the writer is faster than the reader, then
the reader may miss some of the values written into the shared
memory. Similarly, when the reader is faster than the writer, then
the reader may read the same values more than once. Perfect
inter-process communication requires synchronization between the
reader and the writer. You can use semaphores to do this.
Further note that "sleep" is not a synchronization construct.
We use "sleep" to model some amount of computation which may
exist in the process in a real world application.
Also, we have called the different shared memory related
functions such as shmget, shmat, shmdt, and shmctl, assuming
that they always succeed and never fail. This is done to
keep this proram simple. In practice, you should always check for
the return values from this function and exit if there is
an error.
*/nter-process communication requires synchronization between the
reader and the writer. You can use semaphores to do this.
Further note that "sleep" is not a synchronization construct.
We use "sleep" to model some amount of computation which may
exist in the process in a real world application.
Also, we have called the different shared memory related
functions such as shmget, shmat, shmdt, and shmctl, assuming
that they always succeed and never fail. This is done to
keep this proram simple. In practice, you should always check for
the return values from this function and exit if there is
an error.
*/

fork and exec with respect to locking shared memory - C

So I'm just wondering if I had a simple task to do in concurrency, how would I do this with multiple processes using fork() and exec() from a parent process, while locking some aspects of the parent process' memory (so that they don't overwrite each other), but making it available to those processes later?
I know I can do this with POSIX threads with their mutex locks, but what's the process equivalent to that? Is there a way to "lock" shared memory amongst threads? And then would I have to "wait()" for the other threads to finish those locked areas of memory before the other threads could access it?
If you're using the pthreads implementation of mutexes, you would still use them to synchronize between processes... you would place them in shared memory. Initializing a pthread mutex in shared memory addresses this.
You can also use a simple pipe to synchronize access -- pre-fill the pipe with a token and require a successful read of the token to permit resource access. Then write the token back into the pipe in order to release the resource.
First: if you call exec and it succeeds then your process image will be overwritten. You will loose any shared memory and you will need to set it up with your favourite shared memory paradigm (e.g. posix shared memory shm_open).
If you fork then any memory that was mapped shared will remain shared. Means you can place your favourite mutex (e.g. pthread_mutex_t, sem_t) into it and use it with the standard functions that go with it.
void * shared_memory = mmap(
NULL // anywhere
, sysconf(_SC_PAGESIZE) // mmap only works in chunks of pages
// typically 0x1000
, PROT_READ | PROT_WRITE // read-write
, MAP_SHARED // shared
| MAP_ANONYMOUS // anonymous, non-file backed
#ifdef MAP_HASSEMAPHORE
| MAP_HASSEMAPHORE // OS X requires this flag in case you
// intend to have semaphores in that segment
#endif
, -1 // no file backing
, 0
);
if (shared_memory == MAP_FAILED) {
perror("mmap");
abort();
}
// we use that memory to place a mutex tehre
pthread_mutex_t * mutex = shared_memory;
pthread_mutex_init(mutex, NULL);
pid_t pid = fork();
if (pid < 0) {
perror("fork");
abort();
}
if (!pid) {
// child goes here
// use the mutex here
} else {
// parent goes here
// use the mutex here
}

Resources