Semaphores

Key routines:

semget ( key, number , flags) -

  1. number - the number of semaphores in the group. See the dining philosopher for an example of a group of semaphores.
  2. key - is a global host resource ID just like a socket port number. Once one program creates it, it can be accessed by another program running in another address space.
  3. flags - All of these flags get ORed together. Here are some of the possibilities:

semop(ID, pointer, number) -

  1. ID - Semaphore ID
  2. number - number of entries in the structure pointed to
  3. pointer - pointer to a sembuf structure which contains the fields:

semctl (ID, sem_num, OP_CODE, structure pointer) -

This routine handles all of the miscellaneous things that the previous commands can't handle.

For example deleting a semaphore looks like:
semctl(sem_id, 0, IPC_RMID, sem_union) ;

Setting the initial value looks something like:
sem_union.val = 1;
semctl(sem_id, 0,
SETVAL, sem_union) ;

There is also a GETVAL call that looks like
semctl(sem_id, 0, GETVAL, sem_union);

If you have an array of semaphores, then you might use:
unsigned short forks[NUM_PHILOS];
// some code to fill in forks values
semarg.array = forks;
semctl(semid, 0,
SETALL, semarg);

Retrieving current values of an array of semaphores, might look like:
semarg.array = forks;
semctl(semid, 0,
GETALL, semarg);

sem1.c -- from the text

/* After the #includes, the function prototypes and the global variable, we come to the
 main function. There the semaphore is created with a call to semget, which returns the
 semaphore ID. If the program is the first to be called (i.e. it's called with a parameter
 and argc > 1), a call is made to set_semvalue to initialize the semaphore and op_char is
 set to X. */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

union semun {
        int val;                    /* value for SETVAL */
        struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
        unsigned short int *array;  /* array for GETALL, SETALL */
        struct seminfo *__buf;      /* buffer for IPC_INFO */
    };


static int set_semvalue(void);
static void del_semvalue(void);
static int semaphore_p(void);
static int semaphore_v(void);

static int sem_id;


int main(int argc, char *argv[])
{
    int i;
    int pause_time;
    char op_char = 'O';
	 int my_key = getuid();

	 printf("My userid is %d\n", my_key);

    srand((unsigned int)getpid());
    
    sem_id = semget((key_t)my_key, 1, 0666 | IPC_CREAT);

    if (argc > 1) {
        if (!set_semvalue()) {
            fprintf(stderr, "Failed to initialize semaphore\n");
            exit(EXIT_FAILURE);
        }
        op_char = 'X';
        sleep(2);
    }

/* Then we have a loop which enters and leaves the critical section ten times.
 There, we first make a call to semaphore_p which sets the semaphore to wait, as
 this program is about to enter the critical section. */

    for(i = 0; i < 10; i++) {        

        if (!semaphore_p()) exit(EXIT_FAILURE);
        printf("%c", op_char);fflush(stdout);
        pause_time = rand() % 3;
        sleep(pause_time);
        printf("%c", op_char);fflush(stdout);

/* After the critical section, we call semaphore_v, setting the semaphore available,
 before going through the for loop again after a random wait. After the loop, the call
 to del_semvalue is made to clean up the code. */

        if (!semaphore_v()) exit(EXIT_FAILURE);
        
        pause_time = rand() % 2;
        sleep(pause_time);
    }    

    printf("\n%d - finished\n", getpid());

    if (argc > 1) {    
        sleep(10);
        del_semvalue();
    }
        
    exit(EXIT_SUCCESS);
}

/* The function set_semvalue initializes the semaphore using the SETVAL command in a
 semctl call. We need to do this before we can use the semaphore. */

static int set_semvalue(void)
{
    union semun sem_union;

    sem_union.val = 1;
    if (semctl(sem_id, 0, SETVAL, sem_union) == -1) return(0);
    return(1);
}

/* The del_semvalue function has almost the same form, except the call to semctl uses
 the command IPC_RMID to remove the semaphore's ID. */

static void del_semvalue(void)
{
    union semun sem_union;
    
    if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
        fprintf(stderr, "Failed to delete semaphore\n");
}

/* semaphore_p changes the semaphore by -1 (waiting). */

