shmget confusion?????

Hi friends,
This is a small program built on the concept of shared memory. The producer is a separate program and process, and the consumer is a seperate program and process. Both are executed under the same user account. The producer takes some string from the user and adds that string to the shared memory, and the consumer reads that string. You can see my code here.

 
$ cat producer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#define MEM_SZ 4096
struct shared_use_st
{
        int written_by_you;
        char some_text[MEM_SZ];
};
int main()
{
int running = 1;
void *shared_memory = (void *)0;
struct shared_use_st *shared_stuff;
char buffer[MEM_SZ];
int shmid;
shmid = shmget((key_t)1234, MEM_SZ, 0666 | IPC_CREAT);
if(shmid == -1)
{
printf("shmget failed\n");
exit(EXIT_FAILURE);
}
shared_memory = shmat(shmid, (void *)0, 0);
if(shared_memory == (void *)-1)
{
printf("\nshmat failed\n");
exit(EXIT_FAILURE);
}
printf("Memory attached at %x\n", (int)shared_memory);
shared_stuff = (struct shared_use_st *)shared_memory;
while(running)
{
        while(shared_stuff->written_by_you == 1)
        {
        sleep(1);
        printf("\nwaiting for client...\n");
        }
        printf("Enter some text: ");
        fgets(buffer, MEM_SZ, stdin);
        strcpy(shared_stuff->some_text, buffer);
        shared_stuff->written_by_you = 1;
        if(strncmp(buffer, "end", 3) == 0)
        {
                running = 0;
        }
}
if(shmdt(shared_memory) == -1)
{
printf("shmdt failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
 
$ cat consumer.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define MEM_SZ 4096
struct shared_use_st
{
int written_by_you;
char some_text[MEM_SZ];
};
int main()
{
        int running = 1;
        void *shared_memory=(void *)0;
        struct shared_use_st *shared_stuff;
        int shmid;
        srand((unsigned int)getpid());
        shmid = shmget((key_t)1234, MEM_SZ, 0666 | IPC_CREAT);
        if(shmid ==-1)
        {
        printf("shmget failed\n");
        exit(EXIT_FAILURE);
        }
        shared_memory = shmat(shmid, (void *)0, 0);
        if(shared_memory == (void *)-1)
        {
        printf("\nshmat failed\n");
        exit(EXIT_FAILURE);
        }
printf("Memory attached at %x\n", (int)shared_memory);
shared_stuff=(struct shared_use_st *)shared_memory;
shared_stuff->written_by_you = 0;
while(running)
{
if(shared_stuff->written_by_you)
{
printf("You wrote: %s", shared_stuff->some_text);
sleep( rand()%4);
shared_stuff->written_by_you = 0;
if(strncmp(shared_stuff->some_text, "end", 3)==0)
{
running = 0;
}
}
}
if(shmdt(shared_memory)==-1)
{
        printf("shmdt failed\n");
        exit(EXIT_FAILURE);
}
if(shmctl(shmid, IPC_RMID, 0)==-1)
{
printf("shmctl(IPC_RMID)failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}

Now my question and confusion is.
1) Both producer and consumer create structures and shared memory spaces
2) Both are separate programs and seperate processes.
3) How do they know that a common memory space is shared between them, I mean we don't tell the consumer which part of memory is created by the producer, nor the producer is aware of the consumer's created memory.
4) Then how come the producer produces some string and the consumer gets that string.
Could you please throw some light on it. And when I change the first argument of shmget to

IPC_PRIVATE

, the program doesn't work then.
Could you please help me on this.
Looking forward to your kind replies.
Thanks in advance!

First, I'm going to let you know that there is A LOT wrong with these programs. I'll go into a few things, then answer your questions:

1) First and foremost, an integer (shared_stuff->written_by_you) is not a proper synchronization primitive. Depending on architecture, it is quite possible that a change to this variable by either producer or consumer is never visible to the other. It is also possible that the variable be updated before the text (write re-ordering), which will cause corruption.

There are mutexes and condition variables (created with a process shared attribute) that are proper ways to synchronize shared memory access. There are also semaphores, which is the classic way of doing this. I suggest you look them up!

2) You're polling -- consider the loop in the consumer is eating many CPU cycles because it never sleeps if shared_stuff->written_by_you == 0. This is bad practice. Of course, considering you're not properly synchronizing, this is the least of your worries.

3) You're not creating the shared memory segment of the correct size. You should be using sizeof(struct shared_use_st) rather than MEM_SZ in your shmget call.

4) Consider putting struct shared_use_st in a header file both applications include -- also good practice.

There are probably many other things, but these strike me initially. Now, on to your questions:

1) Both producer and consumer create structures and shared memory spaces

Wrong, read "man shmget". If the key already exists, it'll return its ID. If it doesn't, it'll create it. If you want it to fail if the key already exists, and you use the IPC_EXCL flag, then it'll fail. You can NOT create two shared memory segments on a system with the same key! That's the point of the key.

2) Both are separate programs and seperate processes.

Yep.

3) How do they know that a common memory space is shared between them, I mean we don't tell the consumer which part of memory is created by the producer, nor the producer is aware of the consumer's created memory.

That's what shmat does. The shared memory space is created by the kernel (by shmget) in a place accessible across different process spaces. shmat maps that memory space into your process, hence allowing it to be shared.

Think of it like this: shmget looks up a particular shared memory segment by key, if it doesn't exist (and IPC_CREAT is specified) then it'll create one. If it's IPC_PRIVATE, then it'll just create a new one. When a new shared memory segment is created (or an existing one looked up) it gets a system identifier. When shmget succeeds this system identifier is what it returns. shmat takes as its first argument that system identifier (shmid) and maps that memory segment into your process space (assuming it has all the correct permissions). Thus, every process mapping that shmid into their process space sees the same shared memory.

I believe maybe running the "ipcs -m" command may help you understand. Once your shmget succeeds once, you'll see your key (1234) listed (which in hex is 0x000004D2) along with its shmid. It'll go away when all attached programs complete, as your program asks it to be removed by shmctl. You can also remove it manually via the ipcrm command (but, again, it can only be removed once all programs detach).

I also wonder if you're seeing the same "memory attached at" printfs and confusing what's really going on. Or, even different ones (which is quite possible as well) and confusing it even more so. You'd really have to understand how process memory is mapped in your particular O/S (and I'm not going to explain it to you) to "get it". But, just understand that shmat takes a memory region outside of your process space and maps it into your process space (similar to things like mmap).

4) Then how come the producer produces some string and the consumer gets that string.

See everything above -- also realize that due to some very large problems with your application, it's quite possible it doesn't get it. Not because of the shared memory, but because of your failure to correctly synchronize it (with a semaphore or mutex, etc).

5) And when I change the first argument of shmget to IPC_PRIVATE, the program doesn't work then.

man "shmget". IPC_PRIVATE creates an ID that is not accessible by a key. Therefore, in the case of IPC_PRIVATE you are indeed creating two different shared memory segments. Using IPC_PRIVATE requires passing the ID (returned by shmget) to everyone who is going to attach it (via shmat) so that shmat can attach that ID. This can be done by fork (because it'll have a copy of the ID) or by any other means of passing the value.