2

I'm trying to learn the IPC UNIX APIs, specifically shared memory. I have created this small program that tries to either access the shared memory segment or create one.

This is what I do:

gcc -Wall -Wextra *.c
# in one terminal
./a.out
# in another
/a.out

The shared.mem file you can see in the source IS present in the same directory from which I launch the executable.

However, it seems like I'm never actually accessing a previously created shared memory segment (error is "No such file or directory"). I always create a new one - as seen via the ipcs command line, even though the IPC key stays the same.

What am I doing wrong ?

Below is the code I used, for reference. It compiles at least on Linux.

#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define exit_error(what) exit_error_func(what, __FILE__, __LINE__)

#define SHM_SIZE (64)
#define UNUSED(x) (void)(x)

void    *shm_addr = NULL;

void    exit_error_func(const char *what, const char *file, int line)
{
    fprintf(stderr, "Error in %s at line %d: %s. Reason: %s.\n", file, line, what, strerror(errno));
    exit(1);
}

void    sigint_handler(int sig)
{
    shmdt(shm_addr);
    UNUSED(sig);
}

int main(void)
{
    key_t           ipc_key;
    int             shm_id;

    if ((ipc_key = ftok("shared.mem", 1)) == -1)
        exit_error("could not get IPC key");
    printf("IPC key is %d\n", ipc_key);
    if ((shm_id = shmget(ipc_key, SHM_SIZE, 0600)) == -1)
    {
        printf("could not get SHM id, trying to create one now\n");
        if ((shm_id = shmget(ipc_key, SHM_SIZE, IPC_EXCL | IPC_CREAT | 0600)) == -1)
            exit_error("could not create or get shared memory segment");
        else
            printf("created SHM id\n");
    }
    else
        printf("got already existing SHM id\n");
    printf("SHM id is %d\n", shm_id);
    if ((shm_addr = shmat(shm_id, NULL, 0)) == (void *)-1)
        exit_error("could not attach to segment");
    signal(SIGINT, sigint_handler);
    if (shmctl(shm_id, IPC_RMID, NULL) == -1)
        exit_error("could not flag shared memory for deletion");
    printf("SHM flagged for deletion\n");
    while (1)
        sleep(1);
    return (0);
}
conradkleinespel
  • 6,560
  • 10
  • 51
  • 87
  • What is the precise error you are getting from which line of which invocation of the program? – msw May 26 '14 at 20:13
  • The first call to `shmget` fails and sets `errno` to `ENOENT`. – conradkleinespel May 26 '14 at 20:14
  • Mmmmh strange since (as you said) we have: `#define ENOENT 2 /* No such file or directory */` Your code works perfectly on my linux ... try deleting and recreating shared.mem (touch shared.mem). When you say "The shared.mem file you can see in the source IS present in the same directory from which I launch the executable." I imagine you mean a.out and shared.mem are in the same directory? – smagnan May 26 '14 at 21:31
  • Yes, `shared.mem` is in the same folder as `a.out`. I have tried recreating it, but still, the first call to `shmget` always fails. I always get something like this (added the errno return and `perror below): http://pastebin.com/ufVrpawh – conradkleinespel May 26 '14 at 21:38
  • For some reason I don't understand, it works if I remove the call to `shmctl`. Maybe I need to flag for deletion after I do the last `shmdt`? This would require a bit more trickery to know which process is the last to detach through `shmctl(..., IPC_STAT, ...)`. – conradkleinespel May 27 '14 at 09:54

1 Answers1

0

It appears that it is not possible to shmget a shared memory segment that is flagged for deletion. Therefore, the shared memory segment must be marked for deletion once no process needs to shmget it anymore.

Disclaimer: I am no UNIX expert. Although the proposed solution works for me, I am still learning and cannot guarantee accuracy of the information given here.

conradkleinespel
  • 6,560
  • 10
  • 51
  • 87