Shared memory between two c program

i have to shared a variable between two different c programs with shared memory and i do these:

int main() {
    int a=5,b=7;
    int buffer[1];
    int *point;
    int shmid;
    shmid=shmget(IPC_PRIVATE , sizeof(buffer[1]),0666);
    point=(int *)shmat(shmid,NULL,0);
    point[0]=a;
    point[1]=b;
    printf("FIRST = %d\nSECOND = %d",point[0],point[1]);
    fflush(stdout);
    sleep(300);
    shmdt(point);
    exit(0);
}

the other is :

int main(){
    int buffer[1];
    int *point;
    int shmid;
    shmid=shmget(IPC_PRIVATE,sizeof(buffer[1]),0666);
    point=(int *)shmat(shmid,NULL,0);
    printf("first number =%d\nsecond number =%d\n",point[0],point[1]);
    fflush(stdout);
    shmdt(point);
    exit(0);
}

but if i execute the first and after the second, i don't have 5 on first number and 7 on second but both zero. why?
maybe i have to use a key to identify shared memory and not IPC_PRIVATE? ( i also must use semaphores but it's only begin of this program )

Yes, you either need a key or to pass the segment's ID. IPC_PRIVATE is meant to be used between two programs that can pass the ID, either because one forked the other and the ID is resident, or because they passed it in some other way. So, in IPC_PRIVATE world, you'd shmget in one and then pass the shmid to the other to shmat.

If you use the command "ipcs -m" you'll see all the shared memory segments. The KEY for IPC_PRIVATE (usually) is 0xffffffff, but you'll see a bunch of them. Each time you run this program you'll be creating another one. The ID for each will be different. Ultimately, it's the ID that determines which segment you attach to.

Think of shmget as a way to both create and lookup that ID. If you don't pass the same ID to each shmat call, you won't be looking at the same segment. Make sense?

Also, you probably mean sizeof(buffer) not sizeof(buffer[1]). The first will insure the segment can store the whole array, your way provides the segment only enough space to hold one element. Of course, this will cause an overflow.

edit: P.S. since you're never asking the shared memory segment be removed, it'll linger forever. You'll want to use ipcrm to get rid of it. Of course, you could also shmctl with the IPC_RMID command to ask it be removed. It'll hang around until all applications currently attached to it have detached. The kernel will detach the application when it is killed as well, so while it's good practice to shmdt, if you application dies in the middle the kernel will do it. Of course, on some O/Ses I've seen that fail and segments get "stuck" and the dumb machine needs a reboot to clean them up, lol.

1 Like

You can avoid IPC issues by using mmap instead. It can map memory from a file, allowing you to access the same data into two processes' memory:

#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>

int main(void)
{
        int *mem;
        int fd=open("datafile", O_RDWR);
        if(fd < 0)
        {
                perror("couldn't open");
                return(1);
        }


        ftruncate(fd, getpagesize());  // make file long enough
        mem=mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, 
                MAP_SHARED, fd, 0);

        if(mem == MAP_FAILED)
        {
                 perror("Couldn't mmap");
                 return(1);
        }

        mem[0]=0xdeadbeef; // Should appear in file and other process
        munmap(mem, getpagesize());
        close(fd);
        return(0);
}

thanks a lot for your exhaustive answer, i will try soon

---------- Post updated at 05:06 PM ---------- Previous update was at 04:26 PM ----------

so, for example i have to declare :

key_t shmkey=0x200;

and then use that for shmget in both programs?

You're welcome.

That will work, there is also the ftok function. I've seen hard-coded keys and ftok used, more often the former (don't know why, really). Another method I've seen is a hard-coded base with a xor'd in integer from the environment. I suppose for a toy application, it doesn't much matter. For something more production quality, you should probably at the very least insure that the key is configurable so that it can't conflict with any other potentially running application.

if i use

key_t shmkey=0x200;

it doesn't work, it gives segmentation fault and also if i use ftok function...
maybe i'm wrong to use ftok(), which type of file i use in path argument?

It shouldn't segmentation fault even if you give it a wrong token. It sounds like you forgot to do any error checking.

Please post your program. We can't see your computer from here.

the first:

#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
    int a=5,b=7;
    key_t keyshm; // or key_t keyshm=0x200;
    keyshm=ftok("/tmp",32); //see up
    int buffer[1];
    int *point;
    int shmid;
    shmid=shmget(keyshm , sizeof(buffer),0666);
    point=(int *)shmat(shmid,NULL,0);
    point[0]=a;
    point[1]=b;
    printf("FIRST = %d\nSECOND = %d",point[0],point[1]);
    fflush(stdout);
    sleep(300);
    shmdt(point);
    exit(0);
}

the second:

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

int main(){
    int buffer[1];
    int *point;
    key_t keyshm;
    keyshm=ftok("/tmp",32);
    int shmid;
    shmid=shmget(keyshm,sizeof(buffer),0666);
    point=(int *)shmat(shmid,NULL,0);
    printf("first number =%d\nsecond number =%d\n",point[0],point[1]);
    fflush(stdout);
    shmdt(point);
    exit(0);
}