static int semaphore_p(void)
{
    struct sembuf sem_b;
    
    sem_b.sem_num = 0;
    sem_b.sem_op = -1; /* P() */
    sem_b.sem_flg = SEM_UNDO;
    if (semop(sem_id, &sem_b, 1) == -1) {
        fprintf(stderr, "semaphore_p failed\n");
        return(0);
    }
    return(1);
}

/* semaphore_v is similar except for setting the sem_op part of the sembuf structure to 1,
 so that the semaphore becomes available. */

static int semaphore_v(void)
{
    struct sembuf sem_b;
    
    sem_b.sem_num = 0;
    sem_b.sem_op = 1; /* V() */
    sem_b.sem_flg = SEM_UNDO;
    if (semop(sem_id, &sem_b, 1) == -1) {
        fprintf(stderr, "semaphore_v failed\n");
        return(0);
    }
    return(1);
}



startup.c

In this program we initialize the Semaphore with 0, and then spawn a task that wants a resource. The task will suspend, until we increment this semaphore. Actually the task asks for 6 units of resource, so we increment by 6.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <signal.h>

union semun {
        int val;                    /* value for SETVAL */
        struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
        unsigned short int *array;  /* array for GETALL, SETALL */
        struct seminfo *__buf;      /* buffer for IPC_INFO */
    };
int my_semaphore = -1; 
void start_process();


// ********************************************************

 void accessSema(increment){
       struct sembuf sops;

       sops.sem_num = 0;
       sops.sem_op = increment;
       sops.sem_flg = SEM_UNDO;
       
       if (semop(my_semaphore, &sops, 1) < 0)
            perror("my_semaphore");       
           
       return;
   }
 
   
     
// **********************************************

void free_resources()
{
   union semun semarg;
   
   if (my_semaphore >= 0)
        if (semctl(my_semaphore, 0, IPC_RMID, semarg) < 0)
            {
                perror("free_resources: my_semaphore");
            }

}
//**********************************************************
 
  int main(int argc, char * argv[]) {
    union semun semarg;
    int i, pid, status;

    // ------------------------------

// Get semaphore for display critical region

    my_semaphore = semget(IPC_PRIVATE, 1, 0666);
    if (my_semaphore < 0)
    {
        free_resources();
        perror("semget(my_semaphore)");
        exit(-1);
    }
    printf("created my_semaphore %d\n", my_semaphore); 

// Set a single value to zero --- waiting on this will suspend
// a task until this gets incremented

    semarg.val = 0;
    if (semctl(my_semaphore, 0, SETVAL, semarg) < 0)
    {
        perror("semctl_1(my_semaphore)");
        free_resources();
        exit(-1);
    }
    // -------------------------------
 
    pid = fork();
    if (pid < 0)
     {
        perror("fork err");
        free_resources();
        exit(-1);
      }
     if (pid == 0)
      {
        start_process();
        return 0;
      }
      printf("Creating child: %d\n", pid);
        
    // Let the process go for 5 seconds  
    sleep(5);  
    printf("parent done waiting, now set child in action\n");  
     
    accessSema(6); // Note I don't have to just add 1
    // Go collect the child body
      
    pid = wait(&status);
    printf("All done waiting for child\n");

    
    free_resources();


 }//End of main
 
 void start_process()
 {
    printf("Child calling accessSema(-6)\n");
    accessSema(-6); // I can wait for more than 1
    printf("Child returns from accessSema\n");
    sleep(4);
    printf("Child terminating\n");
 }


create.c

Try running this an then run the ipcs program to see what resources you own. Note the error that occurs because of the IPC_EXCL flag.

