Introduction to Mutex vs Semaphore


Mutex vs Semaphore


What are the differences between Mutex vs Semaphore? When to use mutex and when to use semaphore ?

           As per operating system terminology, mutex and semaphore are kernel resources that provide synchronization services (also called as synchronization primitives).

Why do we need such synchronization primitives? Won’t be only one sufficient? To answer these questions, we need to understand few keywords. 
Atomicity and critical section.

The producer-consumer problem:
Consider the standard producer-consumer problem.
Assume, we have a buffer of 4096 byte length. A producer thread collects the data and writes it to the buffer. A consumer thread processes the collected data from the buffer. Objective is, both the threads should not run at the same time.

Using Mutex:
A mutex provides mutual exclusion, either producer or consumer can have the key (mutex) and proceed with their work. As long as the buffer is filled by producer, the consumer needs to wait, and vice versa.
At any point of time, only one thread can work with the entire buffer. The concept can be generalized using semaphore.

Using Semaphore:
        A semaphore is a generalized mutex. In lieu of single buffer, we can split the 4 KB buffer into four 1 KB buffers (identical resources). A semaphore can be associated with these four buffers. The consumer and producer can work on different buffers at the same time.

Misconception:
        There is an ambiguity between binary semaphore and mutex. We might have come across that a mutex is binary semaphore. But they are not! 

        The purpose of mutex and semaphore are different. May be, due to similarity in their implementation a mutex would be referred as binary semaphore.

         Strictly speaking, a mutex is locking mechanism used to synchronize access to a resource. Only one task (can be a thread or process based on OS abstraction) can acquire the mutex. It means there is ownership associated with mutex, and only the owner can release the lock (mutex).

         Semaphore is signaling mechanism (“I am done, you can carry on” kind of signal). For example, if you are listening songs (assume it as one task) on your mobile and at the same time your friend calls you, an interrupt is triggered upon which an interrupt service routine (ISR) signals the call processing task to wakeup.

FAQ's
1. Can a thread acquire more than one lock (Mutex)?
      Yes, it is possible that a thread is in need of more than one resource, hence the locks. If any lock is not available the thread will wait (block) on the lock.

2. Can a mutex be locked more than once?
      A mutex is a lock. Only one state (locked/unlocked) is associated with it. However, a recursive mutex can be locked more than once (POSIX complaint systems), in which a count is associated with it, yet retains only one state (locked/unlocked). The programmer must unlock the mutex as many number times as it was locked.

3. What happens if a non-recursive mutex is locked more than once.
Deadlock !!

      If a thread which had already locked a mutex, tries to lock the mutex again, it will enter into the waiting list of that mutex, which results in deadlock. It is because no other thread can unlock the mutex. An operating system implementer should exercise care in identifying the owner of mutex and return if it is already locked by same thread to prevent deadlocks.

4. Are "binary semaphore" and "mutex" same?
      No. We suggest to treat them separately, as it is explained signalling vs locking mechanisms. But a binary semaphore may experience the same critical issues (e.g. priority inversion) associated with mutex.
A programmer can prefer mutex rather than creating a semaphore with count 1.

5. What is a mutex and critical section?
      Some operating systems use the same word critical section in the API. Usually a mutex is costly operation due to protection protocols associated with it. At last, the objective of mutex is atomic access.
6. What are events?
      The semantics of mutex, semaphore, event, critical section, etc… are same. All are synchronization primitives. Based on their cost in using them they are different. We should consult the OS documentation for exact details.

7. Can we acquire mutex/semaphore in an Interrupt Service Routine?
       An ISR will run asynchronously in the context of current running thread. It is not recommended to query (blocking call) the availability of synchronization primitives in an ISR. The ISR are meant be short, the call to mutex/semaphore may block the current running thread. However, an ISR can signal a semaphore or unlock a mutex.

8. What we mean by “thread blocking on mutex/semaphore” when they are not available?
Every synchronization primitive has a waiting list associated with it. When the resource is not available, the requesting thread will be moved from the running list of processor to the waiting list of the synchronization primitive. When the resource is available, the higher priority thread on the waiting list gets the resource (more precisely, it depends on the scheduling policies).

