Here's a detailed example of fork implementation :-
#include<unistd.h>
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
pid_t pid;
pid = fork();
if(pid<0)
{
fprintf(stderr,"Fork failed");
return 1;
}
else if(pid == 0)
{
printf("child process: My PID is %d\n",getpid());
}
else
{
printf("Parent process: My Child PID is %d\n",pid);
}
return 0;
}
Here, the parent process calls fork. The system call is trapped and handled by the kernel.
Then, the kernel's do_fork function is involved. do_fork calls copy_process(), which performs most of the work.
The copy_process() allocates a new task_struct for the child. The child's task struct is initialized with information from the parent, including memory mappings, file descriptors, and signal handlers.
The kernel sets up the child's memory descriptor(mm_struct). Memory areas(vm_area_struct) are copied, and page tables are set up for copy-on-write.
The child's CPU registers are set up, including setting the program counter to return to user space.
The child process is added to the scheduler's run queue. The scheduler determines when the child process gets the CPU time.
The kernel returns control to user space where the parent process returns the PID of the process whereas the child process returns 0.