I am trying to solve the producer consumer problem using mutexes and a shared buffer, but am having trouble accessing values in my shared buffer struct, specifically the char array. When I invoke the producer.c file in one terminal and print the values (the input is a txt file of the alphabet) using
printf("%c", newBuff->bytes[newBuff->rear]);
the chars do appear as normal, however when I do the same thing in consumer.c, but with
printf("%c", newBuff->bytes[newBuff->front]);
the values appear blank. The newBuff->front value is zero, so it should print the letter a. When I access other values in my struct in consumer.c like front, count, or rear they are correct. Share memory creation as well as attachment also works properly so I believe the issue is either I am not storing the char values properly in the array or I am trying to access them incorrectly. In the code below I placed the printf in the loop for producer.c and then outside the loop for consumer.c so I know for a fact a value is present before the consumer starts extracting data.
Consumer.c
typedef struct buffer{
pthread_mutex_t lock;
pthread_cond_t shout;
int front;
int rear;
int count;
int endOfFile;
char bytes[1024];
} buffer;
int main(int argc, char const *argv[]) {
int i=0;
FILE *file = fopen(argv[1], "w");
if (argc != 2){
printf("You must enter in a file name\n");
}
int shmid, swapCount=0;
char swapBytes[] = "";
char path[] = "~";
key_t key = ftok(path, 7);
buffer* newBuff;
if ((shmid = shmget(key, SIZE, 0666 | IPC_CREAT | IPC_EXCL)) != -1) {
newBuff = (buffer*) shmat(shmid, 0, 0);
printf("successful creation\n");
newBuff->front = 0;
newBuff->count = 0;
newBuff->endOfFile = 0;
pthread_mutexattr_t attr;
pthread_condattr_t condAttr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&newBuff->lock, &attr);
pthread_condattr_init(&condAttr);
pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(&newBuff->shout, &condAttr);
} //shared memory creation
else if ((shmid = shmget(key, 0, 0)) != -1){
printf("%d\n", shmid);
printf("successful attachment\n" );
newBuff = (buffer*) shmat(shmid, 0, 0);
printf("%c\n", newBuff->count);
}
else{
printf("oops\n");
exit(0);
}
pthread_mutex_lock(&newBuff->lock);
printf("%c\n", newBuff->bytes[newBuff->front]);
while (newBuff->endOfFile != 1)
{
while (newBuff->count == 0){
pthread_cond_signal(&newBuff->shout);
pthread_cond_wait(&newBuff->shout, &newBuff->lock);
}
newBuff->front = ((newBuff->front + 1)%SIZE);
newBuff->count--;
}
pthread_mutex_unlock(&newBuff->lock);
shmdt(&newBuff);
//pthread_mutexattr_destroy(&attr);
//pthread_condattr_destroy(&condAttr);*/
return 0;
}
Producer.c
typedef struct buffer{
pthread_mutex_t lock;
pthread_cond_t shout;
int front;
int rear;
int count;
int endOfFile;
char bytes[1024];
} buffer;
int main(int argc, char const *argv[]) {
FILE *file = fopen(argv[1], "r");
if (argc != 2){
printf("You must enter in a file dumbass\n");
}
int shmid;
char path[] = "~";
key_t key = ftok(path, 7);
buffer* newBuff;
printf("dfasdfasdf\n");
if ((shmid = shmget(key, SIZE, 0666 | IPC_CREAT | IPC_EXCL)) != -1) {
newBuff = (buffer*) shmat(shmid, 0, 0);
printf("successful creation\n");
newBuff->front = 0;
newBuff->count = 0;
newBuff->endOfFile=0;
pthread_mutexattr_t attr;
pthread_condattr_t condAttr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&newBuff->lock, &attr);
pthread_condattr_init(&condAttr);
pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(&newBuff->shout, &condAttr);
} //shared memory creation
else if ((shmid = shmget(key, 0, 0)) != -1){
printf("successful attachment\n" );
newBuff = (buffer*) shmat(shmid, 0, 0);
}
else{
printf("oops\n");
exit(0);
}
printf("%d\n", shmid);
pthread_mutex_lock(&newBuff->lock);
while (fscanf(file, "%c", &newBuff->bytes[newBuff->rear]) != EOF) //read in file
{
printf("%c\n", newBuff->bytes[newBuff->rear]);
while (newBuff->count >= SIZE){ //buffer is full
//("%c\n", newBuff->bytes[newBuff->rear]);
pthread_cond_signal(&newBuff->shout);
pthread_cond_wait(&newBuff->shout, &newBuff->lock);
}
//printf("%c\n", newBuff->bytes[newBuff->rear]);
newBuff->rear = ((newBuff->front + 1)%SIZE);
newBuff->count++;
}
newBuff->endOfFile = 1;
pthread_cond_signal(&newBuff->shout);
pthread_mutex_unlock(&newBuff->lock);
shmdt(&newBuff);
//pthread_mutexattr_destroy(&attr);
//pthread_condattr_destroy(&condAttr);
return 0;
}
There are several difficulties with your code, some already addressed in comments:
ftok() requires the path passed to it to designate an existing file, but the path you are passing does not.
You request less shared memory than you actually need: only the size of the buffer content, not of a whole struct buffer. Because the amount of shared memory actually allocated will be rounded up to a multiple of the page size, this may end up being ok, but you should ensure that it will be ok by requesting the amount you actually need.
System V shared memory segments have kernel persistence, so once created, they will continue to exist until they are explicitly removed or the system is rebooted. You never remove yours. You also initialize its contents only when you first create it. Unless you manually delete it between runs, therefore, you'll use old data -- with the end-of-file indicator set, for instance -- on the second and subsequent runs. I suggest having the consumer schedule it for removal.
The consumer prints only one byte of data from the buffer, and it does so before verifying that there is anything to read.
After adding a byte to the buffer, the producer does not update the available byte count until after signaling the consumer. At best, this is wasteful, because the consumer will not see the change in count until the next time (if any) it wakes.
The producer updates the rear index of the buffer incorrectly, based on the current front value instead of on the current rear value. The data will therefore not be written into the correct places in the buffer array.
Once the producer sets the endOfFile flag, the consumer ignores all but one of any remaining unread bytes.
If the producer leaves the count zero when it finishes, the consumer will deadlock.
I find that modified versions of your programs addressing all of these issues successfully and accurately communicate data through shared memory.
Update:
Also,
The way in which consumer and / or producer initializes the mutex and condition variable is not itself safe. It is possible for whichever process attempts the shmget() second (or third, or ...) to access those objects before the first finishes initializing them. More generally, once a shared memory segment is attached, there is no inherent memory barrier involved in writing to it. To address these issues, the natural companion to SysV shared memory is SysV semaphores.
Related
I am learning how to use threads in C and have run into a problem when creating the threads. I am making a program that takes in 2 or more file names as command line arguments, counts the number of bytes in each file in their own thread, and then outputs the name of the largest file. When I use pthread_join() directly after creating a thread, the program runs as intended. However, I know this isn't how threads should be used because it defeats the purpose. When I use pthread_join() in a for loop after creating all the threads, then the program does not work correctly. Could anyone tell me what I am doing wrong? All help is appreciated. Here is my main function.
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //mutex for changing max_bytes and max_name
int max_bytes = 0;
char max_name[100];
struct arg_struct{ //struct to hold args to pass the threads
int fd;
char name[100];
};
int main(int argc, char* argv[])
{
if(argc < 3){ //checks for correct number of arguments passed
perror("Wrong number of arguments");
return EXIT_FAILURE;
}
int arg_num = argc - 1; //holds number of arguments passed
pthread_t threadid[arg_num]; //array of thread IDs
struct arg_struct args;
for(int i = 0; i < arg_num; i++){
args.fd = open(argv[i+1], O_RDONLY);
memcpy(args.name, argv[i+1], sizeof(args.name)); //copies file name into arg_struct
int thread_err = pthread_create(&threadid[i], NULL, count_bytes, (void*)&args); //create thread by calling count_bytes and passing it a struct of args
//pthread_join(threadid[i], NULL);
if(thread_err != 0){
perror("pthread_create failed");
return EXIT_FAILURE;
}
}
for(int i = 0; i < arg_num; i++){
pthread_join(threadid[i], NULL);
}
printf("%s is the largest of the submitted files\n", max_name);
return 0;
}
This is the function that the threads are running.
void *count_bytes(void* arguments)
{
struct arg_struct *args = (struct arg_struct*)arguments; //casting arguments back to struct from void*
int fd = args -> fd;
char name[100];
memcpy(name, args -> name, sizeof(name)); //copies file name into name from args.name
int bytes = 0;
int size = 10;
char* buffer = (char*) malloc(size);
if(buffer == NULL){
perror("malloc failed");
exit(EXIT_FAILURE);
}
int buffer_count = 0;
for(int i = 0; i < size; i++){
buffer[i] = '\0'; //sets all elements to '\0' to determine end of file later
}
int read_return = read(fd, &buffer[buffer_count], 1);
if(read_return == -1){
perror("reading failed");
exit(EXIT_FAILURE);
}
while(buffer[buffer_count] != '\0'){
bytes++;
buffer_count++;
buffer[buffer_count] = '\0'; //sets previous element to '\0' to determine end of file later
if(buffer_count >= size){
buffer_count = 0; //buffer will hold up to 10 elements and then go back to the beginning
}
read_return = read(fd, &buffer[buffer_count], 1);
if(read_return == -1){
perror("reading failed");
exit(EXIT_FAILURE);
}
}
printf("%s has %d bytes\n", name, bytes);
pthread_mutex_lock(&mutex);
if(bytes > max_bytes){
max_bytes = bytes;
memcpy(max_name, name, sizeof(max_name));
}
//locks mutex to avoid race condition
//then sets bytes to max_bytes if it is later than max_bytes
//then locks mutex to allow another thread to have access
pthread_mutex_unlock(&mutex);
return NULL;
}
If it is of any use, these are the two outputs produced when it is running correctly
./a.out another buffered_readword.c
another has 8 bytes
buffered_readword.c has 3747 bytes
buffered_readword.c is the largest of the submitted files
And not correctly
./a.out another buffered_readword.c
buffered_readword.c has 1867 bytes
buffered_readword.c has 1881 bytes
buffered_readword.c is the largest of the submitted files
The problem is that there is only one args structure. After pthread_create is called the new thread may not run immediately. By the time the threads run it is likely that they will both see the same args values. Calling pthread_join inside the thread creation loop "fixes" that because it ensures each thread finishes before args is updated to the next value.
To fix properly pass a different args to each thread. Illustrative code to do that:
struct arg_struct args[arg_num];
for(int i = 0; i < arg_num; i++){
args[i].fd = open(argv[i+1], O_RDONLY);
memcpy(args[i].name, argv[i+1], sizeof(args[i].name));
int thread_err = pthread_create(&threadid[i], NULL, count_bytes, &args[i]);
....
I am trying to initialize some variables in my struct, but I am getting a seg fault when assigning my front variable to equal zero. Specifically newBuff->front = 0;
typedef struct buffer{
pthread_mutex_t lock;
pthread_cond_t shout;
int front;
int rear;
char bytes[1024];
} buffer;
int main(int argc, char const *argv[]) {
FILE *file = fopen(argv[1], "r");
if (argc != 2){
printf("You must enter in a file name\n");
}
printf("%lu\n", sizeof(file));
int shmid;
char path[] = "~";
key_t key = ftok(path, 7);
shmid = shmget(key, SIZE, 0666 | IPC_CREAT | IPC_EXCL); //shared memory creation
buffer* newBuff = (buffer*) shmat(shmid, 0, 0);
newBuff->front = 0;
You're not checking the value of newBuff returned by shmat() to ensure that it is not invalid, e.g. (void*) -1 (per http://man7.org/linux/man-pages/man2/shmop.2.html). You also need to check the return value of shmget() to ensure that it succeeded in the first place.
Almost certainly, newBuff is -1, and trying to dereference that gives you a segfault.
several things I can see:
Wrong control of the arguments. You are checking them, but are you exiting your program? ;)
You are not checking the result when you invoke functions as shmat. Review the manual (man shmat).
Said the above, I can not see your whole code, but this is my recommendation:
typedef struct buffer{
pthread_mutex_t lock;
pthread_cond_t shout;
int front;
int rear;
char bytes[1024];
} buffer;
int main(int argc, char const *argv[]) {
int shmid = -1;
FILE *file = NULL;
if (argc != 2){
printf("You must enter in a file dumbass\n");
// And you must terminate here your program!
return 1;
}
file = fopen(argv[1], "r");
// Another check that you are not making and can raise a SIGVSEG
if (file == NULL) {
printf("The file '%s' can not be opened\n", argv[1]);
return 1;
}
printf("File size: %lu\n", sizeof(file));
char path[] = "~";
key_t key = ftok(path, 7);
// Another check
if (key == -1) {
fclose(f);
printf("The path '%s' does not exist or cannot be accessed\n", path);
return 1;
}
shmid = shmget(key, SIZE, 0666 | IPC_CREAT | IPC_EXCL);
// One more check
if (shmid == -1) {
fclose(f);
printf("An error happened getting shared memory identifier\n");
return 1;
}
buffer* newBuff = (buffer*)shmat(shmid, 0, 0);
// And finally! Another potential source that could raise a SIGVSEG
if (buffer == NULL) {
fclose(f);
printf("An error happened getting the shared memory area\n");
return 1;
}
newBuff->front = 0;
Please! Check every return of the functions! You can not imagine how many real problems happen because such returns are not checked properly because of bad practices.
Generalities and functioning of my program
NB : you will be able to test my program (only one file, containing the main function). I give you the entire code at the end of this question.
I wrote a program which can be used to illustrate the producer-consumer algorithm, with UNIX-Processes. The producer creates some value, for example 5, writes it into a RAM shared_buffer and then writes the latter into a file test.txt. The consumer assigns to this shared_buffer the content of the file test.txt and takes some value from the RAM buffer, shared_buffer.
I use functions to convert my shared_buffer into the file, and reciprocally : arrayToFile and fileToArray. Both are presented at the end of this question.
The shared_buffer has a size of 1 + 10 cases : the first one contains the number of full cases (ie. : with a 5 writen) and the 10 others can be filled either with 5 or nothing.
The first case is useful for the producer, to know where to write next value (ie. : in which case).
The file of course has also 1 + 10 cases. The file is needed because I use processes and not threads (not-shared memory, thus).
shared_buffer's initialisation is contained in the main function. shared_buffer's accesses (in reading and in writing) are contained in consumer's function and in producer's function, respectively. These codes are presented at the end of this question.
Access to shared_buffer and overall to the file are of course under mutual exclusion and three semaphores are used. The mutexe impedes producer and consumer to access it at the same time, and the two other semaphores are used to guarantee that the producer won't try to put a new element if there isn't enough place, and that the consumer won't try to take an element if there isn't any element. Well, it's just the producer-consumer algorithm.
Finally, producer's process runs until the end of time, and so does the consumer's process.
The declaration and initialisation of these three semaphores are presented at the end of this question.
My problem
There is only one problem : when both producer's process and consumer's process are running until the end of times while(TRUE), arrayToFile and fileToArray tell me that the file's opening failed. If I erase one or both while(TRUE), this error disapears (but thus, my program doesn't make its job).
So this problem appears only when both while(TRUE) are writen.
I think it's because I don't make good use of the mutexe. But I couldn't give you more explanations.
Source
Code is highly commented.
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/sem.h>
#include <stdlib.h>
#define TRUE 1
#define ERR_NBR_ARGS "ERROR. Argument needed. Use as follows.\n"
int fileToArray(char *, int *, int);
int arrayToFile(char *, int *, int);
void check_if_command_arguments_are_correct(int);
void mutual_exclusion_produce(int, char*, int*, int);
void mutual_exclusion_consume(int, char*, int*, int);
int main(int argc, char* argv[]) {
check_if_command_arguments_are_correct(argc);
// FILE'S PATH
char* file_path = argv[1];
// DECLARATION AND INITIALISATION OF THE SHARED RESOURCE
int shared_buffer_number_of_elements = 10;
int shared_buffer[shared_buffer_number_of_elements + 1];
shared_buffer[0] = 0;
arrayToFile(file_path, shared_buffer, shared_buffer_number_of_elements);
// FILE'S KEY (used to make processes able to use the same semaphores)
key_t key = ftok(argv[0], '0');
if(key == - 1) {
perror("ftok");
exit(EXIT_FAILURE);
}
// DECLARATION AND INITIALISATION OF THE THREE SEMAPHORES
int semid = semget(key, 3, IPC_CREAT|IPC_EXCL|0666); // Declaration of the semaphores
if(semid == -1) {
perror("semget");
exit(EXIT_FAILURE);
}
int array_semaphores_values[3];
array_semaphores_values[0] = 1;
array_semaphores_values[1] = shared_buffer_number_of_elements;
array_semaphores_values[2] = 0;
int sem_controller = semctl(semid, 3, SETALL, array_semaphores_values); // Initialisation of the semaphores - 2th parameter is the array's size
if(sem_controller == -1) {
perror("semctl");
exit(EXIT_FAILURE);
}
// THE TWO FORKS - CREATION OF BOTH PRODUCER AND CONSUMER
pid_t producer = fork();
if(producer == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if(producer == 0) { // The producer process
mutual_exclusion_produce(semid, file_path, shared_buffer, shared_buffer_number_of_elements);
} else { // The main process
pid_t consumer = fork();
if(consumer == - 1) {
perror("fork");
exit(EXIT_FAILURE);
} else if(consumer == 0) { // The consumer process
mutual_exclusion_consume(semid, file_path, shared_buffer, shared_buffer_number_of_elements);
}
}
semctl(semid, 0, IPC_RMID, 0); // The semaphores are freed
}
void mutual_exclusion_produce(int semid, char* file_path, int* buffer, int size) {
/* The producer does the following :
* 1. It decrements the free cases semaphore ;
* 2. It decrements the mutex ;
* 3. It writes the buffer
* 4. It increments the mutex ;
* 5. It increments the full cases semaphore ;
* */
while(TRUE) {
// DECREMENTS FREE CASES SEMAPHORE AND DECREMENTS MUTEX
struct sembuf operation_decrement_free_cases;
operation_decrement_free_cases.sem_num = 2;
operation_decrement_free_cases.sem_op = -1;
operation_decrement_free_cases.sem_flg = 0;
struct sembuf operation_decrement_mutex;
operation_decrement_mutex.sem_num = 0;
operation_decrement_mutex.sem_op = -1;
operation_decrement_mutex.sem_flg = 0;
semop(semid, &operation_decrement_free_cases, 0);
semop(semid, &operation_decrement_mutex, 0);
// WRITES THE BUFFER INTO THE FILE
buffer[++buffer[0]] = 5;
arrayToFile(file_path, buffer, size);
// INCREMENTS THE MUTEX AND INCREMENTS THE FULL CASES SEMAPHORE
struct sembuf operation_increment_full_cases;
operation_decrement_free_cases.sem_num = 1;
operation_decrement_free_cases.sem_op = +1;
operation_decrement_free_cases.sem_flg = 0;
struct sembuf operation_increment_mutex;
operation_decrement_mutex.sem_num = 0;
operation_decrement_mutex.sem_op = +1;
operation_decrement_mutex.sem_flg = 0;
semop(semid, &operation_increment_mutex, 0);
semop(semid, &operation_increment_full_cases, 0);
}
}
void mutual_exclusion_consume(int semid, char* file_path, int* buffer, int size) {
/*
* The consumer does the following :
* 1. It decrements the full cases semaphore ;
* 2. It decrements the mutex ;
* 3. It reads the buffer ;
* 4. It increments the mutex ;
* 5. It increments the free cases semaphore ;
* */
while(TRUE) {
// DECREMENTS FULL CASES SEMAPHORE AND DECREMENTS MUTEX
struct sembuf operation_decrement_full_cases;
operation_decrement_full_cases.sem_num = 1;
operation_decrement_full_cases.sem_op = -1;
operation_decrement_full_cases.sem_flg = 0;
struct sembuf operation_decrement_mutex;
operation_decrement_mutex.sem_num = 0;
operation_decrement_mutex.sem_op = -1;
operation_decrement_mutex.sem_flg = 0;
semop(semid, &operation_decrement_full_cases, 0);
semop(semid, &operation_decrement_mutex, 0);
// READS THE FILE AND PUT THE CONTENTS INTO THE BUFFER
fileToArray(file_path, buffer, size);
// INCREMENTS THE MUTEX AND INCREMENTS THE FREE CASES SEMAPHORE
struct sembuf operation_increment_free_cases;
operation_decrement_full_cases.sem_num = 2;
operation_decrement_full_cases.sem_op = +1;
operation_decrement_full_cases.sem_flg = 0;
struct sembuf operation_increment_mutex;
operation_decrement_mutex.sem_num = 0;
operation_decrement_mutex.sem_op = +1;
operation_decrement_mutex.sem_flg = 0;
semop(semid, &operation_increment_mutex, 0);
semop(semid, &operation_increment_free_cases, 0);
}
}
void check_if_command_arguments_are_correct(int argc) {
if(argc != 2) {
fprintf(stderr, ERR_NBR_ARGS);
fprintf(stderr, "program_command <file_buffer>\n");
exit(EXIT_FAILURE);
}
}
int fileToArray(char *pathname, int *tab, int size) {
int cible;
if ( (cible = open(pathname,O_RDONLY)) < 0){
fprintf(stderr,"fileToArray - impossible to open the file\n");
return -1;
}
if (read(cible,tab,(size+1) * sizeof(int)) !=(size+1) * sizeof(int)) {
fprintf(stderr,"fileToArray - impossible to read the file\n");
return -1;
}
close(cible);
return 0;
}
int arrayToFile(char *pathname, int *tab, int size) {
int cible;
if ( (cible = open(pathname,O_WRONLY|O_CREAT|O_TRUNC,0666)) < 0){
fprintf(stderr,"arrayToFile - impossible to open the file\n");
return -1;
}
if (write(cible,tab,(size+1) * sizeof(int)) !=(size+1) * sizeof(int)) {
fprintf(stderr,"arrayToFile - impossible to write the file\n");
return -1;
}
close(cible);
return 0;
}
I 'm working on a sample program on mmap and shared memory. Here is the piece of code I was trying,
Process B
#include<stdio.h>
#include<sys/mman.h>
#include<fcntl.h>
#include<unistd.h>
#include<malloc.h>
typedef struct sh_mem_t{
int offset;
char *buffer;
}sh_mem;
int main(){
int fd;
sh_mem *shm_obj;
fd = shm_open("/myshm",O_RDWR,0777);
if(fd == -1){
perror("fd:ERROR");
return -1;
}
shm_obj = mmap(0,sizeof(sh_mem),PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
if(shm_obj == MAP_FAILED){
perror("shm_obj:ERROR");
return -1;
}
printf("\n offset : %d \n",shm_obj->offset);
// printf("\n Good work! : %s \n",shm_obj->buffer);
return 0;
}
Process A
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<malloc.h>
#include<string.h>
#include<stdlib.h>
#include<sys/mman.h>
#include<sys/sem.h>
typedef struct sh_mem_t{
int offset;
char *buffer;
}sh_mem;
int main(int argc,char *argv[]){
int file_size = 0;
int fd_sh = 0;
sh_mem *shmptr = NULL;
int fd = 0;
char offset[2];
int no_bytes_read = 0;
int read_size = 10;
int count = 0;
int ret_val = 0;
/* Variables for semaphore */
int ret = 0;
int semid = 0;
key_t sem_key = 0;
struct sembuf op[1];
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
};
union semun arg;
/* Validate the i/p parameters */
if(argc < 3){
perror("argc:Did u forget the I/P file and the count 0?");
return -1;
}
printf("File : %s",argv[1]);
count = atoi(argv[2]);
/* Create a semaphore */
semid = semget(sem_key,1,IPC_CREAT | 0777);
if(semid == -1){
perror("semid:");
return -1;
}
arg.val = 1;
ret = semctl(semid,0,SETVAL,arg);
/* Open the file to read the contents */
fd = open(argv[1],O_RDONLY);
/* Calculate the total size of the file */
file_size = lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
printf("\n File Size is : %d \n",file_size);
/* Create a new memory object */
fd_sh = shm_open("/myshm",O_RDWR | O_CREAT,0777);
/* Set the memory object's size */
if((ftruncate(fd_sh,sizeof(sh_mem))) == -1){
perror("ftruncate:ERROR");
return -1;
}
/* Map the Memory object */
shmptr = mmap(0,sizeof(sh_mem),PROT_READ | PROT_WRITE,MAP_SHARED,fd_sh,0);
/* Allocate the memory for the buffer */
shmptr->buffer = malloc((sizeof(char)*file_size));
printf("\nThe Map address is : 0x%08x\n",shmptr);
/* Copy the contents to the shared memory */
read(fd,&offset,1);
if(count == 0){
shmptr->offset = 0;
}
while(shmptr->offset < file_size){
/* Semaphore section Start */
op[0].sem_num=0;
op[0].sem_op=-1;
op[0].sem_flg=0;
semop(semid,op,1);
printf("\n ProcessA Entering! \n");
printf("\n initial offset value : %d \n",shmptr->offset);
if(shmptr->offset > 0){
shmptr->buffer = shmptr->buffer + shmptr->offset;
ret_val = lseek(fd,shmptr->offset,SEEK_SET);
}
no_bytes_read = read(fd,shmptr->buffer,read_size);
shmptr->offset = (read_size + shmptr->offset);
printf("\n offset : %d \n",shmptr->offset);
printf("\n contents : %s \n",shmptr->buffer);
sleep(10);
op[0].sem_op = 1;
semop(semid,op,1);
printf("\n ProcessA Leaving ! \n");
/* Semapore section End*/
}
/* Detach from the shared memory */
shmdt(shmptr);
close(fd);
close(fd_sh);
return 0;
}
I have process A, which has put the data into the shared memory containing the structure values offset and buffer. Process B wants to access the contents stored in the shared memory(offset,buffer), but I could able to access only offset. When tried to access the buffer i'm getting a segmentation fault. Why am i getting a seg fault. As the shared object is mapped to the shared memory.
Process A will put 10 bytes into the shared memory and will go to sleep, then again it continues to put the next 10 bytes and so on.
When tried to access the buffer i'm getting a segmentation fault.
buffer is declared as pointer as part of your mapped memory:
typedef struct sh_mem_t{
int offset;
char *buffer;
}sh_mem;
Transferring pointers between processes does not make sense, since the pointer does not have any meaning in the slave process - the data it points to still resides in the master process.
You need to include the actual data you want to transfer from master to slave process:
typedef struct sh_mem_t{
int offset;
char buffer[BUFSIZE];
}sh_mem;
With the updated code in the question, the following changes are necessary to make it work:
In both A and B, change the declaration of the shared memory struct to something like
typedef struct sh_mem_t{
int offset;
char buffer[1024];
}sh_mem;
In A, remove the malloc() for shmptr->buffer. Also remove the line where you adjust the buffer by adding the offset (shmptr->buffer = shmptr->buffer + shmptr->offset;) - that needs to be handled differently, if you still need it
In B, uncomment the line which prints the Good work! output.
With these changes, I was able to start the A process like ./A data.txt 0. When I then start the B process, it prints both offset and buffer content, as it was last printed by the A process.
Some additional remarks
You should use a header file to declare the sh_mem struct, and include this file in both your .c files, to make sure the declaration is consistent between A and B.
With the solution I posted above, the application will crash with file sizes > 1024. You need to handle this accordingly, to make sure to not exceed the buffer size.
Why isnt it working with pointers
You can not access (non-shared) memory from the master process in the slave process, especially not by simply passing a pointer through the shared memory (this would make the shared memory concept obsolete). The memory which you allocated with malloc() in your master process is not part of the shared memory segment, so it is not accessible from the slave process.
In addition, mmap(), by default, is not guaranteed to return the same virtual address in both processes. So, even when you pass a pointer which points to a location inside the shared memory segment in the master, it does not point anywhere useful inside the slave process, unless you pass specific parameters to mmap(). See mmap(2) for more details.
I am new to linux environment. I just know the basics of C. I am trying to learn linux programming. For this I am trying an example on shared memory. Please someone help me with this example.
I am trying to send person details (like name, phone number & address) to another process using Shared memory. After receiving the data by the second process, I am trying to save received data into a file. This is the task I am doing.
I am able to send just the name and receive it in the second process. Can someone please help how to send the data(like name, phone number & address) to second process and in the second process it must print the data and it should save the data to a file.
Here is my code:
address.c
char *shared_memory;
int main()
{
int select;
int segment_id;
char* shared_memory;
int segment_size;
key_t shm_key;
const int shared_segment_size = 0x6500;
shm_key = ftok("/home/madan/programs/shm_tok",'C');
if(shm_key < 0) {
printf("failed to create the key %s\n",strerror(errno));
}
/* Allocate a shared memory segment. */
segment_id = shmget (shm_key, shared_segment_size,
IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
if(segment_id < 0) {
printf("error geting the segment id %s\n",strerror(errno));
}
printf("segment ID:%d\n", segment_id);
/* Attach the shared memory segment. */
shared_memory = (char*) shmat (segment_id, 0, 0);
printf ("shared memory attached at address %p\n", shared_memory);
/* I want to send these details to the shared memory. Can someone suggest me the correct way to send these details to shared memory so that second process can retrieve them*/
sprintf(shared_memory, "maddy\n");
sprintf(shared_memory, "767556686");
sprintf(shared_memory, "Ontario");
system("./address-insert");
/* Detach the shared memory segment. */
shmdt (shared_memory);
/
* Deallocate the shared memory segment.*/
shmctl (segment_id, IPC_RMID, 0);
}
addres-insert.c
int main ()
{
int segment_id;
char* shared_memory;
FILE *fp;
char *name;
int segment_size;
key_t shm_key;
shm_key = ftok("/home/madan/programs/shm_tok",'C');
const int shared_segment_size = 0x6500;
/* Allocate a shared memory segment. */
segment_id = shmget (shm_key, shared_segment_size,
S_IRUSR | S_IWUSR);
if(segment_id < 0) {
printf("error:[%s]",strerror(errno));
}
printf("segment id %d\n",segment_id);
/* Attach the shared memory segment. */
shared_memory = (char*) shmat (segment_id, 0, 0);
if(shared_memory == NULL) {
printf("failed to attach the shared memory %s",strerror(errno));
}
printf ("shared memory2 attached at address %p\n", shared_memory);
/* printing the data from shared memory send by first process*/
printf ("name=%s\n", shared_memory);
/*copying the data in shared memory so i can save them to a file*/
strcpy(name, shared_memory);
printf("%s", name);
/*here i have to save the data to a file. But i don't know how to do it, can someone help me with this please*/
/* Detach the shared memory segment. */
shmdt (shared_memory);
return 0;
}
For saving the data in a file, you can use file stream. For that you have to know about file streams.
Hope these link will help.
http://linux.die.net/man/3/fopen
http://en.wikipedia.org/wiki/C_file_input/output
Here comes the example program which I particularly typed, compiled and attached it for your reference.
#include<sys/types.h>
#include<string.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<stdio.h>
struct mystruct // you can make your own structure if you want to pass many data
{
int i;
float f;
char c;
int arr[3];
}myObj = {1,1.1,'C',{100,1000,10000}};
main()
{
int shmid;
char* addr1;
key_t key;
//file to key. Generates a unique key
key = ftok("/home/muthu/Desktop/anyfile.txt",'T');
shmid = shmget(key,sizeof(struct mystruct),IPC_CREAT|SHM_R|SHM_W);
printf("shmid = %d",shmid);
addr1 = shmat(shmid,0,0);
printf("\nIPC SHARED MEMORY");
//copying your structure at the shared location.
memcpy(addr1,&myObj,sizeof(myObj));
printf("\nMESSAGE STORED");
}
And for shared memory 2.....
//<All necessary header files>
//<same my struct declaration here>
main()
{
int shmid;
char* addr1;
FILE* fp;
key_t key;
struct mystruct* myObj2;
//Generate the same unique key. Must provide the same file here too.
key = ftok("/home/muthu/Desktop/anyfile.txt",'T');
shmid = shmget(key,sizeof(struct mystruct),SHM_R|SHM_W);
addr1 = shmat(shmid,0,0);
myObj2 = (struct mystruct*)malloc(sizeof(struct mystruct)*1);
if(shmid == -1)
printf("\nShared memory error");
//Retrieve the stored information, form the shared location.
memcpy(myObj2,addr1,sizeof(struct mystruct));
fp = fopen("/home/muthu/Desktop/MyFile.txt","w"); //open a file stream
if(fp == NULL)
printf("\nError on opening file stream.\n");
fprintf(fp,"\nIPC SHARED MEMORY");
fprintf(fp,"\nMESSAGE RECIEVED FORM THE SHARED MEMORY IS..\n");
fprintf(fp,"\ninteger: %d",myObj2->i);
fprintf(fp,"\nfloat: %f",myObj2->f);
fprintf(fp,"\nchar: %c",myObj2->c); //write to the file
fprintf(fp,"\narr: %d %d %d",myObj2->arr[0],myObj2->arr[1],myObj2->arr[2]);
fprintf(fp,"\nDATA RECIEVED.");
fclose(fp); //close the file stream
printf("\nMessage successfully stored!");
}
Here is a snippet that you can use
fp = fopen("filename","a+");
fwrite(name, 1, strlen(name),fp);
fclose(fp);
You can store all your three fields onto a structure and load the structure onto the Shared Memory. In this way you are assured to have all the fields under one structure.
In the process that reads the SHM you can use the File Stream utils as mentioned above by others.