Home > Software design >  Semaphore in C - How can I make it alternate?
Semaphore in C - How can I make it alternate?

Time:01-28

//I think the final code now in this post, should have fixed all the issues I've had. Thank you everyone!

I have written code in C that should lock and unlock shared memory via semaphores and write one Byte into. I would want the written data to alternate between "message" and "read".

Maybe I am misunderstanding semaphores and how their lock functionality works?

I added the while(1) to only to have to loop in one process with the other contiously running, only doing something when the if-condition applies.

I seem to end up in nirvana however, as the program does not complete.

I'd appreciate any help!

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

void latenz_Ausgabe(time_t tv_secStartLatenz, time_t tv_secEndLatenz, long tv_nsecStartLatenz, long tv_nsecEndLatenz)
{
  struct timespec startLatenz = {tv_secStartLatenz, tv_nsecStartLatenz};
  struct timespec endLatenz = {tv_secEndLatenz, tv_nsecEndLatenz};
    double nanoSekunden = difftime(endLatenz.tv_nsec, startLatenz.tv_nsec);
    double sekunden = difftime(endLatenz.tv_sec, startLatenz.tv_sec);
    double differenzZeit = (sekunden   nanoSekunden / ((double) 1e9)) / 1000.0; //Zeit berechnen in Sekunden, sowie auf eine Messung runterrechnen
    double latenz = (differenzZeit * ((double) 1e3)) / 2; //1e3 für Millisekunden, / 2 Da hin und zurück gemessen
    printf("Durchschnittliche Latenz: %.6lf milliseconds\n", latenz);
}
 
int main() {

    struct timespec startLatenz, endLatenz;

    int sem_id;
    int sem_idtwo;
    int shm_id;
    int value;

    key_t key = ftok("shared_memory", 1); // Create key shared memory
    key_t keytwo = ftok("semaphore", 1); // Create key semaphore
    key_t keythree = ftok("semaphoretwo", 1); // Create key semaphore


    if((shm_id = shmget(key, 16777216, IPC_CREAT | 0666)) == -1){ // -1 indicates error //create shared memory
     perror("semctl");
     exit(1);
    } else {
    printf("Shared memory created with id: %d \n", shm_id);
    }
 
    if((sem_id = semget(keytwo, 1, IPC_CREAT | 0666)) == -1){ // -1 indicates error // create semaphore
    perror("semctl");
    exit(1);
    } else {
    printf("Semaphore created with id: %d \n", shm_id);
    }
    
    if((sem_idtwo = semget(keythree, 1, IPC_CREAT | 0666)) == -1){ // -1 indicates error // create semaphore
    perror("semctl");
    exit(1);
    } else {
    printf("Semaphore created with id: %d \n", shm_id);
    }

    struct sembuf sem_lock = {0, -1, 0}; // Struct for semop to lock
    struct sembuf sem_unlock = {0, 1, 0}; // Struct for semop to unlock
 
    char *data = (char *)shmat(shm_id, NULL, 0); // Connect Shared-Memorys to Process
 
    pid_t pid;
 
    if((semctl(sem_id, 0, SETVAL, 1)) == -1){ // -1 indicates error //initialize the semaphore with value 1
     perror("semctl");
     exit(1);
    } else {
    printf("Semaphore initialized \n");
    }
    
    if((semctl(sem_idtwo, 0, SETVAL, 1)) == -1){ // -1 indicates error //initialize the semaphore with value 1
     perror("semctl");
     exit(1);
    } else {
    printf("Semaphore initialized \n");
    }
 
    pid = fork();
 
    if(pid < 0){
        printf("Error");
        return 1;
    }
 
    //malloc allocates exactly one Byte 
    char *message = (char *) malloc(1);
    char *read = (char *) malloc(1);
    message[0] = 'a';
    read[0] = 'b';

    clock_gettime(CLOCK_MONOTONIC, &startLatenz);
      
        if (pid == 0) {
          // Process 2:
                while(1){
                semop(sem_idtwo, &sem_lock, 1);
                if (data[0] == message[0]){ 
                  memcpy(data, read, 1);
                }
                semop(sem_idtwo, &sem_unlock, 1);
                }
                           
            }
        else {
        // Process 1: 
                for(int i = 0; i < 1000; i  ){  
                    //1
                    if (i > 0){ //because data[0] is never equal to read[0] during first loop
                    semop(sem_id, &sem_lock, 1);
                    //char dummy = data[0];        // Could save current value before overwrite
                        if (data[0] == read[0]){
                          memcpy(data, message, 1);
                        }
                    semop(sem_id, &sem_unlock, 1);
    
                    //printf("Values: %c %c \n", dummy, data[0]); //Could print
                    }
                    else{ //because data[0] is never equal to read[0] during first loop
                    semop(sem_id, &sem_lock, 1);
                    //char dummy = data[0];        // Could save current value before overwrite
                    memcpy(data, message, 1);
                    semop(sem_id, &sem_unlock, 1);
                    }
                }
            
        }
        
        
        clock_gettime(CLOCK_MONOTONIC, &endLatenz);
        latenz_Ausgabe(startLatenz.tv_sec, endLatenz.tv_sec, startLatenz.tv_nsec, endLatenz.tv_nsec);
     
    shmdt(data); // Remove shared-memory from process
    shmctl(shm_id, IPC_RMID, NULL); // Delete shared memory
    semctl(sem_id, 0, IPC_RMID); // Delete semaphore
 
    return 0;
}

I tried doing it with two locks, I tried with three operations, I tried different loops (inside/outside of the child/parent process)

CodePudding user response:

As far as I understand, you want to see an output that sometimes prints a and sometimes prints b. The printing is done here:

            semop(sem_id, &sem_lock, 1);
            memcpy(data, message, 1);
            semop(sem_id, &sem_unlock, 1);                
        
            printf("%c \n", data[0]);  <----------

So you have just copied a into data and then you do the printing. So it's not extremely surprising that your print out will mostly show a

I changed the code a little to use while(1) for both loops. When running the program and looking at the terminal, it seemed that a was printed all the time.

Then I send the output to grep and grep'ed for b, i.e "./a.out | grep b" and now I do see b being printed.

My conclusion: Since you do the print just after setting data to a, you will print an a in most cases. Further, if you only look at the terminal, you most likely won't be able to notice the few prints of b.

Instead try:

            semop(sem_id, &sem_lock, 1);
            char dummy = data[0];        // Save current value before overwrite
            memcpy(data, message, 1);
            semop(sem_id, &sem_unlock, 1);

            printf("%c %c \n", dummy, data[0]);

then maybe you'll see a different result.

But... you don't have any code ensuring an alternating access. Alternating access don't come by itself. You need code to ensure alternating write access. For instance you could add code so that writes to data is only done when data contains the value written by the other process.

Something like:

if (data[0] == message[0]) memcpy(data, read, 1);

and

if (data[0] == read[0]) memcpy(data, message, 1);
  • Related