Modifying your program a bit:

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

void die(const char *msg)
{
        perror(msg);
        exit(1);
}

int main() {
    int a=5,b=7;
    key_t keyshm; // or key_t keyshm=0x200;
    keyshm=ftok("/tmp",32); //see up
    int buffer[1];
    int *point;
    int shmid;
    shmid=shmget(keyshm , sizeof(buffer),0666);
    if(shmid < 0) die("Couldn't shmget");

    point=(int *)shmat(shmid,NULL,0);
    point[0]=a;
    point[1]=b;
    printf("FIRST = %d\nSECOND = %d",point[0],point[1]);
    fflush(stdout);
    sleep(300);
    shmdt(point);
    exit(0);
}
$ ./shm1
Couldn't shmget: No such file or directory
$ man shmget
SHMGET(2)                  Linux Programmer's Manual                 SHMGET(2)

...

ERRORS
       On failure, errno is set to one of the following:

       EACCES The  user  does  not have permission to access the shared memory
              segment, and does not have the CAP_IPC_OWNER capability.

       EEXIST IPC_CREAT | IPC_EXCL was specified and the segment exists.

       EINVAL A new segment was to be created and size < SHMMIN or size > SHM-
              MAX,  or  no new segment was to be created, a segment with given
              key existed, but size is greater than the size of that segment.

       ENFILE The system limit on the total number  of  open  files  has  been
              reached.

       ENOENT No segment exists for the given key, and IPC_CREAT was not spec-
              ified.

       ENOMEM No memory could be allocated for segment overhead.

       ENOSPC All possible shared memory IDs  have  been  taken  (SHMMNI),  or
              allocating  a segment of the requested size would cause the sys-
              tem to exceed the system-wide limit on shared memory (SHMALL).

       EPERM  The SHM_HUGETLB flag was specified, but the caller was not priv-
              ileged (did not have the CAP_IPC_LOCK capability).

...

So you want 0666 | IPC_CREAT

And you should check the return values of everything in case anything you weren't expecting fails.

Did you change the sizeof? You don't even use buffer in the first program, so you can get rid of that. Then when you shmget pass (sizeof(int) * 2) as the size argument.

Also, to the third argument, shmflg, you have to OR in IPC_CREAT. So the call should be:

key_t shmkey=0x200;
int id;
..,
id = shmget(shmkey, sizeof(int) * 2, 0666 | IPC_CREAT);

Without IPC_CREAT, it's likely that shmget is returning -1 which, since you're not doing error checking, is then passed to shmat which is returning NULL. When you attempt to reference through the NULL pointer you are seg faulting.

I suggest reading the man pages for shmget!

edit: I also suggest not using 0666 and using the proper mode flags; but that's your prerogative. For example:

#include <sys/stat.h>
...
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
...
id = shmget(shmkey, sizeof(int) * 2, mode | IPC_CREAT);

edit2: haha, beaten by hours, didn't even notice there was a second page.

thanks now it works very well, i've a very last question...:rolleyes: where i find manual of mode flags?

man 2 chmod

It describes the modes there.

i've another problem, now i want to add two numbers and print result, two number are written in the first and second elements of buffer of shared memory and after call an adder program and he make sum and put it into third element of buffer so the main program prints result.
well if i have 3+5 program give 114678723
why?
this is the code for main:

#include <sys/types.h>
#include <sys/sem.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <stdlib.h>
#define SEMPERM 0600

typedef union _semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
}semun;
int initsem (key_t semkey){
    int status=0, semid;
    semid=semget(semkey, 1 , SEMPERM | IPC_CREAT | IPC_EXCL);
    if (semid==-1){
        if (errno==EEXIST){semid=semget(semkey,1,0);}
    } else {
        semun arg;
        arg.val=0;
        status=semctl(semid,0,SETVAL,arg);
    }
    if ((semid==-1) || (status==-1)){
        perror("initsem fallita");
        return (-1);
    }
    return (semid);
}
int waitSem(int semid){
    struct sembuf wait_buf;
    wait_buf.sem_num=0;
    wait_buf.sem_op=1;
    wait_buf.sem_flg=SEM_UNDO;
    if (semop(semid,&wait_buf, 1)==-1){
        perror("waitSem Fallita");
        exit(-1);
    }
    return 0;
}

