I have the following program to exercise using forks and shared memory.
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <sys/ipc.h>
int main() {
int bignum = 1000000;
key_t key = ftok(".", 'x');
int shmid = shmget(key, sizeof(int)*bignum, IPC_CREAT | 0666);
if (shmid < 0) {
perror("shmget\n");
return 1;
}
int *arr = shmat(shmid, NULL, 0);
pid_t c1 = fork();
if (c1==0) {
pid_t c2 = fork();
if (c2==0) {
pid_t c3 = fork();
if (c3==0) {
arr[0] = 10;
} else {
arr[1] = 11;
}
wait(NULL);
exit(0);
} else {
arr[2] = 12;
}
wait(NULL);
exit(0);
} else {
arr[3] = 13;
wait(NULL);
for (int i=0; i<4; i++) printf("%d ", arr[i]);
printf("\n");
}
shmdt(arr);
shmctl(shmid, IPC_RMID, NULL);
exit(0);
}
I previously ran this program using a smaller amount of shared memory and have now increased it to see if that affects the program in any way. When I run this I get:
shmget
: Invalid argument
I've seen this post: C linux shmget Invalid argument
I've tried following the advice, but I'm unfamiliar with ipcs
and ipcrm
. I ran ipcs
in a terminal and it gave me some shared memory info. But I can't tell which is the one allocated by the program, there's a lot, and I can't tell what is safe to remove or not.
It also seems odd to me that you wouldn't just do this inside the C program itself, so that makes me wonder if there isn't a better way to resolve this issue. I particularly don't get why the calls to shmdt
and shmctl
don't make this a non-issue. Is there some other way to "undo" memory sharing?
Edit: The question was closed because it is similar to this:
C - System V - remove shared memory segment
However, that uses code that is already in my code -- so it doesn't seem to answer the question.
Edit: Here is my updated code based on an answer. It does resolve the previous issue, but now after some error checking, raises a permission error.
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <sys/ipc.h>
int main() {
size_t bignum = 1000000*sizeof(int);
key_t key = ftok(".", 'x');
int shmid = shmget(key, bignum, IPC_CREAT | 0666);
if (shmid == -1) {
shmctl(shmid, IPC_RMID, NULL);
shmid = shmget(key, bignum, IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget");
exit(1);
}
}
int *arr = shmat(shmid, NULL, 0);
if (arr == (int *)-1) {
perror("shmat");
exit(1);
}
shmdt(arr);
shmctl(shmid, IPC_RMID, NULL);
shmid = shmget(key, bignum, IPC_CREAT | 0666);
arr = shmat(shmid, NULL, 0);
pid_t c1 = fork();
if (c1==0) {
pid_t c2 = fork();
if (c2==0) {
pid_t c3 = fork();
if (c3==0) {
arr[0] = 10;
} else {
arr[1] = 11;
}
wait(NULL);
exit(0);
} else {
arr[2] = 12;
}
wait(NULL);
exit(0);
} else {
arr[3] = 13;
wait(NULL);
for (int i=0; i<4; i++) printf("%d ", arr[i]);
}
if (shmdt(arr) == -1) {
perror("shmdt");
exit(1);
}
if (shmctl(shmid, IPC_RMID, NULL) == -1) {
perror("shmctl");
exit(1);
}
exit(0);
}
Specifically, when I run this it results in
shmget: Permission denied
The old program left a smaller shared memory segment lingering after it exited so attaching to it, requesting a bigger size, fails. You can start your new program by removing it:
key_t key = ftok(".", 'x');
int shmid = shmget(key, 1, 0666); // no IPC_CREAT
if (shmid != -1) { // ok, there was a memory segment there already
shmctl(shmid, IPC_RMID, NULL); // remove it
}
// now create the new one
shmid = shmget(key, sizeof(int)*bignum, IPC_CREAT | 0666);