9. Is it necessary that a thread must block always when resource is not available?
Not necessary. If the design is sure ‘what has to be done when resource is not available‘, the thread can take up that work (a different code branch). To support application requirements the OS provides non-blocking API.
For example POSIX pthread_mutex_trylock() API. When mutex is not available the function returns immediately whereas the API pthread_mutex_lock() blocks the thread till resource is available.


Note: 
The "mutex" is similar to the principles of the binary semaphore with one significant difference
The principle of ownership. Ownership is the simple concept that when a task locks (acquires) a mutex only it can unlock (release) it. If a task tries to unlock a mutex it hasn't locked (thus doesn't own), then an error condition is encountered and, most importantly, the mutex is not unlocked. 
If the mutual exclusion object doesn't have ownership then, irrelevant of what it is called, it is not a mutex.

Comparision chart "Mutex" vs "Semaphore"
BASIS 
SEMAPHORE
MUTEX
FOR COMPARISON
Basic
Semaphore is a signalling mechanism.
Mutex is a locking mechanism.
Existence
Semaphore is an integer variable.
Mutex is an object.
Function
Semaphore allow multiple program threads to access a finite instance of resources.
Mutex allow multiple program thread to access a single resource but not simultaneously.
Ownership
Semaphore value can be changed by any process acquiring or releasing the resource.
Mutex object lock is released only by the process that has acquired the lock on it.
Categorize
Semaphore can be categorized into counting semaphore and binary semaphore.
Mutex is not categorized further.
Operation
Semaphore value is modified using wait() and signal() operation.
Mutex object is locked or unlocked by the process requesting or releasing the resource.
Resources Occupied
If all resources are being used, the process requesting for resource performs wait() operation and block itself till semaphore count become greater than one.
If a mutex object is already locked, the process requesting for resources waits and queued by the system till lock is released.

C program to apply "named" semaphore lock on share resource between independent processes

C program to apply "named" semaphore lock on share resource between independent processes

Steps to test the below code

1. create 3 ".c"  files which has the same code.
2. compile and execute all the 3 binaries at once.

the expected output with timestamps is at the end of this code.

neelkanth_surekha#cat 1.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <semaphore.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <pthread.h>
#include <time.h>
#include <string.h>

/* define macro PRINTF for printf , where in PRINTF takes same arguments as printf and 
 * also prints timestamp  */
#define LOG_INIT()          time_t ltime; /* calendar time */ \
                            char *ptr;\

#define PRINTF(...)      ltime = time(NULL); /* get current cal time */ \
                            ptr = asctime( localtime(&ltime));\
                            ptr[strlen(ptr)-1] = '\0';\
                            printf("%s: ", ptr); \
                            printf(__VA_ARGS__); printf("\n");

#define SEM_NAME "neel_semaphore"
#define SEM_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
#define INITIAL_VALUE  1


