5

I want to use a mutex which will be used to synchronize access to some variables residing in the memory shared b/w two different processes. How can I achieve that. Code sample to perform that will be very appreciated.

MetallicPriest
  • 29,191
  • 52
  • 200
  • 356
  • 1
    Why does your title say "pthreads" but you are asking about "processes"? How are you sharing memory between processes anyway, with memory mapping? – Kerrek SB Jun 25 '11 at 11:52
  • I'm sharing memory by allocating it before forking. So one process is the parent and the other is child. By pthread, I meant using pthread_mutex_lock. – MetallicPriest Jun 25 '11 at 12:08
  • 2
    I think `fork` creates _copies_ of the memory, so it's not exactly "shared". – Kerrek SB Jun 25 '11 at 12:11
  • 2
    Kerrek SB, actually i attach shared memory by using shmget and shmat before forking, so they get mapped to the same address in both processes! – MetallicPriest Jun 25 '11 at 12:16
  • 1
    @KerrekSB Operating systems don't care as much as you probably think about the distinction between threads and processes. In Linux, they are all 'clone'd anyways. – Jeff Hammond Feb 12 '15 at 14:15
  • @MetallicPriest You should use POSIX shared memory (shm_open) instead of Sys5 (shmget). – Jeff Hammond Feb 12 '15 at 14:16

3 Answers3

14

The following example demonstrates the creation, use and destruction of a Pthread interprocess mutex. Generalizing the example for multiple processes is left as an exercise for the reader.

#include <pthread.h>

pthread_mutex_t shm_mutex;

int main(void)
{
    int err;
    pthread_mutexattr_t attr;
    err = pthread_mutexattr_init(&attr); if (err) return err;
    err = pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); if (err) return err;
    err = pthread_mutex_init(&shm_mutex, &attr); if (err) return err;
    err = pthread_mutexattr_destroy(&attr); if (err) return err;
    err = pthread_mutex_lock(&shm_mutex); if (err) return err;
    err = pthread_mutex_unlock(&shm_mutex); if (err) return err;
    err = pthread_mutex_destroy(&shm_mutex); if (err) return err;
    return 0;
}
Jeff Hammond
  • 5,374
  • 3
  • 28
  • 45
  • It would be nice to know why folks downvote this answer. I don't show how to create processes because that is a mostly orthogonal issue. It's also context-dependent. I'm an HPC programmer, so I create processes with MPI. Others might want an example using fork() or exec(). I'm less qualified to write those examples. – Jeff Hammond Dec 31 '19 at 17:21
  • because it is wrong! The mutex must be placed at shared memory(anoymous or file mapped) but not a global var. – Changbin Du Feb 24 '20 at 07:36
  • Do you notice that the code is demonstrating the API usage and very explicitly not showing a complete example? Hence the part that is left as an exercise to the reader? – Jeff Hammond Feb 24 '20 at 11:05
  • This even is not a good demo. Declaring shm_mutex as a simple global var won't work. This can confuse readers. – Changbin Du Feb 25 '20 at 12:39
  • You are correct that my code cannot be copy-pasted into a nontrivial application, but it is correct as written, because a global variable is legal when the code runs as a single process, which is explicitly stated in the answer already. If you have something constructive to say, why not answer the question yourself with something that meets your standards. – Jeff Hammond Feb 25 '20 at 19:06
  • 3
    I'm not denying your answer. I am suggesting you should make it clear how the shared mutex can be created. This is one of the key point of this problem. You can even simply add pesudo code. – Changbin Du Mar 21 '20 at 00:03
  • The question is about an interprocess mutex , the answer should show that rather than claiming "this is a single process mutex, you have to figure out yourself how to do an interprocess one" – M.M Jun 04 '20 at 03:00
  • @M.M `pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED)` is exactly the creation of an *interprocess mutex*. What is not shown is the separate question of creating multiple processes. The OP did not ask how to create processes, so I did not not answer that. – Jeff Hammond Feb 14 '22 at 12:47
  • I deleted this answer for ~1.5 years because of the pointless hate I got from you two. Please keep hating if you want me to delete it again. – Jeff Hammond Feb 14 '22 at 12:48
  • Recommendations on how to improve the answer aren't "pointless hate". You can always mark the answer as "community wiki" if you don't want to get notifications for comments on it – M.M Feb 14 '22 at 13:02
  • Scroll up and hit the `edit` button. Actually make the world a better place instead of continuously whining at me that I interpret the question differently than you do. – Jeff Hammond Feb 14 '22 at 13:25
