Saturday, November 15, 2014

Unix Prog: IPC Semaphores

1. Semaphore Overview
A semaphore is a counter used to provide access to a shared data object for multiple processes.

To obtain a shared resource, a process needs to:
1) Test the semaphore that controls the resource
2) If the value of the semaphore is positive, the process can use the resource. In this case, the process decrements the semaphore value by 1.
3) If the value of the semaphore is 0, the process goes to sleep until the semaphore value is greater than 0

To implement semaphores correctly, the test of a semaphore's value and the decrementing of this value must be an atomic operation.. So, semaphores are normally implemented inside the kernel

2. XSI Semaphore

XSI Semaphore is more complicated.
1) A semaphore is not simply a single non-negative value. Instead, we have to define a semaphore as a set of one or more semaphore values.
2) The creation of a semaphore(semget) is independent of its initialization(semctl)
3) All forms of XSI IPC remain in existence even when no process is using them, we have to worry about a program that terminates without releasing the semaphores it has been allocated.

The kernel maintains a semid_ds structure for each semaphore set:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/x86_64-linux-gnu/bits/sem.h  
 ......  
 /* Data structure describing a set of semaphores. */  
 struct semid_ds  
 {  
  struct ipc_perm sem_perm;       /* operation permission struct */  
  __time_t sem_otime;          /* last semop() time */  
  __syscall_ulong_t __glibc_reserved1;  
  __time_t sem_ctime;          /* last time changed by semctl() */  
  __syscall_ulong_t __glibc_reserved2;  
  __syscall_ulong_t sem_nsems;     /* number of semaphores in set */  
  __syscall_ulong_t __glibc_reserved3;  
  __syscall_ulong_t __glibc_reserved4;  
 };  
 ......  

For each semaphore in semaphore set:
semval: current semaphore value, always >= 0
sempid: pid for last operation
semncnt: number of processes awaiting semval > curval
semzcnt: number of processes awaiting semval == 0

3. semaphore system calls
1) semget
 ubuntu@ip-172-31-23-227:~$ less /usr/include/x86_64-linux-gnu/sys/sem.h  
 ......  
 /* Get semaphore. */  
 extern int semget (key_t __key, int __nsems, int __semflg) __THROW;  
 ......  

semget can be used to create a new semaphore or obtain the semaphore id of existing one.

When a new semaphore is created, following members of semid_ds structure are initialized:
ipc_perm structure is initialized; set_otime is set to 0, sem_ctime is set to the current time, sem_nsems is set to nsems.

nsems represents the number of semaphores in set. If creating new semaphore set, we must specify nsems. If referring to existing semaphore set, nsems could be 0.

2) semctl
 ubuntu@ip-172-31-23-227:~$ less /usr/include/x86_64-linux-gnu/sys/sem.h  
 ......  
 /* Semaphore control operation. */  
 extern int semctl (int __semid, int __semnum, int __cmd, ...) __THROW;  
 ......  
semctl function is the catch all for various semaphore operations. Fourth argument is optional, depending on the command requested, and if present, is of type semun.

 ubuntu@ip-172-31-23-227:~$ less /usr/include/linux/sem.h  
 ......  
 /* arg for semctl system calls. */  
 union semun {  
     int val;            /* value for SETVAL */  
     struct semid_ds *buf;  /* buffer for IPC_STAT & IPC_SET */  
     unsigned short *array; /* array for GETALL & SETALL */  
     struct seminfo *__buf; /* buffer for IPC_INFO */  
     void *__pad;  
 };  
   
 struct seminfo {  
     int semmap;  
     int semmni;  
     int semmns;  
     int semmnu;  
     int semmsl;  
     int semopm;  
     int semume;  
     int semusz;  
     int semvmx;  
     int semaem;  
 };  
 ......  

All commands supported:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/x86_64-linux-gnu/bits/ipc.h  
 ......  
 /* Control commands for `msgctl', `semctl', and `shmctl'. */  
 #define IPC_RMID    0        /* Remove identifier. */  
 #define IPC_SET     1        /* Set `ipc_perm' options. */  
 #define IPC_STAT    2        /* Get `ipc_perm' options. */  
 ......  
 ubuntu@ip-172-31-23-227:~$ less /usr/include/x86_64-linux-gnu/bits/sem.h  
 ......  
 /* Commands for `semctl'. */  
 #define GETPID     11       /* get sempid */  
 #define GETVAL     12       /* get semval */  
 #define GETALL     13       /* get all semval's */  
 #define GETNCNT     14       /* get semncnt */  
 #define GETZCNT     15       /* get semzcnt */  
 #define SETVAL     16       /* set semval */  
 #define SETALL     17       /* set all semval's */  
 ......  

