user3763711
user3763711

Reputation: 57

C - Segmentation fault when dealing with shared memory

I'm trying to program a producer consumer problem by pushing and popping characters in and out of a queue. There are two main files, producer.c and consumer.c. Both of them have shared memory space initialized at the beginning. Producer will be run first and will initialize variables in the shared pointer (static struct shared* sharedptr). One of them is a queue (struct Queue *buffer).

Producer reads characters from a file and put into (push) the queue one at a time, and consumer reads, prints, and takes character out (pop) from the queue one at a time.

The problem is: producer runs no problem in terms of getting the queue pointer (memptr -> buffer) and pushing characters in. However, producer cannot access the queue although it should have the same pointer to the queue.

It properly prints when checking if the buffer is NULL when I tested with this if statement:

if(memptr -> buffer == NULL)
    printf("Buffer is empty.\n");
else printf("Buffer is not empty.\n");

The segmentation fault happens when I tested with this:

if(memptr -> buffer == NULL)
    printf("Buffer is empty.\n");
else if (memptr -> buffer -> tail == NULL){
    printf("Tail is NULL. Something went wrong.\n");
    exit(1);
}

I assume it is happening during the time when it's accessing the tail. It doesn't even evaluate if the tail is NULL; it just sent segfault. This didn't happen when I was testing with all the queue-related functions in common.c (provided below), and plus producer is running fine with it.

What is happening with the pointers? Consumer can access other variables in (struct shared*) memptr, but not buffer -> tail. It doesn't make any sense to me.

I compile the programs on my Mac:

cc -c common.c

cc -o producer -Wall producer.c common.o

cc -o consumer -Wall consumer.c common.o

Here's all the code I have:

common.h:

#define MEMSIZE 200
#define BUFFSIZE 5
#define MAXCOUNT 10

struct shared {
    /* synchronization variables */
    int choosing[MAXCOUNT + 1];
    int ticket[MAXCOUNT + 1];
    /* queue variables */
    struct Queue *buffer;
    int endOfFile;
    int in;     //variable that keeps track of bytes coming in
    int out;    //variable that keeps track of bytes coming in
    int count;  //count variable for producers
    FILE *file;
};

struct Link {
    char value;
    struct Link *next;
    struct Link *prev;
} Link;

struct Queue {
    int size;
    struct Link *head;
    struct Link *tail;
} Queue;

void mutexInit(struct shared *memptr);
void getMutex(short pid);
void releaseMutex(short pid);
void firstInit();
int max(int array[], int maxIndex);

struct Queue *initQueue();
struct Queue *push(char ch, struct Queue *q);
struct Queue *pop(struct Queue *q);
void printQueue(struct Queue *q);
char getBuffer(struct Queue *q);

common.c:

#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>

#include <limits.h>

#include "common.h"

#define FALSE 0
#define TRUE 1

static struct shared *sharedptr;

void mutexInit(struct shared *memptr){
    sharedptr = memptr;
}

void firstInit(){
    //initialize all variables to initial state
    int i;

    for(i = 0; i < 11; i++) {
        sharedptr -> choosing[i] = 0;
        sharedptr -> ticket [i] = 0;
    }
    sharedptr -> buffer = initQueue();
    sharedptr -> endOfFile = FALSE;
    sharedptr -> in = 0;
    sharedptr -> out = 0;
    sharedptr -> count = 1;
    sharedptr -> file = fopen("/Users/BenjaminHsu/Documents/ELEC 377/lab3/Queue/lab3.txt", "r");
    if(sharedptr -> file == NULL){
        printf("Can't find file.\n");
            exit(0);
    }
}

void getMutex(short pid){
    // this should not return until it has mutual exclusion. 
    // Note that many versions of this will probobly be running at the same time.
    int j;

    sharedptr -> choosing[pid] = TRUE;
    sharedptr -> ticket[pid] = max(sharedptr -> ticket, sharedptr -> count + 1) + 1; 
    sharedptr -> choosing[pid] = FALSE;
    for (j = 0; j < sharedptr -> count + 1; j++){
        while(sharedptr -> choosing[j] == TRUE);
        while(sharedptr -> ticket[j] != FALSE && ((sharedptr -> ticket[j] <= sharedptr -> ticket[pid])     && j < pid));
    }
}

void releaseMutex(short pid){
    // set the mutex back to initial state so that somebody else can claim it
    sharedptr -> ticket[pid] = 0;
}

int max(int array[], int maxIndex){
    int max = array[0];
    int i;

    if(maxIndex == 0)
        return max;
    for(i = 1; i < maxIndex; i++){
        if(array[i] > max)
            max = array[i];
    }
    return max;
}    

struct Queue *initQueue(){
    struct Queue *q = (struct Queue*)malloc(sizeof(struct Queue));
    q -> size = 0;
    q -> head = NULL;
    q -> tail = NULL;
    return q;
}

struct Queue *push(char ch, struct Queue *q){
struct Link *temp = (struct Link*)malloc(sizeof(struct Link));

if(q != NULL) {
    temp -> value = ch;
    temp -> next = q -> head;
    temp -> prev = NULL;
    if(q -> size == 0){
        q -> head = temp;
        q -> tail = temp;
    } else {
        q -> head -> prev = temp;
        q -> head = temp;
    }
        q -> size++;
    } else {
        printf("The queue is NULL.\n");
        exit(0);
    }
    return q;
}

