System V Semaphores
|
A System V semaphore is an object that manages one or more shared resources. Unlike most synchronization classes, System V semaphores and semaphore arrays can be used between processes. System V semaphores and semaphore arrays are primarily used for synchronization of resources shared by multiple processes.
os_sysv_semaphore
-An object that manages one or more shared resources. When the semaphore is
constructed, the number of available resources is specified and stored
inside the semaphore as a counter.os_sysv_semaphore_array
-An array of System V semaphores. To create a System V semaphore array, use os_sysv_semaphore_array
with the number of semaphores as the last parameter. To access the n
th semaphore in an array, send semaphore( n
) .This section covers System V semaphores and System V semaphore arrays. Several examples of each are provided.
If two processes try to create and initialize a semaphore or array with the same key, one succeeds and the other attaches to the existing semaphore or array without altering the initial value. Platform<ToolKit> for UNIX ensures that no race conditions occur and that creation and initialization occur atomically.
This section includes several examples of working with System V semaphores and semaphore arrays.
You can obtain or release
semaphores in arbitrary quantities using obtain()
and release() . A process uses obtain()
to obtain a specified number of resources from a semaphore. If the counter is
greater than or equal to the requested number, the counter decrements by the
number requested. The function returns immediately. However, if the counter is
less than the requested number, the process suspends until the specified
number of resources become available.
A process uses release()
to release a specified number of resources to a semaphore. The counter
increments by the requested number and the function returns immediately.
In the following example, both the parent and child processes try to obtain a single resource. Because the parent process obtains the resource first, the resource is blocked from the child until the parent releases the resource.
#include <iostream>
#include <ospace/unix.h>
int
function()
{
os_sysv_semaphore semaphore( os_key( 10 ) );
cout << "Child is trying to obtain resource..." << endl;
semaphore.obtain (1);
cout << "Child obtains resource!" << endl;
return 0;
}
int
main()
{
os_unix_toolkit initialize;
// Create with initial value 1.
os_sysv_semaphore semaphore( os_key( 10 ), O_CREAT, 1, 0600 );
cout << "Initial semaphore value = " << semaphore.value() << endl;
cout << "Parent is trying to obtain resource..." << endl;
semaphore.obtain( 1 );
cout << "Parent obtains resource!" << endl;
os_process child( function ); // Spawn child.
cout << "Sleeping for one second" << endl;
os_this_process::sleep (1);
cout << "Parent is releasing resource" << endl;
semaphore.release( 1 );
os_this_process::wait_for_child( child );
cout << "Removing semaphore" << endl;
semaphore.remove();
return 0;
}
Initial semaphore value = 1
Parent is trying to obtain resource...
Parent obtains resource!
Child is trying to obtain resource...
Sleeping for one second
Parent is releasing resource
Child obtains resource!
Removing semaphore
Several inline member functions
are available that behave in the same manner as obtain(1)
, and release(1) . These function equivalents are
more descriptive than the terms "obtain" and "release" and
better illustrate common semaphore use cases. Using these function
equivalents, a programmer can write code that is easier to read and
understand.
The following functions are
identical in meaning to obtain(1)
.
The following functions are
identical in meaning to release(1) .
In the following example, the parent process creates a semaphore with initial value zero and then attempts to obtain two resources. The parent process is blocked until both children processes release a resource.
#include <iostream>
#include <ospace/unix.h>
int
function()
{
os_sysv_semaphore semaphore( os_key( 10 ) );
os_this_process::sleep( 1 );
cout << "Signaling semaphore..." << endl;
semaphore.signal();
return 0;
}
int
main()
{
os_unix_toolkit initialize;
os_sysv_semaphore semaphore( os_key( 10 ), O_CREAT, 0, 0600 );
os_process child1( function );
os_process child2( function );
cout << "Waiting for both children to signal the semaphore..."
<< endl;
semaphore.wait(); // Wait for one signal.
semaphore.wait(); // Wait for one signal.
cout << "Both children signaled the semaphore..." << endl;
semaphore.remove();
return 0;
}
Waiting for both children to signal the semaphore...
Signaling semaphore...
Signaling semaphore...
Both children signaled the semaphore...
Use the O_UNDO
flag to classify obtain()
or release() as a process that can be undone.
Using this flag helps avoid problems when a process abnormally terminates
after obtaining a resource. If you use an O_UNDO
flag for an obtain() operation, you must use an O_UNDO
flag on the corresponding release() operation.
The O_UNDO
flag automatically reverses any operation that is not completed when a process
terminates. For example, if a resource was obtained, it is released.
Similarly, if a resource was released, it is obtained.
In the following example, two
child processes compete for a single resource. The first child obtains the
resource and then terminates before releasing it. Because the obtain()
process uses the O_UNDO flag, the resource is
released so the second child can obtain it.
#include <iostream>
#include <ospace/unix.h>
int
child1()
{
cout << "child1:" << endl;
os_sysv_semaphore semaphore( os_key( 10 ) );
semaphore.obtain( 1, O_UNDO );
cout << " obtained semaphore, now terminating..." << endl;
os_this_process::terminate(); // Will release resource at this point.
return 0;
}
int
child2()
{
cout << "child2:" << endl;
os_sysv_semaphore semaphore( os_key( 10 ) );
semaphore.obtain( 1, O_UNDO );
cout << " obtained semaphore, now releasing..." << endl;
semaphore.release( 1, O_UNDO );
return 0;
}
int
main()
{
os_unix_toolkit initialize;
os_sysv_semaphore semaphore( os_key( 10 ), O_CREAT, 1, 0600 );
cout << "Begin: semaphore's initial value is ";
cout << semaphore.value() << endl;
os_process child_process1( child1 );
os_this_process::wait_for_child( child_process1 );
os_process child_process2( child2 );
os_this_process::wait_for_child( child_process2 );
cout << "Done: semaphore's final value is ";
cout << semaphore.value() << endl;
semaphore.remove();
return 0;
}
Begin: semaphore's initial value is 1
child1:
obtained semaphore, now terminating...
child2:
obtained semaphore, now releasing...
Done: semaphore's final value is 1
Use the O_NONBLOCK
option to specify an operation as non-blocking. If
an operation results in a block, the operation throws an os_unix_toolkit_error
and returns immediately.
#include <ospace/std/iostream>
#include <ospace/unix.h>
#include <sys/errno.h>
int
child()
{
os_sysv_semaphore semaphore( os_key( 40 ) );
bool waiting = true;
while( waiting )
{
try
{
cout << "Obtaining..." << endl;
semaphore.wait( O_NONBLOCK );
waiting = false;
}
catch ( os_unix_toolkit_error& error )
{
if ( error.native() != EAGAIN )
throw error;
cout << "Failed: sleeping..." << endl;
os_this_process::sleep (1);
}
}
cout << "Succeeded: DONE" << endl;
return 0;
}
int
main()
{
os_unix_toolkit init_unix;
try
{
os_sysv_semaphore semaphore( os_key( 40 ), O_CREAT, 0, 0600 );
os_process child_process( child );
os_this_process::sleep( 3 );
semaphore.signal();
os_this_process::wait_for_child( child_process );
semaphore.remove();
}
catch ( os_unix_toolkit_error& error )
{
cout << "Caught exception:\n\t " << error << endl;
}
return 0;
}
Obtaining...
Failed: sleeping...
Obtaining...
Failed: sleeping...
Obtaining...
Failed: sleeping...
Obtaining...
Failed: sleeping...
Obtaining...
Succeeded: DONE
To specify an operation that
both is non-blocking and can be undone, bitwise OR O_NONBLOCK
and O_UNDO together.
In the following example, the
parent process creates an array of two semaphores.
The parent process then sets each semaphore to an initial value and obtains
the resource of semaphore(1) . The parent process
then spawns two child processes to execute functions child1
, and
child2 .
The first child process attempts
to atomically obtain both semaphores and blocks because semaphore(1)
is held by the parent. Since a child's request is atomically executed, neither
semaphore is altered until both can be obtained.
The second child process obtains
the resource of semaphore(0) and then releases
it. When the parent finally releases the resource of semaphore(1)
, the atomic request of the first child process succeeds.
#include <iostream>
#include <ospace/unix.h>
int
child1()
{
os_sysv_semaphore_array sem_array( os_key( 20 ) );
sem_array.record( true ); // Place semaphore array into record mode.
sem_array.semaphore( 0 ).obtain();
sem_array.semaphore( 1 ).obtain();
cout << "child1: attempt to obtain resources 0 and 1" << endl;
sem_array.execute(); // Execute recorded functions as atomic action.
sem_array.record( false );
cout << "child1: obtained resources 0 and 1" << endl;
sem_array.semaphore( 0 ).release(); // Release resources.
sem_array.semaphore( 1 ).release();
return 0;
}
int
child2()
{
os_sysv_semaphore_array sem_array( os_key( 20 ) );
cout << "child2: attempt to obtain resource 0" << endl;
sem_array.semaphore( 0 ).obtain();
cout << "child2: obtained resource 0" << endl;
sem_array.semaphore( 0 ).release();
cout << "child2: released resource 0" << endl;
return 0;
}
int
main()
{
os_unix_toolkit initialize;
// Create array of two semaphores.
os_sysv_semaphore_array sem_array( os_key( 20 ), O_CREAT, 2, 0600 );
sem_array.semaphore( 0 ).value( 1 ); // Set initial value.
sem_array.semaphore( 1 ).value( 1 ); // Set initial value.
// Prevent child1 from obtaining both.
sem_array.semaphore( 1 ).obtain( 1 );
cout << "parent: obtained resource 1" << endl;
os_process child_process1( child1 );
os_this_process::sleep( 1 );
os_process child_process2( child2 );
cout << "parent: waiting for child2, then releasing resource 1 ...";
cout << endl;
os_this_process::wait_for_child( child_process2 );
sem_array.semaphore( 1 ).release( 1 ); // Allow child1 to obtain both.
os_this_process::wait_for_child( child_process1 );
sem_array.remove(); // Remove semaphore array from system.
return 0;
}
parent: obtained resource 1
child1: attempt to obtain resources 0 and 1
child2: attempt to obtain resource 0
child2: obtained resource 0
child2: released resource 0
parent: waiting for child2, then releasing resource 1 ...
child1: obtained resources 0 and 1
The following example sends
several access messages to an os_semaphore_array
and obtains a variety of information.
#include <iostream>
#include <ospace/security.h>
#include <ospace/unix.h>
int
main()
{
os_security_toolkit init_security;
os_unix_toolkit init_unix;
os_sysv_semaphore_array sem( os_key( 20 ), O_CREAT, 2, 0600 );
cout << sem << endl;
cout << " size: " << sem.size() << endl;
cout << " last_op_time: " << sem.last_op_time() << endl;
cout << " last_change_time: " << sem.last_change_time() << endl;
cout << " owner_user: " << os_user( sem.owner_user() ) << endl;
cout << " owner_group: " << os_group( sem.owner_group() );
cout << endl;
cout << " creator_user: " << os_user( sem.creator_user() );
cout << endl;
cout << " creator_group: " << os_group( sem.creator_group() );
cout << endl;
cout << " mode: " << os_mode( sem.mode() ) << endl;
cout << " key: " << sem.key() << endl;
sem.remove();
return 0;
}
os_sysv_semaphore_array( 917504 )
size: 2
last_op_time: Wed Dec 31 18:00:00 1969
last_change_time: Wed Sep 27 15:49:42 1995
owner_user: os_user( tarrman, 105 )
owner_group: os_group( cavemen, 21 )
creator_user: os_user( tarrman, 105 )
creator_group: os_group( cavemen, 21 )
mode: -rw-------
key: os_key( 20 )
Copyright©1994-2026 Recursion
Software LLC
All Rights Reserved - For use by licensed users only.