SYSTEM CALL: semop(); PROTOTYPE: int semop ( int semid, struct sembuf *sops, unsigned nsops); RETURNS: 0 on success (all operations performed) -1 on error: errno = E2BIG (nsops greater than max number of ops allowed atomically) EACCESS (permission denied) EAGAIN (IPC_NOWAIT asserted, operation could not go through) EFAULT (invalid address pointed to by sops argument) EIDRM (semaphore set was removed) EINTR (Signal received while sleeping) EINVAL (set doesn't exist, or semid is invalid) ENOMEM (SEM_UNDO asserted, not enough memory to create the undo structure necessary) ERANGE (semaphore value out of range) NOTES:
The sops argument points to an array of type sembuf. This structure is declared in linux/sem.h as follows:
/* semop system call takes an array of these */ struct sembuf { ushort sem_num; /* semaphore index in array */ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags */ };
The number of the semaphore you wish to deal with
The operation to perform (positive, negative, or zero)
Operational flags
If sem_op is negative, then its value is subtracted from the semaphore. This correlates with obtaining resources that the semaphore controls or monitors access of. If IPC_NOWAIT is not specified, then the calling process sleeps until the requested amount of resources are available in the semaphore (another process has released some).
If sem_op is positive, then it's value is added to the semaphore. This correlates with returning resources back to the application's semaphore set. Resources should always be returned to a semaphore set when they are no longer needed!
Finally, if sem_op is zero (0), then the calling process will sleep() until the semaphore's value is 0. This correlates to waiting for a semaphore to reach 100% utilization. A good example of this would be a daemon running with superuser permissions that could dynamically adjust the size of the semaphore set if it reaches full utilization.
In order to explain the semop call, let's revisit our print room scenario. Let's assume only one printer, capable of only one job at a time. We create a semaphore set with only one semaphore in it (only one printer), and initialize that one semaphore to a value of one (only one job at a time).
Each time we desire to send a job to this printer, we need to first make sure that the resource is available. We do this by attempting to obtain one unit from the semaphore. Let's load up a sembuf array to perform the operation:
struct sembuf sem_lock = { 0, -1, IPC_NOWAIT };
if((semop(sid, &sem_lock, 1) == -1) perror("semop");
When our print job has completed, we must return the resources back to the semaphore set, so that others may use the printer.
struct sembuf sem_unlock = { 0, 1, IPC_NOWAIT };