IPC_STAT: Fetch the semid_ds structure for this set, storing it in the structure pointed to by semun->buf
IPC_SET: Set the sem_perm.uid, sem_perm.gid and sem_perm.mode fields from the structure pointed to by semun->buf in the semid_ds structure associated with this set.
IPC_RMID: Remove the semaphore set from the system. This removal is immediate.
GETVAL: Return the value of semval for the member semnum.
SETVAL: Set the value of semval for the member semnum.
GETPID: Return the value of sempid for the member semnum.
GETNCNT: Return the value of semncnt for the member semnum.
GETZCNT: Return the value of semzcnt for the member semnum
GETALL: Fetch all the semaphore values in the set. These values are stored in the array pointed to by semun-> array
SETALL: Set all the semaphore values in the set to the values pointed to by semun->array

3) semop:
semop atomically performs an array of operations on a semaphore set.
 ubuntu@ip-172-31-23-227:~$ less /usr/include/x86_64-linux-gnu/sys/sem.h  
 ......  
 /* Operate on semaphore. */  
 extern int semop (int __semid, struct sembuf *__sops, size_t __nsops) __THROW;  
 ......  

One semaphore operation can be represented by "struct sembuf"
 ubuntu@ip-172-31-23-227:~$ less /usr/include/x86_64-linux-gnu/sys/sem.h  
 ......  
 /* Structure used for argument to `semop' to describe operations. */  
 struct sembuf  
 {  
  unsigned short int sem_num;  /* semaphore number */  
  short int sem_op;       /* semaphore operation */  
  short int sem_flg;      /* operation flag */  
 };  
 ......  

sem_flg could be:
SEM_UNDO, IPC_NOWAIT

nsops represents the number "struct sembuf"

sem_op can be negative, 0, or positive.
a) If sem_op is positive: The value of sem_op is added to the specified semaphore's value. If undo flag is specified, sem_op is also subtracted from the semaphore's adjustment value for this process.
b) If sem_op is negative: we want to obtain resources that the semaphore controls. If the semaphore's value is greater than or equal to the absolute value of sem_op(the resources are available), the absolute value of sem_op is subtracted from the semaphore's value. If the undo flag is specfied, the absolute value of sem_op is also added to the semaphore's adjustment value for this process.

If the semaphore's value is less than the absolute value of sem_op(resources are not available), the following conditions apply:

If IPC_NOWAIT is specfied, semop returns with an error of EAGAIN

If IPC_NOWAIT is not specified,the semncnt value for this semaphore is incremented(one more process is going to sleep), and the calling process is suspended until:
the semaphore's value becomes greater than or equal to the absolute value of sem_op, or, the semaphore is removed from the system, or, a signal is caught by the process, and the signal handler returns, the value of semncnt for this semaphore is decremented(since the process is waken up), function returns with EINTR.

c) If sem_op is 0, this means that the calling process wants to wait until the semaphore's value becomes 0

If the semaphore's value is currently 0, the function returns immediately

If the semaphore's value is nonzero, the following conditions apply:
If IPC_NOWAIT is specified, return is made with an error of EAGAIN
If IPC_NOWAIT is not specified, the semzcnt value for this semaphore is incremented(one more process waiting for 0 semaphore value is going to sleep), and the calling process is suspended until:

the semaphore's value becomes 0, or the semaphore is removed from the system, or a signal is caught by the process and the signal handler returns.

Note: semop function operates atomically.

4. Semaphore Adjustment on exit:
Whenever we make semaphore option, we can specify SEM_UNDO flag to let semaphore "memorize" the adjustment value. So when the process terminates, the kernel checks whether the process has any adjustment value for the semaphore, if so, applies the adjustment to the corresponding semaphore.

For example, we have one semaphore value 1; then a process get the resource and decrease the semaphore value to 1 (sem_op is -1 in this case) while specifying the SEM_UNDO flag, then kernel memorizes the adjustment value of this process for this semaphore to be "+1". Then when process terminates, the adjustment will be applied to this semaphore making value from 0 to 1 again, allowing other processes to use the resource again.

If user use any SETVAL/SETALL command to manually setup the semaphore value, adjustment of this semaphore for all processes will become 0.

5. Time Comparison between record locking and semaphore

We use semaphore to control whether to allocate and release the resource.
We create the file using first file byte to control  whether to allocate and release the resource(lock the first byte or release the first byte)

Based on testing results:
semaphore is apparently faster than record locking, but if locking a single resource, and don't need all the fancy features of XSI semaphores, record locking is preferred.

No comments:

Post a Comment