Beware that portability of POSIX semaphores in real life is not much better
than mutexes. Most Unix systems stick with System V IPC due to efficiency
(they are direct kernel calls, as opposed to fugly user level libs that
implement POSIX IPC on many systems). On Linux you can’t share POSIX
semaphores between processes either (last time I checked anyway).
Next caveat is the sharing mechanism - that in itself is not very portable.
While mmap() is available almost everywhere its semantics differ. On Linux
you do not allocate memory with mmap(), on QNX or BSD you can with MAP_ANON,
on Solaris you can too but older versions require you to use fd of /dev/zero
while newer allow MAP_ANON. You can use a named filesystem object, but some
systems will want ltrunc() while others will want ftruncate() and you get
into other kinds of trouble too (bugs, especially on Solaris and neccessity
to deal with locking and cleanup of the files - those are PITA in
themselves). Due to this and other reasons most Unixes stick with System V
API (which does not smell like roses either).
Once you decide how you want to share, choice between semaphore and mutex is
fairly simple. Semaphore has two fundamental differences - one that it has a
counter and the other is that it has no ‘owner’ (i.e., it can be ‘posted’ by
anyone, whereas mutex can only be unlocked by the owner). So semaphores are
naturally suited for flow-control type of operation (producer-consumer
scenario) and mutex as its aptly chosen name suggests is best for mutual
exclusion. A semaphore can be emulated with combination of mutex and
condvar.
– igor
“Chris Foran” <cforan@qnx.com> wrote in message
news:db6evf$h73$1@inn.qnx.com…
Hi Alain,
Alain Achkar wrote:
I need some clarification regarding the following paragraphs from the
QNX6 System Architecture book:
"Another useful property of semaphores is that they were defined to
operate between processes. Although our mutexes work between processes,
the POSIX thread standard considers this an optional capability and as
such may not be portable across systems. For synchronization between
threads in a single process, mutexes will be more efficient than
semaphores.
As a useful variation, a named semaphore service is also available. It
uses a resource manager and as such allows semaphores to be used between
processes on different machines connected by a network. "
Q1. How can I make mutexes work between processes?
Put the mutex in shared memory. Map the shared memory into any process
here the mutex should be used.
Attached is a rough example of how to do this.
Q2. How can I make un-named semaphores work between processes? (If
process A calls sem_init(&sem, 1, 3) where the second argument (1)
indicates shared memory, what does process B do to get to the shared
semaphore?
Like mutexes for threads in separate processes, un-named semaphores have
to go in shared memory. Both process A and process B would have to map
in the shared memory, thus each getting a pointer, which they can pass
into the 1st arg of the sem_init() call.
Q3. What is the difference between recursive mutexes and un-named
semaphores (other than efficiency)?
One important difference is that mutexes by default priority inherit,
and semaphores (both named and unnamed) do not. This feature prevents a
priority inversion situation where a low prio thread could lock a mutex
that a high prio thread will want. If there is an intermediate prio
thread that starves the low prio thread, the low prio thread is
prevented from making progress towards unlocking the mutex so that the
high prio thread can get it. The inheritance feature causes the prio of
the low prio thread to be bumped up to equal that of the high prio
thread, thus preempting the intermediate prio thread, and preventing the
problem.
For this reason, mutexes, even across processes are more desirable that
semaphores.
–Chris Foran
/*
- shmemuser.c
-
- This one is meant to be run in tandem with shmemcreator.c.
-
- Run it as: shmemuser shared_memory_object_name
- Example: shmemuser /wally
-
*/
#include <errno.h
#include <fcntl.h
#include <stdio.h
#include <stdlib.h
#include <string.h
#include <unistd.h
#include <sys/mman.h
#include <sys/stat.h
#include <pthread.h
/* shmem.h contains the structure that is overlayed on the shared memory
*/
#include “shmem.h”
/*
char *progname = “shmemuser”;
int main( int argc, char *argv[] )
{
int fd;
shmem_t *ptr;
setvbuf (stdout, NULL, _IOLBF, 0);
if ( argc != 2 ) {
printf( “use: shmemuser shared_memory_object_name\n” );
printf( “Example: shmemuser /wally\n” );
exit( EXIT_FAILURE );
}
/* open the shared memory object */
fd = shm_open( argv[1], O_RDWR, S_IRWXU );
if ( fd == -1 ) {
printf( “%s: error opening the shared memory object ‘%s’: %s\n”,
progname, argv[1], strerror(errno) );
exit( EXIT_FAILURE );
}
/* get a pointer to a piece of the shared memory */
ptr = mmap( 0, sizeof(shmem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd,
0 );
/* don’t need fd anymore */
close( fd );
/* wait on the semaphore */
printf( “%s: Waiting for the mutex to be unlocked. Run ‘pidin’. I
should be mutex blocked.\n”, progname );
pthread_mutex_lock(&ptr->mutex);
printf("%s: mutex was unlocked\n", progname);
printf( “%s: The shared memory contains ‘%s’\n”, progname, ptr->text );
pthread_mutex_unlock(&ptr->mutex);
munmap( ptr, sizeof(shmem_t) );
return 0;
}
#include <pthread.h
#define MAX_TEXT_LEN 100
typedef struct {
pthread_mutex_t mutex;
char text[MAX_TEXT_LEN+1];
} shmem_t;
/*
- shmemcreator.c
-
- This module demonstrates shared memory and using a mutex to sync. the
access
- of multiple processes. It creates some shared memory and puts the data
and
- mutex in it.
- This one is meant to be run in tandem with shmemuser.c.
-
- Run it as: shmemcreator shared_memory_object_name
- Example: shmemcreator /wally
-
*/
#include <errno.h
#include <fcntl.h
#include <stdio.h
#include <stdlib.h
#include <string.h
#include <unistd.h
#include <sys/mman.h
#include <sys/stat.h
#include <pthread.h
/* shmem.h contains the structure that is overlayed on the shared memory
*/
#include “shmem.h”
/*
char *progname = “shmemcreator”;
int main( int argc, char *argv[] )
{
char *text = “Text by shmemcreator.c”;
int fd;
shmem_t ptr;
pthread_mutexattr_t mutex_attr; //mutex attributes
pthread_mutex_t mutex; //will point to the mutex in shmem
setvbuf (stdout, NULL, _IOLBF, 0);
if ( argc != 2 ) {
printf( “use: shmemcreator shared_memory_object_name\n” );
printf( “Example: shmemcreator /wally\n” );
exit( EXIT_FAILURE );
}
/* create the shared memory object */
fd = shm_open( argv[1], O_RDWR | O_CREAT, S_IRWXU );
if ( fd == -1 ) {
printf( “%s: error creating the shared memory object ‘%s’: %s\n”,
progname, argv[1], strerror(errno) );
exit( EXIT_FAILURE );
}
/* set the size of the shared memory object */
ftruncate( fd, sizeof(shmem_t) );
/* get a pointer to a piece of the shared memory */
ptr = mmap( 0, sizeof(shmem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd,
0 );
/* don’t need fd anymore, so close it */
close( fd );
pthread_mutexattr_init(&mutex_attr);
pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
mutex = (pthread_mutex_t*)ptr;
pthread_mutex_init(mutex, &mutex_attr);
pthread_mutex_lock(&ptr->mutex);
strcpy( ptr->text, text ); /* write to the shared memory */
printf( “%s: Shared memory created and mutex initialized\n”
“%s: Wrote text ‘%s’ to shared memory.\n”
“%s: Sleeping for 20 seconds. While this program is
sleeping\n”
“%s: run ‘shmemuser %s’.\n”,
progname, progname, ptr->text, progname, progname, argv[1] );
sleep( 20 );
printf( “%s: Woke up. Now I’ll unlock the mutex\n”, progname );
pthread_mutex_unlock(&ptr->mutex);
/* another delay for people to look at things */
sleep( 10 );
munmap( ptr, sizeof(shmem_t) );
shm_unlink( argv[1] );
return 0;
}