Firstly, we call fork() where the parent process calls fork and the system call interface receives this call and forwards it to the kernel.
Then, the kernels provides a handler for fork, typically through an interrupt or system call instruction.
A Process Control Block(PCB) is created where the kernel allocates a new task_struct for the child process. The task_struct contains information about the process including the PID, state, scheduling information, and pointers to the memory and file descriptor tables.
After that the Virtual Address spaces is duplicated where the text segment is shared, while for all the other segments a copy is created for both the processes.
Thereafter the file descriptors are copied where the kernel increases the reference count for each file descriptor to reflect that multiple processes now reference these resources.
The kernel sets up the CPU registers and other execution context for the child process. The child process's return value from fork is set to 0 whereas for parent process, it is set to parent's PID.
After that the scheduler gets involved where the newly created child process is added to the scheduler's run queue and the scheduler decides when child process will get CPU time.
Then we return to the user space where the fork system call returns the control to the user space, and the parent process continues its execution and the child process begins the exexution as a copy of the parent.