Search code examples
clinuxsemaphoremkfifo

Linux semaphores


I'm looking for help with synchronization problem in Linux. I'm newbie, and I think I don't really understand how to use semaphores to synchronize. My task is to sync two processes that access a file - one reads from fifo from another process, writes to this file, then another reads. I know that my code lacks synchronization, but i have no clue how to do this.

Code:

sem_t writer, reader;
    void readFromFifoSendToFile(void) {
         sem_init(&writer, 1, 1);
         FILE *fp;
         char buffer[100];
         FILE *file;
         file = fopen("file", "w+");
         fclose(file);

         while(1) {
                  sem_wait(&writer);
                  fp = fopen("fifo", "r");
                  fscanf(fp, "%s", buffer);
                  fclose(fp);
                  file = fopen("file", "a+");
                  fputs(buffer, file);
                  fclose(file);
                  sem_post(&writer);
         }
    }

    void readFromFileAndPrint(void) {
         sem_init(&reader, 1, 1);
         FILE *fp;
         char buffer[100];
         int counter = 0;

         while(1) {
                  sem_wait(&reader);
                  counter++;
                  if(counter == 1) {
                      sem_wait(&writer);
                  sem_post(&reader);
                  fp = fopen("file", "r");
                  fscanf(fp, "%s", buffer);
                  fclose(fp);
                  printf("%s", buffer);
                  sem_wait(&reader);
                  if(counter == 0) {
                      sem_post(&writer);
                  }
                  sem_post(&reader);
         }
    }

Solution

  • Your main problem appears to be related to the concept of how a semaphore works. Semaphores are best viewed as a signal between a producer and consumer. When the producer have done something they post a signal on the semaphore, and the consumer will wait on the semaphore until the producer post a signal.

    So in your case, there should only be one semaphore between the consumer and the producer -- they should share this semaphore for their synchronization. Also, the semaphore should start at the value zero since nothing have been produced yet. Every time the producer post to the semaphore the value will increase by one, the consumer when it waits on the semaphore will sleep if the value is zero, until such a time when the producer post and the value increases and becomes one. If the producer is much faster than the consumer the value of the semaphore can go up and be more than one which is fine, as long as the consumer is consuming the output in the same size of units as the producer produces them.

    So a working example here, but without any error handling -- adding error handling is beyond the scope of this -- I have used threads but you can do the same with processes as long as you can share the semaphore between them

    #include <semaphore.h>
    #include <pthread.h>
    #include <stdio.h>
    #include <unistd.h>
    
    sem_t thereBeData;
    
    void* readFromFifoSendToFile(void*) {
    
        FILE *fp = stdin;
        char buffer[100];
        FILE *file;
    
        file = fopen("file", "a+");
    
        while(1) {
           fscanf(fp, "%s", buffer);
    
           fprintf(file,"%s\n",buffer);
           fflush(file);
    
           sem_post(&thereBeData); // signal the consumer
        }
    }
    
    void* readFromFileAndPrint(void*) {
        FILE *fp = 0;
        char buffer[100];
        int counter = 0;
    
        while(1) {
           sem_wait(&thereBeData); // Waiting for the producer
    
           if (!fp) fp = fopen("file", "r");
           fscanf(fp, "%s", buffer);
    
           printf("%s\n", buffer);
        }    
    }
    
    
    int main(void)
    {
        pthread_attr_t attr;
        pthread_t thread1;
        pthread_t thread2;
    
        sem_init(&thereBeData, 0,0);
        pthread_attr_init(&attr);    
        pthread_create(&thread1, &attr, readFromFifoSendToFile, (void*)0);
        pthread_create(&thread2, &attr, readFromFileAndPrint, (void*)0);
    
        sleep(10);
    }