void main()
{
    LOG_INIT();
     
    int sem_value = 0;

       /* Here file.txt is the shared resource. apply sem lock till it's closed by a process  */
       /* We initialize the semaphore counter to 1 (INITIAL_VALUE) */
       /* If O_CREAT  is specified in oflag, then the semaphore is created if it does not already exist.
        *      *      * If both O_CREAT and O_EXCL are specified in oflag, then an error is returned if a semaphore
        *           *           * with the given name already exists.
        *                *                */
       sem_t *semaphore = sem_open(SEM_NAME, O_CREAT , SEM_PERMS, INITIAL_VALUE);
       if (semaphore == SEM_FAILED) {
           perror("sem_open(3) error");
           exit(EXIT_FAILURE);
       }

       PRINTF("PID %ld trying to acquire semaphore", (long) getpid());

       if (!sem_getvalue(semaphore, &sem_value)) {
           PRINTF("PID %d semaphore value: %d", getpid(), sem_value);
       }

       /* check whether the semaphore is available of not.  */
       if (sem_value ==  1)
       {
           PRINTF("PID %d: I have got the semaphore %s\n", getpid(), SEM_NAME);
       } else {
           PRINTF("PID %d: Some other process has the semaphore now. SO, i will wait "
                   "till the lock is released.\n", getpid());
       }

       /* If semaphore is not available, then the code is blocked here. */
       if (sem_wait(semaphore) < 0) {
           perror("sem_wait(3) failed on child");
           exit(EXIT_FAILURE);
       }

       PRINTF("############################## CRITICAL SECTION STARTS HERE #################################");
       PRINTF("PID %d acquired semaphore\n", getpid());

       PRINTF("PID %d at this point access the shared resource like file or shared memory \n", getpid());

       /* sleep is added here to just for demo purpose so that we can visibly see the other process waiting for semaphore lock */
       sleep(10);
       PRINTF("############################## CRITICAL SECTION ENDS HERE ##################################\n");

       if (sem_post(semaphore) < 0) {
           perror("sem_post(3) error on child");
       }

       if (sem_close(semaphore) < 0)
           perror("sem_close(3) failed");
       PRINTF("PID %ld release semaphore\n", (long) getpid());


       sem_unlink(SEM_NAME);

       return;
}


Output:

neelkanth_surekha#./1 

Thu Nov 23 01:08:30 2017: PID 4139 trying to acquire semaphore
Thu Nov 23 01:08:30 2017: PID 4139 semaphore value: 1
Thu Nov 23 01:08:30 2017: PID 4139: I have got the semaphore neel_semaphore

Thu Nov 23 01:08:30 2017: ############################## CRITICAL SECTION STARTS HERE #################################
Thu Nov 23 01:08:30 2017: PID 4139 acquired semaphore

Thu Nov 23 01:08:30 2017: PID 4139 at this point access the shared resource like file or shared memory 

Thu Nov 23 01:08:40 2017: ############################## CRITICAL SECTION ENDS HERE ##################################

Thu Nov 23 01:08:40 2017: PID 4139 release semaphore

neelkanth_surekha#./2 

Thu Nov 23 01:08:31 2017: PID 4140 trying to acquire semaphore
Thu Nov 23 01:08:31 2017: PID 4140 semaphore value: 0
Thu Nov 23 01:08:31 2017: PID 4140: Some other process has the semaphore now. SO, i will wait till the lock is released.

Thu Nov 23 01:08:40 2017: ############################## CRITICAL SECTION STARTS HERE #################################
Thu Nov 23 01:08:40 2017: PID 4140 acquired semaphore

Thu Nov 23 01:08:40 2017: PID 4140 at this point access the shared resource like file or shared memory 

Thu Nov 23 01:08:50 2017: ############################## CRITICAL SECTION ENDS HERE ##################################

Thu Nov 23 01:08:50 2017: PID 4140 release semaphore

neelkanth_surekha#./3 

Thu Nov 23 01:08:32 2017: PID 4141 trying to acquire semaphore
Thu Nov 23 01:08:32 2017: PID 4141 semaphore value: 0
Thu Nov 23 01:08:32 2017: PID 4141: Some other process has the semaphore now. SO, i will wait till the lock is released.

Thu Nov 23 01:08:50 2017: ############################## CRITICAL SECTION STARTS HERE #################################
Thu Nov 23 01:08:50 2017: PID 4141 acquired semaphore

Thu Nov 23 01:08:50 2017: PID 4141 at this point access the shared resource like file or shared memory 

Thu Nov 23 01:09:00 2017: ############################## CRITICAL SECTION ENDS HERE ##################################

Thu Nov 23 01:09:00 2017: PID 4141 release semaphore


C program to convert "integer in string" into integer without using atoi

C program to convert "integer in string" into integer without using atoi


#include <stdio.h>

int toString(char []);

int main()
{
    char a[100];
    int n;

    printf("Input a valid string to convert to integer\n");
    scanf("%s", a);

    n = toString(a);

    printf("String  = %s\nInteger = %d\n", a, n);

    return 0;
}

