Sun Microsystems Logo
Products and Services
 
Support and Training
 
 

Previous Previous     Contents     Index     Next Next

Synchronization Across Process Boundaries

Each of the synchronization primitives can be set up to be used across process boundaries. This is done quite simply by ensuring that the synchronization variable is located in a shared memory segment and by calling the appropriate init routine with type set to USYNC_PROCESS.

If this has been done, then the operations on the synchronization variables work just as they do when type is USYNC_THREAD.

mutex_init(&m, USYNC_PROCESS, 0);
rwlock_init(&rw, USYNC_PROCESS, 0);
cond_init(&cv, USYNC_PROCESS, 0);
sema_init(&s, count, USYNC_PROCESS, 0);

Using LWPs Between Processes

Using locks and condition variables between processes does not require using the threads library. The recommended approach is to use the threads library interfaces, but when this is not desirable, then the _lwp_mutex_* and _lwp_cond_* interfaces can be used as follows:

  1. Allocate the locks and condition variables as usual in shared memory (either with shmop(2) or mmap(2)).

  2. Then initialize the newly allocated objects appropriately with the USYNC_PROCESS type. Because no interface is available to perform the initialization (_lwp_mutex_init(2) and _lwp_cond_init(2) do not exist), the objects can be initialized using statically allocated and initialized dummy objects.

For example, to initialize lockp:

	lwp_mutex_t *lwp_lockp;
	lwp_mutex_t dummy_shared_mutex = SHAREDMUTEX;
		/* SHAREDMUTEX is defined in /usr/include/synch.h */
	...
	...
	lwp_lockp = alloc_shared_lock();
	*lwp_lockp = dummy_shared_mutex;

Similarly, for condition variables:

	lwp_cond_t *lwp_condp;
	lwp_cond_t dummy_shared_cv = SHAREDCV;
		/* SHAREDCV is defined in /usr/include/synch.h */
	...
	...
	lwp_condp = alloc_shared_cv();
	*lwp_condp = dummy_shared_cv;

Producer/Consumer Problem Example

Example 8-2 shows the producer/consumer problem with the producer and consumer in separate processes. The main routine maps zero-filled memory (that it shares with its child process) into its address space. Note that mutex_init() and cond_init() must be called because the type of the synchronization variables is USYNC_PROCESS.

A child process is created that runs the consumer. The parent runs the producer.

This example also shows the drivers for the producer and consumer. The producer_driver() simply reads characters from stdin and calls producer(). The consumer_driver() gets characters by calling consumer() and writes them to stdout.

The data structure for Example 8-2 is the same as that used for the solution with condition variables (see Nested Locking With a Singly Linked List).


Example 8-2 The Producer/Consumer Problem, Using USYNC_PROCESS

main() {
    int zfd;
    buffer_t *buffer;

    zfd = open("/dev/zero", O_RDWR);
    buffer = (buffer_t *)mmap(NULL, sizeof(buffer_t),
        PROT_READ|PROT_WRITE, MAP_SHARED, zfd, 0);
    buffer->occupied = buffer->nextin = buffer->nextout = 0;

    mutex_init(&buffer->lock, USYNC_PROCESS, 0);
    cond_init(&buffer->less, USYNC_PROCESS, 0);
    cond_init(&buffer->more, USYNC_PROCESS, 0);
    if (fork() == 0)
        consumer_driver(buffer);
    else
        producer_driver(buffer);
}

void producer_driver(buffer_t *b) {
    int item;

    while (1) {
        item = getchar();
        if (item == EOF) {
            producer(b, `\0');
            break;
        } else
            producer(b, (char)item);
    }
}

void consumer_driver(buffer_t *b) {
    char item;

    while (1) {
        if ((item = consumer(b)) == '\0')
            break;
        putchar(item);
    }
}


A child process is created to run the consumer; the parent runs the producer.

Special Issues for fork() and Solaris Threads

Solaris threads and POSIX threads define the behavior of fork() differently. See Process Creation--exec(2) and exit(2) Issues for a thorough discussion of fork() issues.

Solaris libthread supports both fork() and fork1(). The fork() call has "fork-all" semantics--it duplicates everything in the process, including threads and LWPs, creating a true clone of the parent. The fork1() call creates a clone that has only one thread; the process state and address space are duplicated, but only the calling thread is cloned.

POSIX libpthread supports only fork(), which has the same semantics as fork1() in Solaris threads.

Whether fork() has "fork-all" semantics or "fork-one" semantics is dependent on which library is used. Linking with -lthread assigns "fork-all" semantics to fork(), while linking with -lpthread assigns "fork-one" semantics to fork().

See Linking With libthread or libpthread for more details.

Previous Previous     Contents     Index     Next Next