Reputation: 57
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
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
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