next up previous Back to Operating Systems Home Page
Next: 8 deadly sins Up: 1997 term messages Previous: gcc and ANSI

About initializing semaphores

On Wed, 12 Mar 1997, NAME_REMOVED_AND_FORGOTTEN wrote:

> We're concerned that both processes are going to keep re-initializing the
> semaphore.
> 
> How can we make the first process initialize this semaphore, yet have  the
> second one test its value but NOT reinitialize it? 

This has been explained at length in class already: skipping classes
hoping to make up via email is, in my humble opinion, the safest
way to flunk any course, mine included. Your choice, anyway, as long
as I can spare the time to reply to emails...

The fact that SysV semaphores cannot be atomically created and
initialized is indeed a flaw in their design. However, it's easy to work
around it by taking advantage of the fact that:
        - all IPCs are initialized ot zero when created;
        - semop(2) accepts an array of operations (struct sembuf's), 
          to be performed on a semaphore set;
        - it's ok to specify the same element of the set more than once 
          in the array of operations;
        - the execution of the whole array of operations is atomic:
          either all the operations are executed uninterruptibly, or
          none is executed.
 
The idea is then to create a semaphore set of N+2 ([0]..[N+1]) elements
for each set of N semaphores that you need. The [N] element is used as a
flag that indicates if the set has been already initialized, and the [N+1]
element to put a lock on the whole set at initialization, calling semop(2)
with a sembuf array like

struct sembuf init_lck[2]=
        { { N+1, 0, 0        }, /* wait until [N+1] is 0 */
          { N+1, 1, SEM_UNDO }  /* then increment it, so to lock. */
        };

The [N] element can now be safely examined: 

if it's still zero, then all the [0..N-1] elements can be
   initialized as needed via semctl(..., IPC_SET,...), and then [N]
   itself can be set to 1 (semctl again).
otherwise, the semaphore set has been already initialized by some other 
   process, so leave it alone.

Finally, [N+1] is unlocked  ({N+1, -1, SEM_UNDO} ). This completes the
creation/initialization procedure.

The SEM_UNDO flag is specified in all operations to make the kernel undo
all them when the process dies. Otherwise, were the process to die in the
middle of the initialization, no other process would be allowed to enter
and initialize itself. 

An example of this technique is shown in Steven's "UNIX Network Programming".
The sources for the example are available online. Stephen Benoits's TA
page (linked from the class web page) has a pointer to them - look in
the lib5 subdirectory. WARNING - using Steven's code blindly, without
reading and understandig it, is another foolproof way to make a fool
of oneself.


\ Franco Callari