Try to understand what is going on with each of the semget calls. We will go over it in class.

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
    int sem1, sem2, sem3, sem4;
    key_t  ipc_key;
    int my_uid = getuid();
    
    ipc_key = ftok(".", 'A');
    if ((sem1 = semget(ipc_key, 3, IPC_CREAT | 0666)) == -1)
    {
        perror("semget: IPC_CREAT | 0666");
    }
    printf("sem1 identifier %d\n", sem1);
    
    
    if ((sem2 = semget(ipc_key, 3, IPC_CREAT | 
                    IPC_EXCL|0666)) == -1)
    {
        perror("semget: IPC_CREAT | IPC_EXCL|0666");
    }
    printf("sem2 identifier %d\n", sem2);
    
    
    if ((sem3 = semget(my_uid, 3, IPC_CREAT | 
                    IPC_EXCL|0600)) == -1)
    {
        perror("semget: IPC_CREAT | IPC_EXCL|0666");
    }
    printf("sem3 identifier %d\n", sem3);
    
    
    if ((sem4 = semget(IPC_PRIVATE, 3, 0600)) == -1)
    {
        perror("semget: IPC_PRIVATE, 0666");
    }
    printf("sem4 identifier %d\n", sem4);



}

ipcs and ipcrm

You can leave resources tied up with IPC. They won't go away until the system is rebooted. If you want to see what you have tied up issue the command:
ipcs

If you want to delete your semaphores, use the command:
ipcrm

The script below will remove all of your semaphores if you don't want to issue separate commands for each semaphore.


remove script

#!/bin/csh -f
set l = `ipcs -s | grep "$USER"| cut -c12-19`
foreach s ( $l )
    echo $s
    ipcrm sem $s 
