Why Do We Need Semaphores?
- Purpose: To protect critical (common) sections of code shared among multiple processes.
- When multiple processes try to access a shared resource simultaneously, it leads to conflicts or data corruption.
- Example:
- If 3 users send print jobs to 1 printer at the same time, outputs may overlap.
- Using semaphores, the printer is locked by one process at a time, ensuring orderly execution.
Types of Semaphores
1. Binary Semaphore
- Has only two values: 0and 1 (i.e., locked/unlocked).
- Works like a mutex (mutual exclusion).
- Used when only one process is allowed in the critical section at a time.
2. Counting Semaphore
- Can have values more than 1(resource count).
- Used to manage multiple identical resources.
- Allows multiple processes up to the count of available resources.
Example: Counting Semaphore
- Assume 5 printers are available.
- 3 print jobs arrive → 3 printers used.
- 4 more jobs come:
- 2 printers are still available → 2 jobs assigned.
- Remaining 2 jobs wait until any printer becomes free.
- Counting semaphores help in scheduling jobs based on available resource count.
Key Points
- Semaphore = Lock mechanism for resource control.
- Prevents race conditions in critical sections.
- Helps in process synchronization.
- Used in multithreading and multitasking
To perform synchronization using semaphores, following are the steps −
Step 1 − Create a semaphore or connect to an already existing semaphore using semget().
Step 2 − Perform operations on the semaphore, i.e., allocate, release, or wait for the resource using semop().
Step 3 − Perform control operations on the semaphore like initializing, removing, or getting values using semctl().
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg)
This system call creates or allocates a System V semaphore set. The following arguments need to be passed –
- The first argument, key −
Recognizes the semaphore set. It can be either an arbitrary value or derived from the ftok() library function. - The second argument, nsems −
Specifies the number of semaphores.
- If binary, then it is 1, meaning 1 semaphore set is needed.
- Otherwise, set as per the required countof semaphore sets.
- The third argument, semflg −
Specifies the required semaphore flags, such as:
- IPC_CREAT: Creates the semaphore if it does not exist.
- IPC_EXCL: Used with IPC_CREAT; the call fails if a semaphore already exists.
Also includes permission bits(e.g., 0666).
Note − Refer earlier sections for details on permissions.
➤ On success −
Returns a valid semaphore identifier (used in further semaphore operations).
➤ On failure −
Returns -1.
To identify the error, use the errno variable or the perror() function.
➤ Common errors −
- EACCES− Permission denied.
- EEXIST− Semaphore set already exists and cannot be created.
- ENOENT− Semaphore set does not exist.
- ENOMEM− Not enough memory to create the semaphore set.
- ENOSPC− Maximum limit of semaphore sets has been reached.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
This system call performs the operations on the System V semaphore sets viz., allocating resources, waiting for the resources or freeing the resources. Following arguments need to be passed –
- The first argument, semid −
Indicates the semaphore set identifiercreated by the semget()function. - The second argument, semops −
Is the pointer to an arrayof operations to be performed on the semaphore set.
The structure used is:
struct sembuf
{
unsigned short sem_num;/* Semaphore set num */
short sem_op;/* Semaphore operation */
short sem_flg;/* Operation flags, IPC_NOWAIT, SEM_UNDO */
};
Element, sem_op, in the above structure, indicates the operation that needs to be performed –
➤ If sem_op is negative (−ve) −
Allocate or obtain resources.
Blocks the calling process until enough resources are released by other processes.
➤ If sem_op is zero −
The calling process waits until the semaphore value becomes 0.
➤ If sem_op is positive (+ve) −
Release resources (i.e., increment the semaphore value).
For example –
struct sembuf sem_lock = { 0, -1, SEM_UNDO };
struct sembuf sem_unlock = {0, 1, SEM_UNDO };
- The third argument, nsemops −
Specifies the number of operationsin the semops
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum,int cmd,)
This system call performs control operation for a System V semaphore. The following arguments need to be passed –
- The first argument, semid −
Is the semaphore identifier, which is the return value of the semget()system call. - The second argument, semnum −
Is the number of the semaphore. Semaphores are numbered starting from 0. - The third argument, cmd −
Specifies the commandto perform the required control operation on the semaphore. - The fourth argument, of type union semun−
Depends on the cmdvalue.- For some commands, this argument is not applicable.
Let us check the union semun –
union semun
{
int val;/* val for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT and IPC_SET */
unsigned short *array; /* Buffer for GETALL and SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO and SEM_INFO*/
};
The semid_ds data structure which is defined in sys/sem.h is as follows –
struct semid_ds
{
struct ipc_perm sem_perm; /* Permissions */
time_t sem_otime; /* Last semop time */
time_t sem_ctime; /* Last change time */
unsigned long sem_nsems; /* Number of semaphores in the set */
};
Note − Please refer to the manual pages for other data structures.
union semun arg;
Valid values for cmd are −
- IPC_STAT−
Copies the current values of each member of the struct semid_ds to the structure pointed to by buf.
This command requires read permission for the semaphore. - IPC_SET−
Sets the user ID, group ID of the owner, permissions, etc., as pointed to by the struct semid_ds. - IPC_RMID−
Removes the semaphore set. - IPC_INFO−
Returns information about the semaphore limits and parameters in the struct semid_ds pointed to by __buf. - SEM_INFO−
Returns a seminfo structure containing information about the system resources consumed by the semaphore. - Create two processes: Parent and child.
- Shared Memory: Used to store a counter and flags for read/write status.
- Increment Counter: Both parent and child increment the counter in parallel. The count is passed via a command-line argument or defaults to a value if not specified.
- Issue: Parallel access to the shared memory causes incorrect counter increment, as the final value should be double the counter.
- Solution: Use a semaphore (c) to ensure one process completes before the other starts.
- Shared Memory Read: The value is checked in c after completion.
- Execution: Run the write program in one terminal and the read program in another. The write program waits until the read program finishes before completing.