int signalSem (int semid){
    struct sembuf signal_buf;
    signal_buf.sem_num=0;
    signal_buf.sem_op=1;
    signal_buf.sem_flg=SEM_UNDO;
    if (semop(semid,&signal_buf,1)==-1){
        perror("signalSem fallita");
        exit(1);
    }
    return 0;
}
int main (){
    key_t chiavemem=0x100,keysem=0x050;
    int sem,a,id,*point;
    pid_t figlio;
    int buffer[2];
    sem=initsem(keysem);
    if(sem<0){
        perror ("creazione semaforo fallita");
        exit (-1);
    }
    id=shmget(chiavemem,sizeof(buffer[2]),0777 |IPC_CREAT);
    if (id<0){
        perror("id main errato");
        exit(-1);
    }
    point=(int *)shmat(id,NULL,0);
    if (point<0){
        perror ("Errato attacco main");
        exit (-1);
    }
    waitSem(sem);
    point[0]=3;
    point[1]=5;
    signalSem(sem);
    figlio=fork();
    if (figlio<0){
        perror("fork fallita");
        exit (-1);
    }
    if (figlio==0){
        execvp("/home/francesco/NetBeansProjects/Esercizio1/addizione",NULL);
        a=point[2];
        fflush(stdout);
        exit(0);
    }
    printf("il risultato � %d",a);
    shmdt(point);
    shmctl(id,IPC_RMID,0);
    exit(0);
}

instead adder is:

#include <sys/types.h>
#include <sys/sem.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#define SEMPERM 0600

typedef union _semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
}semun;
int initsem (key_t semkey){
    int status=0, semid;
    semid=semget(semkey, 1 , SEMPERM | IPC_CREAT | IPC_EXCL);
    if (semid==-1){
        if (errno==EEXIST){semid=semget(semkey,1,0);}
    } else {
        semun arg;
        arg.val=0;
        status=semctl(semid,0,SETVAL,arg);
    }
    if ((semid==-1) || (status==-1)){
        perror("initsem fallita");
        return (-1);
    }
    return (semid);
}
int waitSem(int semid){
    struct sembuf wait_buf;
    wait_buf.sem_num=0;
    wait_buf.sem_op=1;
    wait_buf.sem_flg=SEM_UNDO;
    if (semop(semid,&wait_buf, 1)==-1){
        perror("waitSem Fallita");
        exit(-1);
    }
    return 0;
}

int signalSem (int semid){
    struct sembuf signal_buf;
    signal_buf.sem_num=0;
    signal_buf.sem_op=1;
    signal_buf.sem_flg=SEM_UNDO;
    if (semop(semid,&signal_buf,1)==-1){
        perror("signalSem fallita");
        exit(1);
    }
    return 0;
}
int main(){
    int id,*point,a,b,c;
    key_t keysem=0x050,chiavemem=0x100;
    int sem,buffer[2];
    sem=initsem(keysem);
    if (sem<0){
        perror("errore creazione semaforo");
        exit(-1);
    }
    id=shmget(chiavemem,sizeof(buffer[2]),0777|IPC_CREAT|IPC_EXCL);
    if(id<0){
        perror("errore creazione memoria condivisa");
        exit(-1);
    }
    point=(int *)shmat(id,NULL,0); 
    if (point<0){
        perror("Attacco additore errato");
        exit(-1);
    }
    waitSem(sem);
    point[0]=a;
    point[1]=b;
    c=a+b;
    point[2]=c;
    signalSem(sem);
    shmdt(point);
    exit(0);
}

P.S. Merry Christmas

I didn't even run the first one, at all, and your second program didn't bother waiting for it.

Next time I run it, it complains that the shared memory segment already exists. Why are you using IPC_EXCL all the time?

Only one of your programs should be allowed to create the semaphore, in any case.

I see there is a problem with the memory segment size that you allocate, and then try to use memory beyond that size. The shmget statement creates segment of size sizeof(buffer[2]) - two problems here
a) array buffer is declared buffer[2], so there are two elements in it, buffer[0] and buffer[1],
b) I guess you meant t oallocate segment to accomodate array buffer - just say sizeof(buffer) in your shmget statement

Then again later you are placing result of computation into buffer[2] - same problem as above.

Remember in C array element counts start with 0, so any array A(N) has element A[0], A[1], ... , A[N-1], there is no A[N]

yes but also if i use buffer[3] instead buffer[2], return same error, i have already thought that was an error of memory segment, but if i try all possibilities.
if i use IPC_CREAT i've same error and also if i use nothing i've same error. why?

---------- Post updated at 03:37 AM ---------- Previous update was at 02:21 AM ----------

i've change dimension of buffer...and i do a test for every system call and now it gives error on shmdt system call, it says "Invalid Argument"
why?

if you are trying to access buffer[3] make sure the array is declared with saze of at least 4, that is

int buffer(4);

nothing also with buffer[4] i've same error on output...

no one can help me?

You agreed not to bump posts here when you registered.

I've been busy. I'll take another look at it now that I'm not busy.

Your semaphore obviously didn't work,at all, ever, and I'm not sure how to fix it.

[edit] some updates:

    if (figlio==0){
        execvp("./adder","./adder", NULL);
        a=point[2]; // THIS CODE WON'T EVER BE EXECUTED
        fflush(stdout); // NEITHER WILL THIS
        exit(0); // NEITHER WILL THIS
    }

execvp replaces the current program. It's not like system().

You need to wait() for the child in the parent to finish instead, then get the value from point[] yourself.