int toString(char a[]) {
    int c, sign, offset, n;

    if (a[0] == '-') {  // Handle negative integers
        sign = -1;
    }

    if (sign == -1) {  // Set starting position to convert
        offset = 1;
    }
    else {
        offset = 0;
    }

    n = 0;

    for (c = offset; a[c] != '\0'; c++) {
        n = n * 10 + a[c] - '0';
    }

    if (sign == -1) {
        n = -n;
    }

    return n;
}


C program to change to default action inside the signal handler

C program to change to default action inside the signal handler


#include<stdio.h>

#include<signal.h>

#include<unistd.h>



void sig_handler(int signo)

{

    if (signo == SIGSEGV) {

        printf("received SIGSEGV\n");

        signal(signo, SIG_DFL);



/*  The raise() function sends a signal to the calling process or thread.

  In a single-threaded program it is equivalent to

   kill(getpid(), sig);

 */

        raise(signo);

    }

}



int main(void)

{    /* register signal handler */

    if (signal(SIGSEGV, sig_handler) == SIG_ERR) {

        printf("\ncan't catch SIGSEGV\n");

    }



    while(1)

        sleep(1);

    return 0;

}


C program to send signal (SIGTERM) from the signal handler(SIGINT) of one process to another process which is registered with SIGTERM

C program to send signal (SIGTERM) from the signal handler(SIGINT) of one process to another process which is registered with SIGTERM


Neelkanth_98$ cat signal1.c
#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void sig_handler(int signo)
{
    int process_id;
    char process_id_from_file[10];
    FILE *pid_fd;
    char command[100];

    sprintf(command,"pidof %s", "test_process");
    pid_fd = popen(command, "r");
    if (pid_fd == NULL)
    {
        return;
    }

    if (fgets(process_id_from_file, sizeof process_id_from_file , pid_fd) == NULL)
    {
        return;
    }
    pclose(pid_fd);

    process_id = atoi(process_id_from_file);

    if (signo == SIGINT) {
        printf("received SIGINT from another process\n");
        kill(process_id, SIGTERM);
        printf("send SIGTERM to another process\n");
        return;
    }
}

int main(void)
{    /* register signal handler */
    if (signal(SIGINT, sig_handler) == SIG_ERR)
        printf("\ncan't catch SIGINT\n");

    while(1)
        sleep(1);
    return 0;
}
Neelkanth_98$ cat signal2.c
#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void sig_handler(int signo)
{
    if (signo == SIGTERM)
        printf("received SIGTERM from test process\n");
}

int main(void)
{    /* register signal handler */
    if (signal(SIGTERM, sig_handler) == SIG_ERR)
        printf("\ncan't catch SIGTERM\n");

    while(1)
        sleep(1);
    return 0;
}


C program to print userid and group id

C  program to print userid and group id 


#include <stdio.h>
#include <sys/types.h>
#include <pwd.h>
#include <unistd.h>
#include <sys/types.h>
#include <grp.h>


int main()
{
    struct passwd *pwd;
    struct group  *grp;
    int uid, gid;


    pwd = getpwnam("nobody");
    if (pwd == NULL) {
        printf("Failed to get uid");
    }
    uid = pwd->pw_uid;

    grp = getgrnam("root");
    if (grp == NULL) {
        printf("Failed to get gid");
    }
    gid = grp->gr_gid;


    printf("\n %d  %d \n", uid, gid);


    return 0;
}
~


C program to implement error handling

C program to implement error handling


#include <stdio.h>
#include <errno.h>

extern int errno ;

int main ()
{
  FILE * pFile;
  pFile = fopen ("unexist.ent","rb");
  if (pFile == NULL)
  {
    perror ("The following error occurred");
    printf( "Value of errno: %d\n", errno );
  }
  else
    fclose (pFile);
  return 0;
}
If file unexist.ent does not exit then it will produce following result:
The following error occurred: No such file or directory
Value of errno: 29