C: Shared memory and forking, print statement executing several times - c

I'm working on a program that calculates the collatz conjecture for a given number using shared memory. The parent will create a child process, then the child will calculate the conjecture, and using shared memory supply it to the parent so the parent can print the value out.
If the child can't calculate the full conjecture, because it runs out of space to store it in the shared memory struct, then the parent will create a new child process to continue where the last one stopped.
I'm having an issue where the print statements in the parent process that display the childs' results from shared memory are printed multiple times.
/*********************************
* Applies the Collatz conjecture
* to the given positive integer
* using shared memory.
*********************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <sys/stat.h>
#define MAX_SEQUENCE 30
typedef struct {
int size; // Number of values in this sequence
short partial; // Flag
long num[MAX_SEQUENCE];
} shared_data;
int main(int argc, char* argv[])
{
// Name of the shared memory segment
const char *name = "myMemorySeg";
// Shared memory file descriptor
int shm_fd;
// A pointer to the shared memory segment
shared_data* shared_memory;
// Handle input validation
if (argc != 2) {
fprintf(stderr, "Usage: %s num\n", argv[0]);
exit(1);
}
// Get number from argument
int n = atoi(argv[1]);
do {
int pid;
// Create a child process with shared memory space
pid = create_child(&name, &shm_fd, &shared_memory);
// Parent
if (pid > 0) {
wait(NULL);
// Print out collatz results
int i;
for (i = 0; i < shared_memory->size; i++)
printf("%d ", shared_memory->num[i]);
// If this was only part of the sequence
// Then start the next sequence at the collatz of the last sequence value
if (shared_memory->partial)
n = get_collatz(shared_memory->num[MAX_SEQUENCE - 1]);
}
// Child
else if (pid == 0) {
// Generate the collatz sequence and store the result in the shared memory
int i = 0;
shared_memory->num[i++] = n; // Store the initial number
while (n != 1 && i < MAX_SEQUENCE) {
n = get_collatz(n);
shared_memory->num[i++] = n; // Store the next number
}
// If we have filled the sequence array and n hasn't reached 1
// then this is only a partial sequence
shared_memory->partial = (i == MAX_SEQUENCE && n != 1) ? 1 : 0;
// What is the sequence size?
shared_memory->size = i;
// Kill the child process
exit(0);
}
// Remove the shared memory object
shm_unlink(name);
} while (shared_memory->partial); // While the last sequence was partial
printf("\n");
return 0;
}
/********************************
* create_child()
*
* Opens a shared memory space
* and creates a child process
* to share that space with the
* parent.
*
* Returns the process id if
* successful, otherwise exits
* the parent process.
********************************/
int create_child(char **name, int *shm_fd, shared_data** shared_memory) {
// Create a shared memory object
*shm_fd = shm_open(*name, O_CREAT|O_RDWR, 0666);
// Configure the size of the shared memory object
ftruncate(*shm_fd, sizeof(shared_data));
// Memory map the shared memory object
*shared_memory = (shared_data *) mmap(0, sizeof(shared_data), PROT_WRITE, MAP_SHARED, *shm_fd, 0);
// Create child process
int pid;
// Return -1 if error
if ((pid=fork()) == -1) {
perror("Failed to create child process");
exit(1); // Kill parent process
}
// Otherwise return the pid created by fork
return pid;
}
/********************************
* get_collatz()
*
* Returns the result of running
* the input n through the
* collatz conjecture function.
********************************/
int get_collatz(int n) {
return (!(n%2)) ? (n/2) : (3*n + 1);
}
This is what the console output looks like:
Interestingly, if I add a print statement with a new line to the parent process before it prints out the results of the child process from shared memory like so:
do {
int pid;
printf("\n");
// Create a child process with shared memory space
pid = create_child(&name, &shm_fd, &shared_memory);
// Parent
if (pid > 0) {
wait(NULL);
// Print out collatz results
int i;
for (i = 0; i < shared_memory->size; i++)
printf("%d ", shared_memory->num[i]);
// If this was only part of the sequence
// Then start the next sequence at the collatz of the last sequence value
if (shared_memory->partial)
n = get_collatz(shared_memory->num[MAX_SEQUENCE - 1]);
}
// Child
Then the print statements will be output the correct number of times.
Another interesting fact is that this solution only works if I place the newline print statement before the create_child() call, and not if I place it after.
I don't want these to be separated by new lines, I want them to print all on one line. Any ideas what is causing these extra print statements?

You either need to add appropriate flush calls or you need to change your standard output not to be buffered.
The library is trying to be efficient and not actually write to the terminal until it has a full line. So it stores partial lines in a buffer. When you fork, you wind up with two processes that are substantially identical, that is, they each have the same buffered data. If both of them finishes a line of output, both of them will write the buffered data.
You may still have an issue that all the various outputs are jumbled up together. The more usual way to handle this is to have only one process responsible for all output and the "worker" processes communicate their results back to the "manager" process to be printed in a sane, orderly way.

Related

Passing information between child and parent process - linux (without `pipe()`)

As part of my assignment, I have to implement child-parent processes.
"The parent process shall NOT wait for the child to exit but instead shall prints an element as soon as it arrives into the shared buffer ".
I know the case when the parent has to wait for child to end, but how to implement communication of message from child to parent in async way?
P.S. The exact Q is
Write a program whose main routine obtains two parameters n and d from the user, 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 child process should obtain the values of n and d (you have multiple choices on how to do that) and create an arithmetic sequence of length n, and whose first element is 0 and each subsequent element has the value of kd, where k is the element number (k= 0 to n-1).
The child process shall create the elements, one at a time and wait for a random interval of time (0 to 9.999 seconds) between generating elements of the sequence. As soon as an element is generated, the child places the element in the shared buffer by organizing it as described in slides 33-37 of lecture 4.
(e.g.: if n=5 and d=2, the sequence shall be 0,2,4,6,8)
The parent process shall NOT wait for the child to exit but instead shall prints an element as soon as it arrives into the shared buffer (again, in a manner similar to slides 33-37 of lecture 4)
Hint; use fflush() to ensure printf’s are printed immediately into the screen.
My code so far - gist
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
void printLab4(int n, int a, int *fibo)
{
int i;
for (i = 0; i < n; i++)
printf("%d -> %d\n", i, fibo[i]);
}
void computeLab4(int n, int a, int *fibo)
{
int i;
for (i = 0; i < n; i++)
{
int sleepSec = rand() % 10;
printf("sleeping for %d : ", sleepSec);
sleep(sleepSec);
fibo[i] = i * a;
// randomly sleeping for 0-10 secs
printf("Generated new element %d after %d seconds \n", fibo[i], sleepSec);
}
}
int main(int argc, char *argv[])
{
pid_t childPID;
int status;
int shm_fd;
int *shared_memory;
int msize; // the size (in bytes) of the shared memory segment
const char *name = "Lab_4";
int n, a;
if (argc != 3)
{
fprintf(stderr, "usage: %s <Lab4 Seq to be generated>\n", argv[0]);
return -1;
}
n = atoi(argv[1]);
a = atoi(argv[2]);
printf("%d \n", n);
printf("%d \n", a);
if (n < 0 || a < 0)
{
fprintf(stderr, "Illegal number: %s\n", argv[1]);
return -2;
}
// calculating the array size based on the number of terms being passed from child to parent
msize = (n + 2) * sizeof(int);
// open the memory
shm_fd = shm_open(name, O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG);
if (shm_fd < 0)
{
fprintf(stderr, "Error in shm_open()");
return -3;
}
printf("Created shared memory object %s\n", name);
// attach the shared memory segment
ftruncate(shm_fd, msize);
printf("shmat returned\n");
// allocating the shared memory
shared_memory = (int *)mmap(NULL, msize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (shared_memory == NULL)
{
fprintf(stderr, "Error in mmap()");
return -3;
}
printf("Shared memory segment allocated correctly (%d bytes).\n", msize);
shared_memory[0] = n;
shared_memory[1] = a;
childPID = fork();
if (childPID == -1)
{
fprintf(stderr, "Cannot proceed. fork() error");
return -4;
}
if (childPID == 0)
{
// then we're the child process
computeLab4(shared_memory[0], shared_memory[1], shared_memory + 1);
exit(0);
}
else
{
// parent will wait until the child finished
wait(&status);
// print the final results in the
printLab4(shared_memory[0], shared_memory[1], shared_memory + 1);
// now detach the shared memory segment
shm_unlink(name);
}
return 0;
}
Since you are acquiring the values from the command line at the time the program is invoked by the shell, when the parent calls fork(), the child will have access to the same variables. There is no need to "pass" the values from parent to child.
When the child creates the elements, I assume the parent is reading the elements from the shared memory. The "as soon as it arrives" is a somewhat dubious requirement unless you are on a parallel and real-time system. However, simulating that requirement can be achieved with a busy wait loop of the parent checking to see if the shared memory has acquired new data.
In order to not starve the operating system of CPU cycles while the parent is in the busy wait loop, the parent can call sched_yield() between check iterations, which behaves like a sleep that instantly wakes up.
The shared memory between parent and child can be treated as some kind of queue, and the busy wait of the parent is checking if the queue is non-empty, and if so, processes queue elements until it is empty, at which point it busy waits again.
for (;;) {
if (q_is_empty(q)) {
sched_yield();
continue;
}
int v = q_dequeue(q);
process(v);
}
Instead of sched_yield(), you could use usleep() or nanosleep() with a value of 1.
The child is of course adding elements to the queue, following the stipulations of the assignment.
for (;;) {
if (q_is_full(q)) {
sched_yield();
continue;
}
v = next_value_in_sequence_after_delay();
q_enqueue(v);
}
You may want to add an indication (such as enqueueing -1) that the child is done. The child can then exit, and the parent can know it is safe to reap the child.
The full check and empty check can logically be viewed as part of the enqueue and dequeue operations themselves. So, a possible implementation may be:
void q_enqueue(struct queue_type *q, int v) {
while (q_is_full(q)) sched_yield();
q->q[q->tail % Q_MAX_ELEM] = v;
q->tail += 1;
}
int q_dequeue(struct queue_type *q) {
while (q_is_empty(q)) sched_yield();
int v = q->q[q->head % Q_MAX_ELEM];
q->head += 1;
return v;
}
And then the parent and child functions might look like:
void parent(struct queue_type *q) {
for (;;) {
int v = q_dequeue(q);
if (v == -1) break;
printf("|%d", v);
fflush(stdout);
}
printf("|done!\n");
}
void child(struct queue_type *q, int n, int d) {
int v = 0;
for (;;) {
if (v == -1) break;
useconds_t t = rand() % 10000;
usleep(t * 1000);
v = next_value(n, d, v);
q_enqueue(q, v);
}
}
Below is the rest of the queue implementation I tested with, but you may want to research lock-free queues to see how you might implement single consumer / single producer queues without the need for traditional critical section protection.
#define Q_MAX_ELEM 1
struct queue_type {
volatile uint32_t head;
volatile uint32_t tail;
volatile int q[Q_MAX_ELEM];
};
void q_init(struct queue_type *q) {
static const struct queue_type q_zero;
*q = q_zero;
}
bool q_is_empty(struct queue_type *q) {
uint32_t tail = q->tail;
return q->head == tail;
}
bool q_is_full(struct queue_type *q) {
uint32_t head = q->head;
return (q->tail - head) == Q_MAX_ELEM;
}
Try it online!
The typical way to do this would be to create a pair of pipes shared between the child and parent, with the POSIX pipe() function. The Linux manual page even contains an example: https://www.man7.org/linux/man-pages/man2/pipe.2.html

C: why after fork() child block is not running?

I'm studying how fork() actually works so my code below has no purpose other than spawning new processes with fork() and see them die randomly. So:
I put my fork() in a for loop (to run twice for now) to see more than one child be created, and to my surprised it seems that the second Child has a parent that was not the same parent as the first child. So, if my initial PID was 1000, the two child created would be 1002 (child of 1000) and 1003 (child of 1001???). I didn't understand what happened that a parent was created. This guy explained but I can't say I fully understood.
To try and find out what was going on, I printed my parent (and child) processes with their PID, but if I declare a char for my parent, my child won't run the function. I explain what I mean in my code between <<< >>>.
So, can anyone help me understand my 1st point, and identify why 2 is happening?
Full code below:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
const int PASS = 5;
const int RANDLIMIT = 5;
const int FORKNUMBER = 2;
int i = 0;
void doSomeWork(char *who);
int main(int argc, char *argv[])
{
printf("Just started, I am: %d\n", (int) getpid());
int j;
pid_t pid;
for (j=0; j < FORKNUMBER; j++)
pid = fork();
printf("fork returned: %d\n", (int) pid);
srand((int) pid + rand());
if (pid < 0) {
perror("fork failed");
} else if (pid == 0) {
char * childPid;
char * childName;
sprintf(childPid, "%d", (int) getpid());
childName = (char *) malloc(strlen("Child - ") + strlen(childPid) + 1 );
strcpy(childName, "Child - ");
strcat(childName, childPid);
doSomeWork(childName);
exit(0);
}
//<<< The malloc above for the child to send a parameter >>>
//<<< to the function, works fine. But when I try to do >>>
//<<< the same for my parent, the simple declaration of a>>>
//<<< char below, makes the child block (the if PID==0) >>>
//<<< not run. The 3 lines commented below were an >>>
//<<< attempt to understand what was preventing the child>>>
//<<< block from running. Now, if the parent calls the >>>
//<<< function with a string not declared before, the >>>
//<<< child block runs fine.>>>
//char parentName[strlen("Parent") + 1];
//strcpy(parentName, "Parent");
//doSomeWork(parentName);
doSomeWork("Parent");
//wait(NULL);
return(0);
}
void doSomeWork(char *who)
{
int control = 0;
for(; i < PASS; i++){
sleep(rand() % RANDLIMIT);
printf("%s: Done pass #%d, my parent = %d\n", who, i, getppid());
if (control == 0)
{
char childWord[6];
strncpy(childWord, who, 5);
if (strcmp(childWord, "Child") == 0 && (int) getppid() == 1 )
{
control = 1;
printf("%s: became orphan at #%d\n", who, i);
}
}
}
printf("%s: exiting...\n", who);
}
EDIT:
For 1, I created a function like below:
int nbDigits(int number)
{
int i=0;
for(; number > 10; i++)
{
number /= 10;
}
return ++i;
}
Now instead of declaring a pointer to a char like this, char * childPid; I declared a char array like this char childPid[nbDigits(getpid()) + 1]; and everything worked like a charm.
Check out Joseph's suggestion below using asprintf(), seems neat.
You can't call fork() in a loop but only check what it returns at the end of the loop if you want your program to work. When you do so, you're starting an exponential number of child processes, and each one thinks it's "the parent" as long as it was the parent of its final fork(). Move your test of pid to inside the loop.
char * childPid;
sprintf(childPid, /* ... */);
That's going to clobber some random memory. You need to point childPid to something before you sprintf to it, or replace sprintf with something like asprintf that will allocate itself.
As soon as you run fork() any child process will start its execution from there.
Fork system call use for creates a new process, which is called child
process, which runs concurrently with process (which process called
system call fork) and this process is called parent process. After a
new child process created, both processes will execute the next
instruction following the fork() system call. source
Thus, when your first iteration happens (i=0) there will be a new process, with a new pid, and then, both parent and this child process will call the next iteration (i=1) and create a new child for each one with two more new pid. In the end, you will have 4 different pid.
Example
Parent process pid=1000
i = 0, creates pid=1001, now you have both 1000 and 1001
i = 1, creates a child from 1001 -> 1002 and a child from 1000 again, 1003.
In the end you have 1000, 1001, 1002 and 1003 and all of these four processes will run the following instruction which is the printf.

Interprocess Communication in C, one character at a time

First off, this IS homework, I am not asking for an answer, however I am confused about something.
I have a homework assignment for a programming class, and I am a little confused about how to write the code the specific way that the instructor is asking.
The program first creates a child process, and then proceeds to send command line arguments from the parent process, through a pipe, ONE CHARACTER at a time to the child process, and then read them into the child process ONE CHARACTER at a time, incrementing the character count in the child process each time a character is read in.
I think I accomplished sending the data through the pipe one character at a time, but I have no idea how to "go" to the child process every time a character is sent, read it, increment the number of characters, and then go back to the parent process and repeat.
Here is my code, It works and gives accurate answers, but any tips on how to accomplish what my instructor is asking would be appreciated, thank you!!
// Characters from command line arguments are sent to child process
// from parent process one at a time through pipe.
//
// Child process counts number of characters sent through pipe.
//
// Child process returns number of characters counted to parent process.
//
// Parent process prints number of characters counted by child process.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char **argv)
{
// set up pipe
int pA[2];
char buff[50];
pipe(pA);
// call fork()
pid_t childId = fork();
if (childId == 0) {
// -- running in child process --
int nChars = 0;
// close the output side of pipe
close(pA[1]);
// Receive characters from parent process via pipe
// one at a time, and count them.
nChars = read(pA[0], buff, sizeof(buff)); //this line of code is what i need to change to be reading characters in one at a time
// Return number of characters counted to parent process.
return nChars;
}
else {
// -- running in parent process --
int nChars = 0;
int size = 0;
printf("CS201 - Assignment 3 - Timothy Jensen\n");
// close the input side of the pipe
close(pA[0]);
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
for (int i = 1; i < argc; i++)
{
size = strlen(argv[i]);
for (int z = 0; z < size; z++)
{
write(pA[1], &argv[i][z], 1);
}
}
// Wait for child process to return. Reap child process.
// Receive number of characters counted via the value
// returned when the child process is reaped.
wait(&nChars);
printf("child counted %d chars\n", nChars/256);
return 0;
}
}
You need to make the following changes:
Make the last argument 1 in the call to read.
read(pA[0], buff, 1);
Put the above call in a while loop and increment nChar for every successful attempt at read.
while ( read(pA[0], buff, 1) == 1 )
{
++nChars;
}
Close the file descriptor from the parent process once you are done writing to it.
Here's a working version of main.
int main(int argc, char **argv)
{
// set up pipe
int pA[2];
char buff[50];
pipe(pA);
// call fork()
pid_t childId = fork();
if (childId == 0) {
// -- running in child process --
int nChars = 0;
// close the output side of pipe
close(pA[1]);
// Receive characters from parent process via pipe
// one at a time, and count them.
while ( read(pA[0], buff, 1) == 1 )
{
++nChars;
}
return nChars;
}
else {
// -- running in parent process --
int nChars = 0;
int size = 0;
printf("CS201 - Assignment 3 - Timothy Jensen\n");
// close the input side of the pipe
close(pA[0]);
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
for (int i = 1; i < argc; i++)
{
size = strlen(argv[i]);
for (int z = 0; z < size; z++)
{
write(pA[1], &argv[i][z], 1);
}
}
close(pA[1]);
// Wait for child process to return. Reap child process.
// Receive number of characters counted via the value
// returned when the child process is reaped.
wait(&nChars);
printf("child counted %d chars\n", nChars/256);
return 0;
}
}
It seems a little silly, but you could change:
nChars = read(pA[0], buff, sizeof(buff));
to:
char ch;
nChars = read(pA[0], &ch, 1);
Of course, you would put the above into a loop to assemble a string 'one character at a time' back into buff.

about creating a shared memory and provide the communication between parent/child process

hi there i'm trying to implement an example of using shared memory between child and parent process. The aim is child process have to calculate a fibonacci serie with a given size as an input from user and write every number as an element of an array. After that process will printout this array. The issue is if i create this memory segment and attach it before fork() operation it works fine, after i made fork() operation child process can reach that memory segment and generate the array properly and finally parent can printout the array after child finishes its job. the code is like this;
create memory segment
attach memory segment
initialize the array elements to zero
fork()
if(pid==0)// Child Process
call the child function and send the pointer of a structure which includes the array and array size
return to parent and printout the array properly
this is first example i implemented. However, i'm trying to use an another way as you can see the full code below;
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <string.h>
#define MAX_SEQUENCE 10
typedef struct {
long fib_sequence[MAX_SEQUENCE];
int sequence_size;
} shared_data;
void child_func(shared_data* dataPtr);
void main(int argc,char *argv[]){
int shmID,size,status,i;
shared_data* dataPtr;
pid_t pid;
if((pid=fork())<0){
printf("Error while fork()\n");
exit(0);
}
else if(pid>0){ // Parent Process
if((shmID = shmget(IPC_PRIVATE, MAX_SEQUENCE*sizeof(long), IPC_CREAT | 0666))<0){
printf("Allocation process was unsuccesfull\n");
exit(0);
}
dataPtr = (shared_data*) shmat(shmID,NULL,0);
for(i=0; i<MAX_SEQUENCE; i++)
dataPtr->fib_sequence[i]==0;
dataPtr->sequence_size = atoi(argv[1]);
if((dataPtr->sequence_size) < 0){
printf("You entered an invalid(negative) size number\n");
exit(0);
}
else if((dataPtr->sequence_size) > MAX_SEQUENCE){
printf("Please enter a value less than MAX_VALUE\n");
exit(0);
}
wait(status); // Wait untill child finishes its job
for(i=0; i<dataPtr->sequence_size; i++)
printf("%ld ", dataPtr->fib_sequence[i]);
printf("\n");
shmdt((void *) dataPtr);
shmctl(shmID,IPC_RMID,NULL);
}
else{ // Child Process
child_func(dataPtr);
exit(0);
}
}
void child_func(shared_data* dataPtr){
int index;
printf("I am in Child Process\n");
printf("Size of array %d\n", dataPtr->sequence_size);
dataPtr->fib_sequence[0];
if((dataPtr->sequence_size) > 0){
dataPtr->fib_sequence[1]=1;
for(index=2; index < dataPtr->sequence_size; index++)
dataPtr->fib_sequence[index] = dataPtr->fib_sequence[index-1] + dataPtr->fib_sequence[index-2];
}
}
when i run the second example in when it enters in the child process it prints meaningless value of dataPtr->fib_sequence. i am curious about a few questions which are;
In the second example why it prints wrong value of dataPtr->size in child
In the first example can we admit that we do create and attach memory segment in parent process cause we are doing this stuff before fork() operation

Shared Memory With Two Processes In C?

I want to do the following:
Parent process creates a child process. Then the child process reads n int's from the user and store them in a shared memory. The parent process then displays them.
I reached the following:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#define SHMSIZE 27
int main() {
int shmid;
int *shm;
int *n;
if(fork() == 0) {
shmid = shmget(2009, SHMSIZE, 0);
shm = shmat(shmid, 0, 0);
n = shm;
int i;
for(i=0; i<5; i++) {
printf("Enter number<%i>: ", i);
scanf("%d", n++);
}
printf ("Child wrote <%d>\n",shm);
shmdt(shm);
}
else {
wait();
int *s;
shmid = shmget(2009, SHMSIZE, 0666 | IPC_CREAT);
shm = shmat(shmid, 0, 0);
s = shm;
wait(NULL);
printf ("Parent reads <%d>\n",shm) ;
shmdt(shm);
shmctl(shmid, IPC_RMID, NULL);
}
return 0;
}
And the output is just this line:
Enter number<1>:
And if I entered a number, let's say 25, it outputs this:
Parent reads <r>
r: random -ve number changes every time I execute the code
It never went through the child process code ! Am I doing this in a wrong way ?
Ok, better collect in an answer instead...
There are several problems with you program. If you enable warnings when building (I use -Wall -Wextra) a lot of them will be quite evident.
The first two problems I already mentioned in my comments, but I explain them here:
The first is the call to wait(). There is no wait function in C or POSIX that takes no argument.
The second problem is the scanf call, you are calling it with *++, where *n takes the value of the memory pointed to by n which most likely can result in a crash. Remove the asterisk.
The third problem is that you treat the shared memory as both an array of integers (with n) and as a string. You cant really do both, pick one or the other.
You create the shared memory in the parent process, but wait for the child process to finish before you create the memory.
There is a race condition between the parent and child process, since the share memory might be created after the child tries to access it.
Edit I came up with this instead, which seems to work for me. I added comments on the things I changed.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <sys/wait.h> /* Needed for the wait function */
#include <unistd.h> /* needed for the fork function */
#include <string.h> /* needed for the strcat function */
#define SHMSIZE 27
int main() {
int shmid;
char *shm;
if(fork() == 0) {
shmid = shmget(2009, SHMSIZE, 0);
shm = shmat(shmid, 0, 0);
char *s = (char *) shm;
*s = '\0'; /* Set first location to string terminator, for later append */
int i;
for(i=0; i<5; i++) {
int n; /* Variable to get the number into */
printf("Enter number<%i>: ", i);
scanf("%d", &n);
char number[20];
sprintf(number, "%d", n); /* Convert the number to string */
strcat(s, number); /* Append the number to the string */
}
strcat(s, "\n"); /* Append newline */
printf ("Child wrote <%s>\n",shm);
shmdt(shm);
}
else {
/* Variable s removed, it wasn't used */
/* Removed first call to wait as it held up parent process */
shmid = shmget(2009, SHMSIZE, 0666 | IPC_CREAT);
shm = shmat(shmid, 0, 0);
wait(NULL);
printf ("Parent reads <%s>\n",shm) ;
shmdt(shm);
shmctl(shmid, IPC_RMID, NULL);
}
return 0;
}
Do note that point 5 in the list above have not been resolved.
My problem was so stupid. I need to provide the Child process with the ability to write into the SHM. This line in the if-block :
shmid = shmget(2009, SHMSIZE, 0);
Will become like this:
shmid = shmget(2009, SHMSIZE, 0666 | IPC_CREAT);
Thanks to you all and especially to #JoachimPileborg :)
Your description seems to not be correct since there is no code that outputs "Parent Wrote <>".
You are reading numbers and storing them as int in *n++, but then you are appending a '\n' character to the n-int array and you are treating shm as a string?
It seems to me that in your child you are creating a shared memory, writing to it and then closing (discarding) the shared memory. Then your second part opens a new shared memory with the same segment, but yet it is a new shared memory. Normally one process creates a shared memory, then the second opens it and when the last process closes the shared memory, then it is freed by the OS.
One problem is that the child process is attempting to use get the shared memory before it has been created by the parent. The parent has a wait() call before creating the shared memory, so it won't exist when the client tries to retrieve the id. Even if the wait() call is moved, it may not work because there is a race condition. The call to shmget may need to precede the fork call (or use some synchronization to make sure it actually exists before retrieving it in the child process).
And (as others have already pointed out), the child is attempting to write integers to the memory while the reading (printing of it) tries to treat it as a character string.

Resources