tsar2512
tsar2512

Reputation: 2994

How to use mutexes in a shared memory forked process?

I'm sharing a counter variable between a multi-process program, where processes are created using the fork() call. I'm using the following code to create a shared memory where the counter is stored, and can be accessed and incremented by each process.

This is the thread creation mechanism in the main parent thread

void createSHM()
{
int key = SHMKEY;
int *shm;

if ((shmid = shmget(key, SHMSZ, IPC_CREAT | 0666)) < 0) {
    perror("shmget");
    exit(1);
}

if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
    perror("shmat");
    exit(1);
}
*shm=0
}

and this is the function called in each of the forked processes to get the value of the counter, and increment it.

int attachSHMandIncrement()
{
int key = SHMKEY;
int *shm;

if ((shmid = shmget(key, SHMSZ, 0666)) < 0) {
    perror("shmget");
    exit(1);
}

if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
    perror("shmat");
    exit(1);
}
*shm++;
return *shm;
}
  1. I'm not sure if this implementation is race free? Can anyone comment on if it is, and if not, an example of how this can be made race free?

  2. Given the fact that these are forked processes and will inherit the memory at the time of fork. Is there anything I should keep in mind? I've done this before in totally separate processes but here I might have to do it in forked processes.

Upvotes: 2

Views: 2760

Answers (2)

tsar2512
tsar2512

Reputation: 2994

I found a solution online, that works for me. I am posting it here since I wanted an example code as an answer anyways. I hope this helps others.

This is using an mmapped area shared between the processes, and using a mutex to ensure no race condition.

#include "unpipc.h"
#define SEM_NAME "mysem"

int main(int argc, char **argv)
{
    int fd, i, nloop, zero = 0; 7 int *ptr;
    sem_t  *mutex;
    if (argc != 3)
       err_quit("usage: incr2  &lt;#loops&gt;");
     nloop=atoi(argv[2]); 
      /* open file, initialize to 0, map into memory */
      fd = Open(argv[1], O_RDWR | O_CREAT, FILE_MODE);
     write(fd,&amp;zero,sizeof(int)); 
     ptr = Mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
     close(fd);
     /* create, initialize, and unlink semaphore */
     mutex = Sem_open(Px_ipc_name(SEM_NAME), O_CREAT | O_EXCL, FILE_MODE, 1);
     sem_unlink(Px_ipc_name(SEM_NAME));
     setbuf(stdout, NULL);       /* stdout is unbuffered */
     if (Fork() == 0) {          /* child */
     for (i = 0; i &lt; nloop; i++) {
         sem_wait(mutex);
         printf("child:%d\n",(*ptr)++); 
         sem_post(mutex);
     }
     exit(0);
     }
     /*parent*/ 
     for (i = 0; i &lt; nloop; i++) {
        sem_wait(mutex);
        printf("parent:%d\n",(*ptr)++); 
        sem_post(mutex);
     }
      exit(0);
     }

Upvotes: 0

Chris Dodd
Chris Dodd

Reputation: 126203

Since you don't have any mutexes, there's nothing to stop two processes from trying to increment the counter simultaneously, and since increment of an int is generally not atomic, this is an immediate race condition. Over and above the increment race, the routine increments and then reads the value, which is also not atomic, so subject to races.

In order to avoid races, you need some sort of mutex. You could use a semaphore, or you might be able to use a pthread mutex in shared memory (if you have pthreads available). Alternately you may be able use file locks, or you could write your own atomic assembly routines and call them. If you're using C11, you could even use the stuff in stdatomic.h. Lots of possible choices.

edit

You can wrap a mutex around the racing operations to avoid the race condition at the end of your increment function:

    struct sembuf sem_ops = { 0, -1, SEM_UNDO };
    semop(semid, &sem_ops, 1);
    *shm++;
    rv = *shm;
    sem_ops.sem_op = 1;
    semop(semid, &sem_ops, 1);
    return rv;
}

Generally, you'll just attach the shared memory once in each process (and create it once in one process), and then just use it, without repeatedly attaching and detaching it. Similarly, you'll create and initialize your mutexes once, and then use them.

Upvotes: 2

Related Questions