7

Use a POSIX semaphore initialized to 1 instead. (See below) Use sem_init for unnamed semaphores or sem_open for named ones.

sem_t sem;

/* initialize using sem_init or sem_open */

sem_wait(&sem);
/* critical region */
sem_post(&sem);

Many years after initially posting this answer, it has to be updated.

Mutexes should actually be used instead of semaphores. R and kuga's comments (copied verbatim below) explain why. In particular I find kuga's mention that mutexes can only be posted by their locking thread most compelling.


R

sem_init requires a nonzero pshared argument to be shared, just like a mutex would require the pshared attribute. There's no reason to prefer semaphores over mutexes for this, and in fact mutexes would be better because you could use a robust mutex which allows you to handle the (very real!) case where one process dies while holding the lock.

kuga

Additionally to R..`s post, a mutex can only be posted by the thread that locks it. This is often required and a semaphore does not provide this feature. So this is not the correct answer, Jeff´s answer should be flagged as the correct answer.

cnicutar
  • 178,505
  • 25
  • 365
  • 392
  • @MetallicPriest Enough for what ? Put more effort into your questions – cnicutar Jun 25 '11 at 12:34
  • I mean, would the variable of type sem_t require some special initialization to make it work for inter process communication. – MetallicPriest Jun 25 '11 at 12:38
  • 1
    Did you actually read my answer ? "*Use sem_init for unnamed semaphores or sem_open for named ones*". – cnicutar Jun 25 '11 at 12:39
  • 1
    `sem_init` requires a nonzero pshared argument to be shared, just like a mutex would require the pshared attribute. There's no reason to prefer semaphores over mutexes for this, and in fact mutexes would be better because you could use a robust mutex which allows you to handle the (very real!) case where one process dies while holding the lock. – R.. GitHub STOP HELPING ICE Jun 25 '11 at 13:58
  • @R.. I actually didn't know pthread mutexes can be shared across processes :-) Of course, you're absolutely right. – cnicutar Jun 25 '11 at 17:58
  • 2
    -1 as a justification on why `sem_t` should be preferred over `pthread mutexes` is missing, or, alternatively, a note on the possibility of using `pthread mutexes`. – Vincenzo Pii Apr 03 '13 at 14:53
  • Additionally to R..`s post, a mutex can only be posted by the thread that locks it. This is often required and a semaphore does not provide this feature. So this is not the correct answer, Jeff´s answer should be flagged as the correct answer. – kuga Jan 02 '17 at 12:19
  • @kuga Thanks a lot for that, I didn't know. I edited the answer and included your mention. Can you add a link to some documentation? – cnicutar Jan 03 '17 at 10:16
  • 1
    Actually its a litte bit more complicated ;) see: http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_mutex_lock.html. You should probably activate the PTHREAD_MUTEX_ERRORCHECK setting. – kuga Jan 03 '17 at 15:41
1

As the reply from Changbin Du in the previous answer,

The mutex must be placed at shared memory(anoymous or file mapped) but not a global var.

Here is the demo:

// The mutex must be placed at shared memory(anoymous or file mapped) but not a global var
pthread_mutex_t *shm_lock;

void child_start()
{
   pthread_mutex_lock(shm_lock);

   printf("Child process has started\n");
   sleep(1);
   printf("Child process has finished\n");

   pthread_mutex_unlock(shm_lock);
}

void parent_start()
{
   pthread_mutex_lock(shm_lock);

   printf("Parent process has started\n");
   sleep(1);
   printf("Parent process has finished\n");

   pthread_mutex_unlock(shm_lock);
}

int main(void)
{
   // init shared lock at anoymous shared memory
   shm_lock = (pthread_mutex_t*) mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);

   pthread_mutexattr_t attr;
   pthread_mutexattr_init(&attr);
   pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
   pthread_mutex_init(shm_lock, &attr);
   pthread_mutexattr_destroy(&attr);

   pid_t pid = fork();
   if (pid == 0)
   {
      child_start();
      return 0;
   }

   parent_start();
   wait(NULL);

   pthread_mutex_destroy(shm_lock);
   return 0;
}

Then, the correct log will be printed.

Parent process has started
Parent process has finished
Child process has started
Child process has finished
Ben
  • 11
  • 2