Semaphores are one of the IPC methods in Unix. In the 1960s, E. W. Dijkstra designed semaphores, which is a programming construct used for IPC. Dijkstra’s proposed model states:
"Suppose there is a stretch of railroad in which there is a single track over which only one train at a time is allowed. A semaphore guards the track. A train has to wait before entering the single track until the semaphore is in a state that allows travel. When the train enters the track, the semaphore changes the state to prevent other trains from entering the track. A train that is leaving this track must change the state of the semaphore to allow another train to enter the track."In computer jargon, "a semaphore is an integer value. A process waits for permission to proceed by waiting for the integer to become 0. If the process proceeds after the signal, it may increment the integer by 1. When the process is completed, it changes the semaphore’s value by subtracting 1 from the integer.
A semaphore provides the test-n-set pattern functionality. This means that you can set the semaphore after you check it or wait until it clears and then set it. In other words, a semaphore is a resource that holds an integer value and permits processes to synchronize by testing and setting the integer value in a single atomic operation. This means that the process that tests the value of a semaphore and sets it to a different value ensures that no other process interferes with the operation. The two types of operations possible on a semaphore are the wait operation and the signal operation.
A set of operations first checks whether the semaphore’s value is equal to a number. If it is, the value of the semaphore is decreased and returned. If the semaphore value is not equal to the number, the operation blocks the calling process until the semaphore’s value reaches the desired value.
An operation of a signal on a semaphore increments the value of the semaphore and activates one or more processes that are waiting on the semaphore. Semaphores allow processes to change or query the status information and control and monitor the availability of system resources, such as shared memory segments.
Semaphores are used to synchronize operations on processes or to synchronize access to data resources that may be accessed by several processes in parallel. In Unix, semaphores are implemented on later versions of Sys V. A semaphore set contains a control structure and an array of individual semaphores. A semaphore set can hold up to 25 elements. In Unix, operations involving semaphores are achieved by these three system calls:
- semget(): Is used to initialize the semaphore set
- semctl(): Is used to change the ownership permission
- semop(): Is used to perform operations on semaphores
The Advantage of Semaphores
Semaphores can be used to solve the problem of a critical section. A process is said to be in a critical section when it is executed independently and no other process interferes with its execution. This condition is mutually exclusive.
Semaphore Initialization
Semaphores are initialized by using the semget() system function. The syntax of this function is:
int semget(key_t key, int nsems, int semflg);
This function returns an integer value. When the functions returns successfully, it returns the value of the semaphore ID, semid.
The first argument, key, is mandatory and specifies an access value associated with the semaphore ID. The second argument, nsems, specifies the numbers of elements stored in a semaphore array. The call to this function fails if the value of nsems is greater than the number of elements in an existing array. If the exact count of the array is not known, specifying 0 in this argument ensures a successful call to this function. The third argument, semflg, indicates the initial access permissions and creation control flags.
Use of the semget() Function
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
key_t key; /* This key is passed to semget() function */
int semflg; /*This argument is passed tosemget() function*/
int nsems; /*This argument is passed to semget() function.*/
int semid; /*The return value from semget() is stored in this variable.*/
key =
nsems =
semflg =
if ((semid = semget(key, nsems, semflg)) == -1)
{
perror("semget: Call to semget failed");
exit(1);
}
else
........
/* Code may continue here */
Semaphores are controlled by the semctl() system function. This function changes the permissions and other features of a semaphore set. The syntax of this function is:
int semctl(int semid, int semnum, int cmd, union semun arg);
This function takes three arguments and returns an integer value. The first argument is the semaphore ID. The second argument is a value that selects a semaphore from an array of semaphores. The third argument may take any of these values of the control flags:
- GETVAL: Returns the value of a single semaphore
- SETVAL: Sets the value of a single semaphore
- GETPID: Returns the PID of the process that performed the last operation on the semaphore
- GETNCNT: Returns the number of processes waiting for the value of a semaphore to increase
- GETALL: Returns the values for all the semaphores in a set
- SETALL: Sets the values for all the semaphores in a set
- IPC_STAT: Returns the status information from the control structure for the semaphore set
- IPC_SET: Sets the effective user and group identifications and permissions
- IPC_RMID: Removes the specified semaphore set
A process must have an appropriate user identification to use an IPC_SET or IPC_RMID command. In addition, the commands that are accessed by the process should have read and write permissions.
The fourth argument, semnum, specifies a union depending on the operation requested. This argument is optional. If it is needed, it should be declared by application programs, as shown:
union semun
{
int val;
struct semid_ds *buf;
ushort *array;
} arg;
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
union semun
{
int val;
struct semid_ds *buf;
ushort *array;
} arg;
int i;
int semnum = ....;
int cmd = GETALL; /*This gets the value */
i = semctl(semid, semnum, cmd, arg);
if (i == -1)
{
perror("Call to semctl() system function is failed!");
exit(1);
}
else
/* Code may continue here */
A Unix semaphore is an array of semaphores opened simultaneously and atomically by an array of operations specified in the semop() system function. The syntax of this function, which is used to obtain or release a semaphore, is:
int semop(int semid, struct sembuf *sops, size_t nsops);
This function takes three arguments and returns an integer value. The first argument, semid, is the semaphore ID returned by the semget() call. In the second argument, sops refers to a pointer to an array of structures. The information about the semaphore operation contained in each structure includes:
- The semaphore number.
- The operation to be performed on the semaphore.
- The control flags, if any.
sembuf refers to a structure which is defined in the system header file, <sys/sem.h>, as shown:
struct sembuf
{
ushort_t sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
The semop() function can either use IPC_NOWAIT or SEM_UNDO flag. The IPC_NOWAIT flag is used to return the function without changing the semaphore value. The SEM_UNDO flag is used to end the individual operations in the array when the process terminates. The argument, sops, is a pointer to an array of semaphore operation structures. Each structure stores the operation to be performed on semaphores.
A process having the read permission can test a semaphore for a zero value. A semaphore can be incremented when it has the write permission. When an operation fails, the value of the semaphore does not change. The process is blocked unless the IPC_NOWAIT flag is set and remains blocked until:
- All the semaphore operations are completed
- The process receives a signal
- The semaphore set is removed
At any point in time, only one process can update a semaphore. Simultaneous requests by different processes are performed randomly. An array of operations is provided by a semop() function. No updates are performed until the successful completion of all the operations on the array.
If a process terminates abnormally and fails to undo an operation or release the semaphore, the semaphore is locked in memory in the same state in which the process has left it. To overcome this, you can use the SEM_UNDO control flag. If the process is terminated, the system applies the operations in the undo structures. This helps an aborted process to leave any semaphore in an inconsistent state.
When a semaphore controls a process sharing a resource, the operation on the semaphore is performed by SEM_UNDO. Another process must be able to recognize this to restore the resource to a consistent state. If the process runs normally, the reversing operation updates the undo structure with a different complementary value. This guarantees that the undo structure gets the zero value before the process is terminated. When the undo structure reaches zero, the structure is removed. The SEM_UNDO flag should be used with caution because the inconsistent use of this flag leads to excessive resource consumption. This is because the allocated undo structures are used before the system is restarted.
Illustration of the semop() Function
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int i;
int nsops; /* This stores the number of operations to be performed */
int semid; /* This stores the semaphore id of the semaphore set */
struct sembuf *sops; /* This pointer is used perform operations on semaphores.*/
if ((semid = semop(semid, sops, nsops)) == -1)
{
perror("Call to semop() system function is failed!");
exit(1);
}
else
(void) fprintf(stderr, "semop() system functionreturns :%d\n", i);
/* Code may go here*/
/* semabinit.c –This program initialize a semaphore
to be used by the programs, sema and semb */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
/* The semaphore key is a long integer. This is an external identification of the semaphore that a program may use to access it.*/
#define KEY (1492)
void main()
{
int id; /* This number is used as an internal identification of the semaphore.*/
/* The semnum union is an argument to the semctl() function. This function performs various tasks for the semaphore depending on which arguments are passed to it.You can initially set the semaphore value to zero.*/
union semun
{
int val;
struct semid_ds *buf;
ushort * array;
} argument;
argument.val = 0;
/* This creates the semaphore with external key, KEY. if it is not there then just give the permission 0666 to it. */
id = semget(KEY, 1, 0666 | IPC_CREAT);
/*Checking the return value.*/
if(id < 0)
{
fprintf(stderr, "Unable to obtain semaphore.\n");
exit(0);
}
/* Stores the semaphore. The second argument to semget() is given as 1.*/
/* Sets the value of the semaphore which contains the number zero, in semaphore array id to the value zero. */
if( semctl(id, 0, SETVAL, argument) < 0)
{
fprintf( stderr, "Unable to set semaphore value.\n");
}
else
{
fprintf(stderr, "Semaphore %d initialized.\n", KEY);
}
}
/* Semaphore example: Program 1 (sema.c) */
/*Two programs, sema and semb are available to you now. Semb can be started at any time. This will be forced to wait until the program sema is executed. Sema and semb needs not to be executed by the same user */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define KEY (1492)
/* This is the external name of the semaphore known to any program that wishes to access it. */
void main()
{
int id; /*This stores the internal identifier of the semaphore. */
struct sembuf operations[1];
/* An "array"of one operation to perform on the semaphore. */
int retval; /* Return value from semop() */
/* Receives the index for the semaphore with external name KEY. */
id = semget(KEY, 1, 0666);
if(id < 0)
/* Semaphore does not exist. */
{
fprintf(stderr, "Program sema unable to find semaphore and hence exiting.\n");
exit(0);
}
/* Begins a semaphore V-operation. */
printf("Program sema starts a V-operation.\n");
/* Sets the sembuf structure. */
/* Finds the semaphore, which one?: */
operations[0].sem_num = 0;
/* Which operation? Add 1 to semaphore value : */
operations[0].sem_op = 1;
/* Sets the flag and waits: */
operations[0].sem_flg = 0;
/* Do the operation now.*/
retval = semop(id, operations, 1);
if(retval == 0)
{
printf("V-operation by program sema is succeeded.\n");
}
else
{
printf("sema: V-operation did not succeed.\n");
perror("You find the reason!");
}
}
/*When the program, sema gets executed twice then the program, semb can also execute twice. */
/* Semaphore example: Program 2(semb.c) */
/* The two programs, sema and semb are available to you now. Semb can be initiated at any time. It will have to wait until sema is executed. These two programs, sema and semb may not necessarily be executed by the same user.*/
/*You can test as: Execute semb & The & is at the end of semb is important and it allows you to execute in the same system, otherwise you would have to move to a different terminal to execute sema. Then execute sema. */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define KEY (1492)
/* This is the external name of the semaphore that any program can use to access it. */
void main()
{
int id; /* This identifies the semaphore internally. */
struct sembuf operations[1];
/* An "array"of one operation to perform on the semaphore. */
int retval; /* This stores the return value from semop() */
/* This receives the index for the semaphore with external name KEY. */
id = semget(KEY, 1, 0666);
if(id < 0)
/* If semaphore is not available. */
{
fprintf(stderr, "Program semb cannot find semaphore and hence exiting.\n");
exit(0);
}
/* This performs a semaphore P-operation. */
printf("Program semb just to begin a P-operation. \n");
printf("Process id is %d\n", getpid());
/* This sets up the sembuf structure. */
/* This tells which semaphore in the semaphore array:*/
operations[0].sem_num = 0;
/* Determination of the semaphore operation. Subtract 1 from semaphore value : */
operations[0].sem_op = -1;
/* Sets the flag and waits */
operations[0].sem_flg = 0;
/* Start the operation */
retval = semop(id, operations, 1);
if(retval == 0)
{
printf("P-operation succeeded by program semb.\n");
printf("Process id is %d\n", getpid());
}
else
{
printf("semb: P-operation failed.\n");
}
}
/* If sema is executed twice then semb can execute twice. */


Currently Active Users
Categories
Latest Blog Entries

Rate this article