Does a child process get a copy of its parent’s stack?
Today in operating systems class, the following question came up:
Does a Linux child process get a copy of its parent's stack?
I said yes, based on the rationale that child processes couldn't return from nested function calls without copies of their parent's stack.
I built the following experiment to find out what really happens. The results seem to indicate that I was right...
Code (forkexample.c):
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
void foo(void);
void bar(void);
void biz(void);
pid_t child_pid;
int main(int argc, char *argv[])
{
pid_t pid;
int child_exit_status;
foo();
printf("Both parent & child should get here - PID: %d\n", getpid());
if(getpid() == child_pid)
{
return 0; /* only the child exits here */
}
/* block until child returns */
pid = waitpid(child_pid, &child_exit_status, WNOHANG);
printf("Only parent should be here - PID: %d\n", getpid());
return 0;
}
void foo(void)
{
printf("Entering foo() as PID: %d\n", getpid());
bar();
printf("Exiting foo() as PID: %d\n", getpid());
}
void bar(void)
{
printf("Entering bar() as PID: %d\n", getpid());
biz();
printf("Exiting bar() as PID: %d\n", getpid());
}
void biz(void)
{
printf("Entering biz() as PID: %d\n", getpid());
if ((fork() == 0))
{
child_pid = getpid();
printf("I am the child. PID: %d\n", getpid());
}
else
{
printf("I am the parent. PID: %d\n", getpid());
}
printf("Exiting biz() as PID: %d\n", getpid());
}
Compile:
gcc -Wall forkexample.c -o forkexample
Run:
$ ./forkexample Entering foo() as PID: 12084 Entering bar() as PID: 12084 Entering biz() as PID: 12084 I am the child. PID: 12085 Exiting biz() as PID: 12085 Exiting bar() as PID: 12085 Exiting foo() as PID: 12085 Both parent & child should get here - PID: 12085 I am the parent. PID: 12084 Exiting biz() as PID: 12084 Exiting bar() as PID: 12084 Exiting foo() as PID: 12084 Both parent & child should get here - PID: 12084 Only parent should be here - PID: 12084
As you can see, the child process successfully rolls back the call stack - way past the function it was forked in. It seems to me that this would be impossible, if it didn't have a copy of its parent's stack.