end
if ($#l != 0 ) echo $#l semaphore\(s\) for $user removed

diningPhilo.c

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <signal.h>

union semun {
        int val;                    /* value for SETVAL */
        struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
        unsigned short int *array;  /* array for GETALL, SETALL */
        struct seminfo *__buf;      /* buffer for IPC_INFO */
    };
#define NUM_PHILOS 5
int semid = -1; /* Semaphore IPC ID */
int sem_display = -1; // semaphore for display output

// ********************************************************

// used to guarantee only one dining philosopher doing printf at a time

 void accessDisplay(increment){
       struct sembuf sops;

       sops.sem_num = 0;
       sops.sem_op = increment;
       sops.sem_flg = SEM_UNDO;
       
       if (semop(sem_display, &sops, 1) < 0)
            perror("semop for display");       
           
       return;
   }
 
   // ************************************************
   // print out which forks are on the table

    void update_display(int philo, int eating)
    {
        int i;
        union semun semarg;

        unsigned short forks[NUM_PHILOS];
        printf("update_display: philo(%d)   eating(%d)\n", philo, eating);

       accessDisplay(-1);
       semarg.array = forks;
       if (semctl(semid, 0, GETALL, semarg) < 0)
        {
             perror("semctl(update_display)");
        }

        printf("------------------------------------------------------\n");
        for (i=0; i < NUM_PHILOS; i++)
        {
            if (i < 10)
                printf("%d    ", i);
             else
                printf("%d   ",i);
        }
        printf("\n");
        for (i=0; i < NUM_PHILOS; i++)
        {
            if (forks[i])
                printf("--E  ");
            else
                printf("     ");
        }
        printf("\n");
        accessDisplay(1);
    }

// ********************************************************

// Called to get 2 adjacent forks or to put them down on the table

   void accessForks(int rightFork, int increment){
       struct sembuf sops[2];
              
       int leftFork = rightFork+1;
       if (leftFork >= NUM_PHILOS) leftFork = 0;

       sops[0].sem_num = leftFork;
       sops[0].sem_op = increment;
       sops[0].sem_flg = SEM_UNDO;

       sops[1].sem_num = rightFork;
       sops[1].sem_op = increment;
       sops[1].sem_flg = SEM_UNDO;
       
       if (semop(semid, sops, 2) < 0)
            perror("semop for 2 forks");       
           
       return;
   }
   void getForks(int rightFork){
       accessForks(rightFork, -1);
    }   
   void putForks(int rightFork){
       accessForks(rightFork, 1);
    }
    
// ****************************************************

// Here is the dining philosopher code

  void start_philo( int number) {
       int nSleep;
       srand(number); // seed random number generator
       
        for (; ; ) 
          {
            // Start out Thinking
             nSleep = abs(rand()) %4;
             sleep(nSleep);
               
            // Enter Eating mode
             getForks(number);
             
             update_display(number,1); // we are now eating

             nSleep = abs(rand()) %8;                                  
             sleep(nSleep);             
            
             // we are about to start thinking
             putForks(number);
             update_display(number,0); 

           }// End of for loop for Philosopher
    }
     
// **********************************************

void free_resources()
{
   union semun semarg;
   
   if (semctl(semid, 0, IPC_RMID, semarg) < 0)
    {
        perror("free_resources: semid ");
    }
    if (sem_display >= 0)
        if (semctl(sem_display, 0, IPC_RMID, semarg) < 0)
            {
                perror("free_resources: sem_display");
            }

}
//**********************************************************
 
  int main(int argc, char * argv[]) {
    union semun semarg;
    unsigned short forks[NUM_PHILOS];
    int children[NUM_PHILOS];
    int i, pid, status;
  
    for (i=0; i < NUM_PHILOS; i++)
    {
        forks[i] = 1;
        children[i] = 0;
    } 


 // ------------------------------- 
 // Get semaphore for 5 forks

    semid = semget(IPC_PRIVATE, 5, 0666);
    if (semid < 0)
    {
        perror("semget(semid)");
        exit(-1);
    } 
    printf("created semid %d\n", semid);

    semarg.array = forks;
    if (semctl(semid, 0, SETALL, semarg) < 0)
    {
        perror("semctl(semid)");
        free_resources();
        exit(-1);
    }
    printf("Initialized fork semaphore\n");
    // ------------------------------

// Get semaphore for display critical region

    sem_display = semget(IPC_PRIVATE, 1, 0666);
    if (sem_display < 0)
    {
        free_resources();
        perror("semget(sem_display)");
        exit(-1);
    }
    printf("created sem_display %d\n", sem_display); 

    semarg.val = 1;
    if (semctl(sem_display, 0, SETALL, &semarg) < 0)
    {
        perror("semctl(sem_display)");
        free_resources();
        exit(-1);
    }
    // -------------------------------
 
    // Finally start up our philosophers
   
    for (i = 0; i < NUM_PHILOS; i++)
    {
            pid = fork();
            if (pid < 0)
                {
                    perror("fork err");
                    free_resources();
                    exit(-1);
                }
            if (pid == 0)
            {
                start_philo(i);
                return 0;
            }
            children[i] = pid;
            printf("Creating child: %d\n", pid);
    }
    
    // Let the philosophers go for 20 seconds  
    sleep(20);       
       
    printf("After 20 Seconds, we Terminate\n");
    for (i=0; i < NUM_PHILOS; i++)
          kill(children[i], SIGTERM);
    
    // Go collect the Philosopher Bodies
      
    for ( ;;)
    {
        pid = 0;
       // For each child still around, execute a call to wait
        for (i = 0; i < NUM_PHILOS; i++)
        {
            if (children[i])
            {
                pid = wait(&status);
                break;
            }
        } 
        // if we didn't find any child, then exit this loop
        if (pid == 0) break;
        printf("freeing up child %d\n", pid);
        for (i = 0; i < NUM_PHILOS; i++)
        {
            if (children[i] == pid)
                children[i] = 0;
        }    
    }
    printf("All done waiting for children\n");

    // Find out if we have any dishonest philosophers
    // Make sure we get all of our forks back  
    // SEM_UNDO should make this happen

    semarg.array = forks;
    for (i=0; i < NUM_PHILOS; i++) 
        forks[i] = 0; // make sure GETALL is really filling fork

    if (semctl(semid, 0, GETALL, semarg) < 0)
        {
             perror("semctl(main)");
        }
    for (i=0; i < NUM_PHILOS; i++)
    {
        if (forks[i] != 1)
            printf("Fork %d was not returned %d\n", i, forks[i]);
    }

    free_resources();


    }//End of main



workers.c

Interesting example, that we won't look at too carefully --- somewhat involved.
/* workers.c -- Workers/Tools Demo */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>

 union semun {
        int val;                    /* value for SETVAL */
        struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
        unsigned short int *array;  /* array for GETALL, SETALL */
        struct seminfo *__buf;      /* buffer for IPC_INFO */
    };

#define N_SEC   3       /* Run simulation for n seconds */

static int semid = -1;  /* Semaphore IPC ID */

/*
 * Worker List :
 */
static struct {
    char    *name;      /* Worker name */
    pid_t   PID;        /* Process ID */
} workers[] = {
    { "Paul", 0 },
    { "Alfred", 0 },
    { "Robert", 0 },
    { "Adam", 0 }
};

/*
 * Tool names :
 */
static char *tools[] = {
    "Hammer",
    "Screwdriver",
    "Chisel"
};

/*
 * In this case we just want to notify the control
 * semaphore (#3). The control semaphore prevents
 * other processes from reporting something to
 * stdout until the present process is finished its
 * reporting to stdout. This keeps the report serialized.
 */
static void
notifyControl(void) {
    int z;
    static struct sembuf sops = { 3, 1, 0 };

    z = semop(semid,&sops,1);
    if ( z == -1 ) {    
        perror("semop(notify ctl)");
        exit(13);
    }
}

/*
 * This function randomly selects 1 to 3 tools that
 * will be required. When hammers or chisels are
 * selected, only require one tool. For screwdrivers
 * allow one or two screwdrivers to be required.
 */
static struct sembuf *
identifyToolsNeeded(int *n) {
    int x, y, sem;
    static struct sembuf sops[3];

    /*
     * Determine the number of tools required:
     */
    *n = rand() % 3 + 1;    /* 1 to 3 */

    /*
     * Now uniquely define the tools needed:
     */
    for ( x=0; x<*n; ++x ) {
        do  {
            sem = rand() % 3;
            for ( y=0; y<x; ++y )
                if ( sops[y].sem_num == sem ) {
                    /* Already used */
                    sem = -1;
                    break;
                }
    } while ( sem == -1 );
        sops[x].sem_num = sem;  /* Tool required */
        if ( sem == 1 ) /* Allow up to 2 screwdrivers */
            sops[x].sem_op = rand() % 2 + 1;
        else            /* All other tools, only one each */
            sops[x].sem_op = -1;
        sops[x].sem_flg = 0;    /* No flags */
    }

    /*
     * The control semaphore :
     */
    sops[*n].sem_num = 3;   /* Sem #3 controls stdout */
    sops[*n].sem_op = -1;   /* Wait operation */
    sops[*n].sem_flg = 0;   /* No flags */

    return sops;
}

/*
 * This function is used to report our tools & state:
 */
void
reportTools(int n,struct sembuf *sops,char *name,char flg) {
    int x;
    int nt;             /* Number of tools */
    char *oper;         /* Operation */
    char buf[1024];     /* Formatting buffer */

    if ( flg == 'W' )
        oper = "waiting for tools";
    else if ( flg == 'X' )
        oper = "using tools";
    else
        oper = "returning tools";
    
    sprintf(buf,"Worker %s is %s\n",
        name,oper);

    /*
     * Only report the tools for the "using"
     * status report:
     */
    if ( flg == 'X' ) {
        for ( x=0; x<n; ++x ) {
            nt = abs(sops[x].sem_op);
            sprintf(buf+strlen(buf),
                "  %d %s%s\n",
                nt,
                tools[sops[x].sem_num],
                nt == 1 ? "" : "s");
        }
    }

    /*
     * Write all of our text lines atomically to
     * standard output :
     */
    write(1,buf,strlen(buf));
}

/*
 * This function is run by the child process to
 * simulate one worker needing and returning tools:
 */
static void
toolTime(int workerx,time_t t0) {
    char *name = workers[workerx].name;
    pid_t pid = getpid();   /* Get our process ID */
    int x;                  /* Work int */
    time_t tn;              /* Current time */
    int n_tools;            /* Number of tools required */
    struct sembuf *sops = 0;/* Tool list */

    /*
     * Start random number generator :
     */
    srand((unsigned)pid);

    /*
     * Loop for N_SEC seconds :
     */
    for (;;) {
        /*
         * Check for quitting time :
         */
        time(&tn);
        if ( tn - t0 >= N_SEC )
            return;         /* Quitting time! */

        /*
         * Identify the tools that we need:
         */
        sops = identifyToolsNeeded(&n_tools);
        reportTools(n_tools,sops,name,'W');

        /*
         * Wait for tools to become available:
         * Note: n_tools+1 includes ctl semaphore.
         */
        semop(semid,sops,n_tools+1);
        reportTools(n_tools,sops,name,'X');
        notifyControl();    /* Done with stdout */

        /*
         * Pretend to work :
         */
        sleep(1);

        /*
         * Turn wait sem_ops into notifies for
         * the tools only (we don't use the control
         * semaphore here):
         */
        for ( x=0; x<n_tools; ++x) 
            sops[x].sem_op = -sops[x].sem_op;

        /*
         * Return the tools to the toolbox :
         * [Notify operation]
         */
        reportTools(n_tools,sops,name,'R');
        semop(semid,sops,n_tools);
    }
}

/*
 * Main program :
 */
int
main(int argc,char **argv) {
    int z;                  /* Return code */
    int x;                  /* Work index */
    time_t t0;              /* Start time */
    union semun semarg;     /* semctl() arg */
    /* Initial counts of tools in toolbox: */
    static ushort icounts[] = {
        3,  /* Hammers */
        2,  /* Screwdrivers */
        1,  /* Chisel */
        1   /* Control semaphore */
    };
    ushort tcounts[4];      /* Final tool counts */
    int status;             /* Termination status */
    pid_t chPID;            /* Child process ID */
    
    /*
     * Get a private set of semaphores :
     */
    semid = semget(IPC_PRIVATE,4,0600);
    if ( semid == -1 ) {
        perror("semget()");
        exit(1);
    }

    /*
     * Initialize the semaphore counts :
     */
    semarg.array = icounts;
    z = semctl(semid,0,SETALL,semarg);
    if ( z == -1 ) {
        perror("semctl(SETALL)");
        exit(1);
    }

    /*
     * Record our start time :
     */
    time(&t0);

    /*
     * Now create four worker processes :
     */
    for ( x=0; x<4; ++x ) {
        fflush(stdout);
        fflush(stderr);

        if ( !(chPID = fork()) ) {
            /* Child process: */
            toolTime(x,t0);
            return 0;

        } else if ( chPID == (pid_t)(-1) ) {
            fprintf(stderr,"%s: fork(x=%d)\n",
                strerror(errno),x);
            return 13;
        }
        workers[x].PID = chPID;
    }
    
    /*
     * Now wait for all processes to end :
     */
    do  {
        chPID = wait(&status);
        for ( x=0; x<4; ++x )
            if ( workers[x].PID == chPID ) {
                workers[x].PID = 0;
                break;
            }
        /*
         * See if they have all terminated :
         */
        for ( x=0; x<4; ++x )
            if ( workers[x].PID != 0 )
                break;
    } while ( x < 4 );

    printf("All workers have quit.\n");

    /*
     * Obtain all semaphore counts :
     */
    semarg.array = tcounts;
    z = semctl(semid,0,GETALL,semarg);
    if ( z == -1 ) {
        perror("semctl(GETALL)");
        exit(1);
    }

    /*
     * Check our tool counts :
     */
    for ( x=0; x<3; ++x )
        if ( tcounts[x] != icounts[x] )
            printf("Now have %d %ss. Had initially %d.\n",
                tcounts[x], tools[x], icounts[x]);
        else
            printf("All %d %ss are accounted for.\n",
                tcounts[x], tools[x]);

    puts("Simulation Complete.");

    /*
     * Remove the semaphore set:
     */
    z = semctl(semid,0,IPC_RMID,semarg);
    if ( z == -1 ) {
        perror("semctl(IPC_RMID)");
        exit(1);
    }

    return 0;
}


Exercise:

Create a set of programs: such that you have a datafile and upto N users can simultaneously read your data file. However, you have another program that updates the file, and only one user can update the file at a time. In fact when someone is updating the file, no one else can be reading the file. Suggested solution:

Define a semaphore associated with a path (see the ftok statement above)

reader.c - attempts to access the semaphore. If it doesn't already exist, then it is created. If it is created, then it is initialized to N. The reader decrements the semaphore by 1 before reading the data.

writer.c - does the same thing as the reading in terms of creating the semaphore and initializing it. However, the writer decrements the semaphore by N before updating the file. Consider the difference of decrementing the semaphore by 1 N times versus decrementing the semaphore by N one time.

For both the reader.c and the writer.c programs, you leave the semaphore around (don't delete at the end of your program).

When you are finally done running your system, you can write a program that deletes the semaphore.