I'm struggling with a homework problem. I'm asked to:
Write a program whose main routine obtains one parameter n from the user (n<40), i.e. passed to your program when it was invoked from the shell. Your program shall then create a shared memory and a child process. The shared memory shall have a size of BUF_SZ*sizeof(unsigned short), where BUF_SZ is defined as 5 using a macro, e.g. “#define BUF_SZ 5”.
The child process should obtain the value of n from the parent (you actually have multiple options for doing that) and create a Fibonacci sequence of length n and whose elements are of type unsigned short. You may find more information about Fibonacci numbers at (https://en.wikipedia.org/wiki/Fibonacci_number).
The child process shall create the elements, one at a time, and wait for a random interval of time ( 0 <= time < 2 seconds) between generating elements of the sequence. As soon as an element is generated, the child places the element in the shared memory by organizing it as described in class.
The parent process shall print elements it receives on the shared butter immediately, without waiting for the child process to exit.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <errno.h>
#include <fcntl.h>
#define BUF_SZ 5
#define NAME "buffer"
#define NAME2 "inptr"
#define NAME3 "outptr"
int main() {
int fd[2];
int n = atoi(argv[1]);
unsigned short *data;
int* inptr;
int* outptr;
int size = BUF_SZ * sizeof(unsigned short);
int size2 = sizeof(int);
/*while(n >= 40 || n <= 0) {
printf("Enter a positive integer less than 40: ");
scanf("%i", &n);
}*/
int shmid = shm_open(NAME, O_CREAT|O_RDWR, 0666);
ftruncate(shmid, size);
data = (unsigned short*) mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, shmid, 0);
int shmid2 = shm_open(NAME2, O_CREAT|O_RDWR, 0666);
ftruncate(shmid2, size2);
inptr = (int*) mmap(NULL, size2, PROT_READ|PROT_WRITE, MAP_SHARED, shmid2, 0);
int shmid3 = shm_open(NAME2, O_CREAT|O_RDWR, 0666);
ftruncate(shmid3, size2);
outptr = (int*) mmap(NULL, size2, PROT_READ|PROT_WRITE, MAP_SHARED, shmid3, 0);
memset(inptr, 0, size2); //in variable
memset(outptr, 0, size2); //out variable
pipe(fd);
pid_t pid = fork();
if(pid > 0) {
write(fd[1], &n, sizeof(n)); //write n value to child
while (*inptr == *outptr);
printf("%i\n", data[*outptr]);
fflush(stdout);
*outptr = (*outptr + 1) % BUF_SZ;
}
else if(pid == 0) {
read(fd[0], &n, sizeof(n)); //get n value from parent
int prev = 0;
int curr = 1;
int tmp;
for(int i = 0; i <= n; i++) {
while (((*inptr + 1) % BUF_SZ) == *outptr);
if(i == 0) {
data[*inptr] = 0;
*inptr = (*inptr + 1) % BUF_SZ;
}
else if(i == 1) {
data[*inptr] = 1;
*inptr = (*inptr + 1) % BUF_SZ;
}
tmp = curr;
curr += prev;
prev = tmp;
data[*inptr] = curr;
*inptr = (*inptr + 1) % BUF_SZ;
}
}
else {
printf("failed");
}
return 0;
}
I created shared memory buffers for the elements of the fib sequence and in and out pointers. In writes to the buffer and out reads from the buffer. The parent process checks if the in and out pointers are equal or not. When they are not equal, it reads from the buffer. The child process outputs elements to the buffer when the in pointer is one ahead of the out pointer. The issue is that the parent process gets stuck in an infinite while loop and it doesn't switch to the child process. I'm not sure how to proceed.
A few issues ...
while
loop.shm_open
uses NAME2
instead of NAME3
. This causes inptr
and outptr
to point to the same memory cell. So, they "alias" each other and they will always be the same value.inptr
and outptr
should have volatile
on them [as dimich mentioned].int main(int argc,char **argv)
wait(NULL)
to reap the child).Here is the corrected code. It is annotated. I've added some debug prints to help:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/mman.h>
#define BUF_SZ 5
#define NAME "buffer"
#define NAME2 "inptr"
#define NAME3 "outptr"
#if DEBUG
#define dbgprt(_fmt...) \
printf(_fmt)
#else
#define dbgprt(_fmt...) \
do { } while (0)
#endif
int
main(int argc,char **argv)
{
int fd[2];
int n = atoi(argv[1]);
unsigned short *data;
// NOTE/BUG: without volatile the optimizer could incorrectly elide code
#if 0
int *inptr;
int *outptr;
#else
volatile int *inptr;
volatile int *outptr;
#endif
int size = BUF_SZ * sizeof(unsigned short);
int size2 = sizeof(int);
/* while(n >= 40 || n <= 0) { printf("Enter a positive integer less than 40: "); scanf("%i", &n); } */
int shmid = shm_open(NAME, O_CREAT | O_RDWR, 0666);
ftruncate(shmid, size);
data = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, shmid, 0);
int shmid2 = shm_open(NAME2, O_CREAT | O_RDWR, 0666);
ftruncate(shmid2, size2);
inptr = mmap(NULL, size2, PROT_READ | PROT_WRITE, MAP_SHARED, shmid2, 0);
// NOTE/BUG: this uses the same file as shmid2, so inptr are aliased together
#if 0
int shmid3 = shm_open(NAME2, O_CREAT | O_RDWR, 0666);
#else
int shmid3 = shm_open(NAME3, O_CREAT | O_RDWR, 0666);
#endif
ftruncate(shmid3, size2);
outptr = mmap(NULL, size2, PROT_READ | PROT_WRITE, MAP_SHARED, shmid3, 0);
#if 0
memset((void *) inptr, 0, size2); // in variable
memset((void *) outptr, 0, size2); // out variable
#else
*inptr = 0;
*outptr = 0;
#endif
pipe(fd);
pid_t pid = fork();
if (pid > 0) {
write(fd[1], &n, sizeof(n)); // write n value to child
// NOTE/FIX: we need an outer loop to get all numbers from the child
while (1) {
while (*inptr == *outptr);
printf("parent: %i\n", data[*outptr]);
fflush(stdout);
*outptr = (*outptr + 1) % BUF_SZ;
dbgprt("parent: inptr=%d outptr=%d\n",*inptr,*outptr);
}
}
else if (pid == 0) {
read(fd[0], &n, sizeof(n)); // get n value from parent
int prev = 0;
int curr = 1;
int tmp;
dbgprt("child: n=%d\n",n);
for (int i = 0; i <= n; i++) {
while (((*inptr + 1) % BUF_SZ) == *outptr);
if (i == 0) {
data[*inptr] = 0;
dbgprt("child: i0\n");
*inptr = (*inptr + 1) % BUF_SZ;
}
else if (i == 1) {
dbgprt("child: i1\n");
data[*inptr] = 1;
*inptr = (*inptr + 1) % BUF_SZ;
}
tmp = curr;
curr += prev;
prev = tmp;
printf("curr=%d\n",curr);
data[*inptr] = curr;
*inptr = (*inptr + 1) % BUF_SZ;
dbgprt("child: inptr=%d outptr=%d\n",*inptr,*outptr);
}
}
else {
printf("failed");
}
return 0;
}
In the code above, I've used cpp
conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Note: this can be cleaned up by running the file through unifdef -k