struct Queue *pop(struct Queue *q){
    if(q != NULL) {
        if(q -> size == 0){
            printf("nothing to pop.\n");
        } else if(q -> size == 1){
            q -> head = NULL;
            q -> tail = NULL;
        } else {
            q -> tail -> prev -> next = NULL;
            q -> tail = q -> tail -> prev;
        }
        q -> size--;
    } else {
        printf("The queue is NULL.\n");
        exit(0);
    }

    return q;
}

char getBuffer(struct Queue *q) {
    if(q -> tail != NULL)
        return (char)q -> tail -> value;
    else {
        printf("Buffer is empty.\n");
        exit(1);
    }
}

void printQueue(struct Queue *q){

    struct Link *temp;
    if(q != NULL){
        if(q -> size > 0){
            temp = q -> head;
            while(temp -> next != NULL){
                printf("%c->", temp -> value);
                temp = temp -> next;
            }
            printf("%c\n", temp -> value);
        } else 
            printf("Queue is NULL.\n");
    } else 
        printf("Queue is empty.\n");
}

producer.c

#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <errno.h>

#include "common.h"

#define FALSE 0
#define TRUE 1

#define MYPID 1

int main (int argc, char *argv[]){

    // initialize the shared memory, load in the initial array's, spawn the worker
    // processes.
    key_t   key;
    struct shared *memptr;
    int shmid;
    int c;
    int pid;
    char ch;


    /*   Shared memory init     */
    key = ftok(".", 'S');
    if((shmid = shmget(key, MEMSIZE, IPC_CREAT|0666)) == -1 ){
        if( (shmid = shmget(key, MEMSIZE, 0)) == -1){
            printf("Error allocating shared memory. \n");
            exit(1);
        }
    }

    // now map the region..
    if((int)(memptr = (struct shared *) shmat(shmid, 0, 0)) == -1){
        printf("Couldn't map the memory into our process space.\n");
        exit(1);
    }

    mutexInit(memptr);

    //_______________________________
    memptr -> count = 0; //temp code to assume and test with one producer only
    //_______________________________


    if(memptr -> count == 0)
        firstInit(memptr);
    else if (memptr -> count >= MAXCOUNT) {
        printf("Exceed maximum limit for number of producers");
        exit(0);
    } else {
        memptr -> count++;
    }

    pid = memptr -> count;

    printf("pid:%d", pid);
    printf("eof: %d", memptr -> endOfFile);

    while(memptr -> endOfFile == FALSE) {
        if((memptr -> in - memptr -> out) < BUFFSIZE){
            ch = fgetc(memptr -> file); //read one character from the text file

            if(ch == EOF){
                memptr -> endOfFile = TRUE;
                break;
            }

            getMutex(pid); //wait for mutex
            memptr -> buffer = push(ch, memptr -> buffer); //write the character into the buffer
            printQueue(memptr -> buffer);
            releaseMutex(pid); 

            //______________________________________
            printf("%c", getBuffer(memptr -> buffer));  //a test to see if producer 
                                                        //can access buffer ->tail
            //______________________________________

            //increment the in variable
            memptr -> in++;
        }
    }

    memptr -> count--;

    return 0;
}

consumer.c:

#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <errno.h>

#include "common.h"

#define FALSE 0
#define TRUE 1

#define MYPID 0

int main (int argc, char *argv[]){
    // initialize the shared memory, load in the initial array's, spawn the worker
    // processes.
    key_t   key;
    struct shared *memptr;
    int shmid;
    char ch;


    /*   Shared memory init     */
    key = ftok(".", 'S');
    if((shmid = shmget(key, MEMSIZE, IPC_CREAT|0666)) == -1 ){
        if( (shmid = shmget(key, MEMSIZE, 0)) == -1){
            printf("Error allocating shared memory. \n");
            exit(1);
        }
    }

    // now map the region..
    if((int)(memptr = (struct shared *) shmat(shmid, 0, 0)) == -1){
        printf("Couldn't map the memory into our process space.\n");
        exit(1);
    }

    mutexInit(memptr);

    do{
        if(memptr -> out < memptr -> in){ //compare the in and out to see if the buffer is empty
            if(memptr -> buffer == NULL)
                printf("Buffer is empty.\n");
            else if (memptr -> buffer -> tail == NULL){
                printf("Tail is NULL. Something went wrong.\n");
                exit(1);
            }

            ch = getBuffer(memptr -> buffer); //read a character from the buffer and print
            //wait for mutex
            getMutex(MYPID);
            printf("%c", memptr -> buffer -> tail -> value);
            memptr -> buffer = pop(memptr -> buffer);
            releaseMutex(MYPID);
            //release mutex

            memptr -> out++;
        }
    } while((memptr -> endOfFile == FALSE) || (memptr -> count != 0));

    return 0;
}

Upvotes: 1

Views: 1235

Answers (2)

Ascot
Ascot

Reputation: 146

You cannot use pointers (struct Queue *buffer and FILE *file) in a shared memory just like that because when you allocate memory with e.g. malloc() in Producer, then only that process can access data from that pointer.

What you could do is create two additional shared memories for *buffer and *file and attach them inside *memptr. But generally pointers aren't really meant to be used in shared memory.

For more detailed information you can check answers in this question: Pointers inside shared memory segment.

Upvotes: 1

DoxyLover
DoxyLover

Reputation: 3484

Here's one problem: you're calling shmat with the paramter shmaddr zero (NULL). This tells the system to map the shared memory segment at whatever address it wants. There is no guarantee that the mapped address will be the same in both processes! If different base addresses are used in the two processes, pointers written by one (producer) will not be valid for the other (consumer).

Upvotes: 1

Related Questions