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.
Related
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.
I wrote a C program for the Dining Philosophers Problem using pthread some time ago and am now trying to change it to use fork() instead. This is an exercive for a lecture I already passed. But a friend asked me for help and I can't seem to get it figured out by myself, which is driving me crazy!
If i do a "ps" the processes are there. But there isn't any output to stdout, so I think I'm doing something wrong with the pipes.
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define N 5
#define LEFT (i+4)%N
#define RIGHT (i+1)%N
#define THINKING 0
#define HUNGRY 1
#define EATING 2
sem_t spoon;
sem_t phil[N];
int state[N];
int phil_num[N]={0,1,2,3,4};
int fd[N][2]; // file descriptors for pipes
pid_t pid, pids[N]; // process ids
int i;
int num;
void philosopher(int i);
void test(int i);
void take_spoon(int i);
void put_spoon(int i);
char buffer[100];
int main(void)
{
for(i=0;i<N;++i)
{
pipe(fd[i]);
pids[i] = fork();
printf("i=%d\n",i);
printf("pids[i]=%d\n",pids[i]);
if(pids[i]==0)
{
// child
dup2(fd[i][1],1);
close(fd[i][0]);
close(fd[i][1]);
philosopher(i);
_exit(0);
}
else if(pids[i]>0)
{
// parent
dup2(fd[i][0],0);
close(fd[i][0]);
close(fd[i][1]);
}
}
// wait for child processes to end
for(i=0;i<N;++i) waitpid(pids[i],NULL,0);
return 0;
}
void philosopher(int i)
{
while(1)
{
sleep(1);
take_spoon(i);
sleep(2);
put_spoon(i);
sleep(1);
}
}
void take_spoon(int i)
{
sem_wait(&spoon);
state[i] = HUNGRY;
printf("philosopher %d is hungry\n",i+1);
test(i);
sem_post(&spoon);
sem_wait(&phil[i]);
}
void put_spoon(int i)
{
sem_wait(&spoon);
state[i] = THINKING;
printf("philosopher %d puts down spoon %d and %d hin\n",i+1,LEFT+1,i+1);
printf("philosopher %d thinks\n",i+1);
test(LEFT);
test(RIGHT);
sem_post(&spoon);
}
void test(int i)
{
if( state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING)
{
state[i] = EATING;
printf("philosopher %d takes spoon %d and %d\n",i+1,LEFT+1,i+1);
printf("philosopher %d eats\n",i+1);
sem_post(&phil[i]);
}
}
Thank you in advance for any help.
Several issues. First is that after fork(), the child process and parent process do not share memory. This is one of the primary differences between a thread and a process. Each process has its own virtual address space. Whatever you want the philosophers to share, you have to explicitly do that by creating shared memory. It seems you intended your global variables to be shared among all the processes. (Note that there are some things shared, such as open file descriptors, and the child does get a copy of the variables from the parent, initialized to the values that were assigned to them at the time of the fork() call.)
Second, you have some confusingly unnecessary variables. In particular, the pipes do not serve any real purpose. The stdout for each of the processes will go to the console screen already, without the need for trying to pipe them back to the parent. This is because the child process already inherits the open file descriptors of the parent, so the child will already be using the same stdout as the parent. In addition, the phil_num, and num variables were unused, and the i. pid and pids variables seemed to be needlessly made global.
Third, you failed to initialize your semaphores. The default initialization as a global variable probably leaves the semaphore "useable" but with a 0 initial value, meaning sem_wait() on it will just block. In your case, you need those semaphores in shared memory, so a call to sem_init() is mandatory anyway (to indicate it is going to be shared between multiple processes), and the call gives you a chance to properly initialize the semaphore with a value of 1 so that the initial sem_wait() call has a chance to return.
After adjusting the globals down to what really needs to be shared, they can be bundled together into a structure. Then, a global pointer can be created for the shared data.
struct shared_data {
sem_t spoon;
sem_t phil[N];
int state[N];
};
struct shared_data *shared;
void initialize_shared(); /* at program start */
void finalize_shared(); /* at program end */
One way to create shared memory is to use mmap(). After the memory is created, the data should be initialized properly. This includes a call to sem_init() on the semaphores. sem_destroy() is used to clean up a semaphore, and the mapped memory can be released with munmap(). These are done for you when the process exits, but provided for completeness. (You should always check the return values of all the operating system calls you make, but I have elided them for the sake of brevity.)
void initialize_shared()
{
int i;
int prot=(PROT_READ|PROT_WRITE);
int flags=(MAP_SHARED|MAP_ANONYMOUS);
shared=mmap(0,sizeof(*shared),prot,flags,-1,0);
memset(shared,'\0',sizeof(*shared));
sem_init(&shared->spoon,1,1);
for(i=0;i<N;++i) sem_init(&shared->phil[i],1,1);
}
void finalize_shared()
{
int i;
for(i=0;i<N;++i) sem_destroy(&shared->phil[i]);
munmap(shared, sizeof(*shared));
}
Your main() implementation does not really change, except you need to add local variables for the ones that were needlessly global, as well as call initialize_shared() and optionally finalize_shared(). Also, remove all the code related to pipe().
int main(void)
{
int i;
pid_t pid, pids[N]; // process ids
initialize_shared();
for(i=0;i<N;++i)
{
pid = fork();
if(pid==0)
{
// child
philosopher(i);
_exit(0);
}
else if(pid>0)
{
// parent
pids[i] = pid;
printf("pids[%d]=%d\n",i,pids[i]);
}
else
{
perror("fork");
_exit(0);
}
}
// wait for child processes to end
for(i=0;i<N;++i) waitpid(pids[i],NULL,0);
finalize_shared();
return 0;
}
Note that your program never really exits on its own, since philosopher() is implemented as an infinite loop.
I want to fork multiple processes and then use a semaphore on them. Here is what I tried:
sem_init(&sem, 1, 1); /* semaphore*, pshared, value */
.
.
.
if(pid != 0){ /* parent process */
wait(NULL); /* wait all child processes */
printf("\nParent: All children have exited.\n");
.
.
/* cleanup semaphores */
sem_destroy(&sem);
exit(0);
}
else{ /* child process */
sem_wait(&sem); /* P operation */
printf(" Child(%d) is in critical section.\n",i);
sleep(1);
*p += i%3; /* increment *p by 0, 1 or 2 based on i */
printf(" Child(%d) new value of *p=%d.\n",i,*p);
sem_post(&sem); /* V operation */
exit(0);
}
And the output is:
child(0) forked
child(1) forked
Child(0) is in critical section.
Child(1) is in critical section.
child(2) forked
Child(2) is in critical section.
child(3) forked
Child(3) is in critical section.
child(4) forked
Child(4) is in critical section.
Child(0) new value of *p=0.
Child(1) new value of *p=1.
Child(2) new value of *p=3.
Child(3) new value of *p=3.
Child(4) new value of *p=4.
Parent: All children have exited.
This clearly means the semaphore didn't work as it was supposed to. Can you explain how I should use semaphores on forked processes?
The problem you are facing is the misunderstanding of sem_init() function. When you read the manual page
you will see this:
The pshared argument indicates whether this semaphore is to be shared
between the threads of a process, or between processes.
If you are done reading up to this point, you will think that the non-zero value of pshared will make the semaphore inter-process semaphore. However, this is wrong.
You should continue reading and you'll understand that you have to locate the semaphore in a shared memory region. To do that, several functions can be used as
you can see below:
If pshared is nonzero, then the semaphore is shared between processes,
and should be located in a region of shared memory (see shm_open(3),
mmap(2), and shmget(2)). (Since a child created by fork(2) inherits
its parent's memory mappings, it can also access the semaphore.) Any
process that can access the shared memory region can operate on the
semaphore using sem_post(3), sem_wait(3), etc.
I find this approach as a more complicated approach than others, therefore I want to encourage people to use sem_open() instead of sem_init().
Below you can see a complete program illustrates the following:
How to allocate shared memory and use shared variables between forked
processes.
How to initialize a semaphore in a shared memory region and is used
by multiple processes.
How to fork multiple processes and make the parent wait until all of
its children exit.
#include <stdio.h> /* printf() */
#include <stdlib.h> /* exit(), malloc(), free() */
#include <sys/types.h> /* key_t, sem_t, pid_t */
#include <sys/shm.h> /* shmat(), IPC_RMID */
#include <errno.h> /* errno, ECHILD */
#include <semaphore.h> /* sem_open(), sem_destroy(), sem_wait().. */
#include <fcntl.h> /* O_CREAT, O_EXEC */
int main (int argc, char **argv){
int i; /* loop variables */
key_t shmkey; /* shared memory key */
int shmid; /* shared memory id */
sem_t *sem; /* synch semaphore *//*shared */
pid_t pid; /* fork pid */
int *p; /* shared variable *//*shared */
unsigned int n; /* fork count */
unsigned int value; /* semaphore value */
/* initialize a shared variable in shared memory */
shmkey = ftok ("/dev/null", 5); /* valid directory name and a number */
printf ("shmkey for p = %d\n", shmkey);
shmid = shmget (shmkey, sizeof (int), 0644 | IPC_CREAT);
if (shmid < 0){ /* shared memory error check */
perror ("shmget\n");
exit (1);
}
p = (int *) shmat (shmid, NULL, 0); /* attach p to shared memory */
*p = 0;
printf ("p=%d is allocated in shared memory.\n\n", *p);
/********************************************************/
printf ("How many children do you want to fork?\n");
printf ("Fork count: ");
scanf ("%u", &n);
printf ("What do you want the semaphore value to be?\n");
printf ("Semaphore value: ");
scanf ("%u", &value);
/* initialize semaphores for shared processes */
sem = sem_open ("pSem", O_CREAT | O_EXCL, 0644, value);
/* name of semaphore is "pSem", semaphore is reached using this name */
printf ("semaphores initialized.\n\n");
/* fork child processes */
for (i = 0; i < n; i++){
pid = fork ();
if (pid < 0) {
/* check for error */
sem_unlink ("pSem");
sem_close(sem);
/* unlink prevents the semaphore existing forever */
/* if a crash occurs during the execution */
printf ("Fork error.\n");
}
else if (pid == 0)
break; /* child processes */
}
/******************************************************/
/****************** PARENT PROCESS ****************/
/******************************************************/
if (pid != 0){
/* wait for all children to exit */
while (pid = waitpid (-1, NULL, 0)){
if (errno == ECHILD)
break;
}
printf ("\nParent: All children have exited.\n");
/* shared memory detach */
shmdt (p);
shmctl (shmid, IPC_RMID, 0);
/* cleanup semaphores */
sem_unlink ("pSem");
sem_close(sem);
/* unlink prevents the semaphore existing forever */
/* if a crash occurs during the execution */
exit (0);
}
/******************************************************/
/****************** CHILD PROCESS *****************/
/******************************************************/
else{
sem_wait (sem); /* P operation */
printf (" Child(%d) is in critical section.\n", i);
sleep (1);
*p += i % 3; /* increment *p by 0, 1 or 2 based on i */
printf (" Child(%d) new value of *p=%d.\n", i, *p);
sem_post (sem); /* V operation */
exit (0);
}
}
OUTPUT
./a.out
shmkey for p = 84214791
p=0 is allocated in shared memory.
How many children do you want to fork?
Fork count: 6
What do you want the semaphore value to be?
Semaphore value: 2
semaphores initialized.
Child(0) is in critical section.
Child(1) is in critical section.
Child(0) new value of *p=0.
Child(1) new value of *p=1.
Child(2) is in critical section.
Child(3) is in critical section.
Child(2) new value of *p=3.
Child(3) new value of *p=3.
Child(4) is in critical section.
Child(5) is in critical section.
Child(4) new value of *p=4.
Child(5) new value of *p=6.
Parent: All children have exited.
It is not bad to check shmkey since when ftok() fails, it returns -1. However if you have multiple shared variables and
if the ftok() function fails multiple times, the shared variables that have a shmkey with value -1 will reside in the same
region of the shared memory resulting in a change in one affecting the other. Therefore the program execution will get messy. To avoid this, it is better checked if the ftok()
returns -1 or not (better to check in source code rather than printing to screen like I did, although I wanted to show you the key values in case there is a collision).
Pay attention to how the semaphore is declared and initialized. It's different than what you have done in the question (sem_t sem vs sem_t* sem). Moreover, you should use them as they appear in this example. You cannot define sem_t* and use it in sem_init().
Linux minimal anonymous sem_init + mmap MAP_ANONYMOUS example
I like this setup as it does not pollute any global namespace as sem_open does.
The only downside is that MAP_ANONYMOUS is not POSIX, and I don't know any replacement: Anonymous shared memory? shm_open for example takes a global identifier just like sem_open.
main.c:
#define _GNU_SOURCE
#include <assert.h>
#include <semaphore.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char **argv) {
pid_t pid;
typedef struct {
sem_t sem;
int i;
} Semint;
Semint *semint;
size_t size = sizeof(Semint);
semint = (Semint *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
assert(semint != MAP_FAILED);
/* 1: shared across processes
* 0: initial value, wait locked until one post happens (making it > 0)
*/
sem_init(&semint->sem, 1, 0);
semint->i = 0;
pid = fork();
assert(pid != -1);
if (pid == 0) {
sleep(1);
semint->i = 1;
msync(&semint->sem, size, MS_SYNC);
sem_post(&semint->sem);
exit(EXIT_SUCCESS);
}
if (argc == 1) {
sem_wait(&semint->sem);
}
/* Was modified on the other process. */
assert(semint->i == 1);
wait(NULL);
sem_destroy(&semint->sem);
assert(munmap(semint, size) != -1);
return EXIT_SUCCESS;
}
Compile:
gcc -g -std=c99 -Wall -Wextra -o main main.c -lpthread
Run with sem_wait:
./main
Run without sem_wait:
./main 1
Without this synchronization, the assert is very likely to fail, since the child sleeps for one whole second:
main: main.c:39: main: Assertion `semint->i == 1' failed.
Tested on Ubuntu 18.04. GitHub upstream.
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
I have a program with a parent and a child process. Before the fork(), the parent process called malloc() and filled in an array with some data. After the fork(), the child needs that data. I know that I could use a pipe, but the following code appears to work:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main( int argc, char *argv[] ) {
char *array;
array = malloc( 20 );
strcpy( array, "Hello" );
switch( fork() ) {
case 0:
printf( "Child array: %s\n", array );
strcpy( array, "Goodbye" );
printf( "Child array: %s\n", array );
free( array );
break;
case -1:
printf( "Error with fork()\n" );
break;
default:
printf( "Parent array: %s\n", array );
sleep(1);
printf( "Parent array: %s\n", array );
free( array );
}
return 0;
}
The output is:
Parent array: Hello
Child array: Hello
Child array: Goodbye
Parent array: Hello
I know that data allocated on the stack is available in the child, but it appears that data allocated on the heap is also available to the child. And similarly, the child cannot modify the parent's data on the stack, the child cannot modify the parent's data on the heap. So I assume the child gets its own copy of both stack and heap data.
Is this always the case in Linux? If so, where the is the documentation that supports this? I checked the fork() man page, but it didn't specifically mention dynamically allocated memory on the heap.
Each page that is allocated for the process (be it a virtual memory page that has the stack on it or the heap) is copied for the forked process to be able to access it.
Actually, it is not copied right at the start, it is set to Copy-on-Write, meaning once one of the processes (parent or child) try to modify a page it is copied so that they will not harm one-another, and still have all the data from the point of fork() accessible to them.
For example, the code pages, those the actual executable was mapped to in memory, are usually read-only and thus are reused among all the forked processes - they will not be copied again, since no one writes there, only read, and so copy-on-write will never be needed.
More information is available here and here.
After a fork the child is completely independent from the parent, but may inherit certain things that are copies of the parent. In the case of the heap, the child will conceptually have a copy of the parents heap at the time of the fork. However, modifications to the head in the child's address space will only modify the child's copy (e.g. through copy-on-write).
As for the documentation: I've noticed that documentation will usually state that everything is copied, except for blah, blah blah.
The short answer is 'dirty on write' - the longer answer is .. a lot longer.
But for all intends and purposes - the working model which at C level is safe to assume is that just after the fork() the two processes are absolutely identical -- i.e. the child gets a 100% exact copy -- (but for a wee bit around the return value of fork()) - and then start to diverge as each side modifies its memory, stack and heaps.
So your conclusion is slightly off - child starts off with the same data as parent copied into its own space - then modifies it - and see s it as modified - while the parent continues with its own copy.
In reality things are bit more complex - as it tries to avoid a complete copy by doing something dirty; avoiding to copy until it has to.
Dw.
The example doesn't work because the parent is not updated by the child. Here's a solution:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
typedef struct
{
int id;
size_t size;
} shm_t;
shm_t *shm_new(size_t size)
{
shm_t *shm = calloc(1, sizeof *shm);
shm->size = size;
if ((shm->id = shmget(IPC_PRIVATE, size, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR)) < 0)
{
perror("shmget");
free(shm);
return NULL;
}
return shm;
}
void shm_write(shm_t *shm, void *data)
{
void *shm_data;
if ((shm_data = shmat(shm->id, NULL, 0)) == (void *) -1)
{
perror("write");
return;
}
memcpy(shm_data, data, shm->size);
shmdt(shm_data);
}
void shm_read(void *data, shm_t *shm)
{
void *shm_data;
if ((shm_data = shmat(shm->id, NULL, 0)) == (void *) -1)
{
perror("read");
return;
}
memcpy(data, shm_data, shm->size);
shmdt(shm_data);
}
void shm_del(shm_t *shm)
{
shmctl(shm->id, IPC_RMID, 0);
free(shm);
}
int main()
{
void *array = malloc(20);
strcpy((char *) array, "Hello");
printf("parent: %s\n", (char *) array);
shm_t *shm = shm_new(sizeof array);
int pid;
if ((pid = fork()) == 0)
{ /* child */
strcpy((char *) array, "Goodbye");
shm_write(shm, array);
printf("child: %s\n", (char *) array);
return 0;
}
/* Wait for child to return */
int status;
while (wait(&status) != pid);
/* */
shm_read(array, shm);
/* Parent is updated by child */
printf("parent: %s\n", (char *) array);
shm_del(shm);
free(array);
return 0;
}