Once a semaphore is created, System V semaphores require that those semaphores that were created in a set be initialized. Creation and initialization of semaphore is not an atomic operation, and initialization needs to be done separately by the user. The function that does this is:
int semctl(int semid, int semnum, int cmd, ...);
- semid
- The semaphore identifier.
- semnum
- The n'th semaphore in the set identified by semid.
- cmd
- This defines the type of operation performed on the semaphore. For initialization purposes, the flag used is
SETVAL
. For other values please refer to the man pages.
Depending on what the chosen "cmd" requires, a fourth argument could be also passed--type union semun
--but this is an optional argument. To use this structure, the user must explicitly define it as follows:
union semun {
int val;
struct semid_ds *buf;
ushort *array;
} arg;
The following code snippet sets the first semaphore of the set identified by the semaphore semid
to value 1.
{
semun init;
init.val = 1;
int i = semctl(semid, 1, SETVAL, init);
}
Once a semaphore is created and initialized, the user is ready to
perform operations on this set of semaphores. Again, the interface used
for performing operations, like locking or unlocking of semaphores, is
not straight-forward. It requires a certain amount of instructions to
be written before calling the following function, which will carry out
the desired operation on a semaphore.
int semop(int semid, struct sembuf *sops, size_t nsops);
- semid
- The semaphore identifier.
- sops
- Is a pointer to a user-defined array of semaphore structure.
However, the documentation suggests that this structure shouldn't be
extended. The structure is:
struct sembuf {
ushort sem_num; /* identifies which semaphore in the set */
short sem_op; /* could be positive,negative or zero */
short sem_flg; /*coud be IPC_NOWAIT ,SEM_UNDO*/
}; - nsops
- Determines how many sembuf you are passing. The nsops argument is provided in case the operation needs to be performed on bunch of semaphores at one time.
NOTE: If the SEM_UNDO
flag is specified, the kernel
resets the value of the semaphore by reversing all effects of previous
operations when the program terminates.
The sem_op
member of struct sembuf is an important
variable that locks and unlocks the semaphore. It can take the
following values, which determines the kind of operation to be
performed on the semaphore.
- If
sem_op
is zero: - Then it waits for the semaphore value to drop to zero.
- If
sem_op
is positive value - Then it increases the semaphore value by absolute
sem_op
value. - If
sem_op
is negative value - Then it decreases the semaphore value by absolute
sem_op
value.
You can see from the above sem_op
values that positive
values correspond to releasing a semaphore and negative values
correspond to a locking semaphore. NOTE: The value of the semaphore
never goes below zero.
The most important operation, after creating, initializing, and operating on semaphores, is the cleaning up the semaphores and their memories. This should happen in cleanup handlers or in destructors, while exiting out of code normally, or while exiting the application abnormally. If this is not done, then the application would, at some point, not find enough semaphores to work with because they are defined system-wide in System V-based implementations.
The following snippet cleans up a semaphore set identified by semid
{
int ret = semctl(semid,0,IPC_RMID); //removing the 0th semaphore in the set
}