Semaphores in Linux
Pages: 1, 2, 3, 4, 5, 6
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_opis zero: - Then it waits for the semaphore value to drop to zero.
- If
sem_opis positive value - Then it increases the semaphore value by absolute
sem_opvalue. - If
sem_opis negative value - Then it decreases the semaphore value by absolute
sem_opvalue.
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
}