Wednesday 2 October 2013

[Operating System #1] Handling Child Process

We know that a new process can be created using fork() system call. This new process we call it as a Child Process.

fork():
---------
fork() prototype declaration:
            #include<unistd.h>
            pid_t fork(void);
Return: It returns two results. One result is Process ID (PID) of new process (child process), which is returned to parent process.  Second result is 0 (if child process is created successfully.), which is returned to child process.

We will identify child and parent process using this return value only.

Sample code:
----------------
                pid_t child_pid;
                child_pid = fork();
                if (child_pid == 0)
                {  /* child process */
                } else {
                    /* parent process */
                }

The child will have its own PCB (Process Control Block), so this process is assigned with a PID. We use getpid() call to get the PID of the process. Similarly, we use getppid() call to get the PID of its parent process.
getpid() & getppid():
--------------------------
These calls returns PID of the process of integer datatype.
This child process will share same address space with its parent i.e. it shares stack segment, data segment and code segment and it has its own PCB.

------------------------------------------------------------------------------
Example-1
========

  1. #include<unistd.h>
  2. #include<stdio.h>

  3. #define CHILD 0

  4. int main()
  5. {
  6. pid_t child_pid;

  7. printf("Before fork: pid: %d\n", getpid());
  8. child_pid = fork();
  9. printf("Hello Fork: pid: %d, parent_pid: %d, childpid: %d\n", getpid(), getppid(), child_pid);
  10. return 0;
  11. }

output:
======
Before fork: pid: 2164
Hello Fork: pid: 2164, parent_pid: 1924 (bash), childpid: 2165
Hello Fork: pid: 2165, parent_pid: 1, childpid: 0

Observation:
==========
The code after fork() function will be executed twice. One time for parent process and 2nd time for child process. Since, child process shares same address space it includes code segment also.

Parent process (2164) is executed before child and got terminated, its parent process is 1924 (terminal). The fork() return value is 2165 (child process PID).

After parent, child is executed and its has PID 2165. Since the parent process is terminated, the child process is taken care by root process "init"(1).

On success, fork() returns
1. PID of the child process in the parent process
2. 0 is returned in the child process
-----------------------------------------------------------------------------------------

There are few cases, we need to look at them very carefully.
Child Process terminated first
Parent process terminates first

The above example is for 2nd case, where parent process is terminated first.
The below example is for 1st case: Child process is terminated first.
-----------------------------------------------------------------------------------------
Example-2 
========

  1. #include<unistd.h>
  2. #include<stdio.h>

  3. #define CHILD 0

  4. int main()
  5. {
  6. pid_t child_pid;

  7. child_pid = fork();
  8. if (child_pid == CHILD) {
  9. /* child process */
  10. printf("child: my pid: %d..parent_pid: %d\n", getpid(), getppid());
  11. } else {
  12. /* parent process */
  13. printf("parent: my pid: %d\n", getpid());
  14. while(1);
  15. }
  16. return 0;
  17. }


Output:
=======
parent: my pid: 2244
child: my pid: 2245..parent_pid: 2244

Observation: ps -Af
===============
rrajk     2244  1924 86 09:47 pts/2    00:00:39 ./fork2
rrajk      2245  2244  0 09:47 pts/2    00:00:00 [fork2] <defunct>

Child process is done with its code execution. There is no code left to continue.
And, parent is still alive, child process is put into "defunc" state, until immediate parent process instructs kernel to destroy terminated process.
This child process is called as "Zombie Process".
------------------------------------------------------------------------------------------

We know that child process shares same address space with its parent process. If one process modifies the shared data then a new address space is allocated for that process. This concept is called Copy-On-Write approach.

The below example #3 will show, how copy-on-write approach works.
-------------------------------------------------------------------------------------------
Example-3
========

  1. #include<unistd.h>
  2. #include<stdio.h>
  3. #include<stdlib.h>

  4. #define CHILD 0

  5. int main()
  6. {
  7. pid_t child_pid;
  8. int k =10;

  9. child_pid = fork();
  10. if (child_pid == CHILD) {
  11. /* child process */
  12. printf("child: %d, parent: %d\n", getpid(), getppid());
  13. k = 44;
  14. printf("child: k value: %d\n", k);
  15.         } else {
  16. /* parent process */
  17. printf("parent: %d\n", getpid());
  18. wait(NULL);
  19. printf("parent: k value: %d\n", k);
  20. while(1);
  21. }
  22. return 0;
  23. }


output:
======
parent: 2124
child: 2125, parent: 2124
child: k value: 44
parent: k value: 10

observation:
===========
wait(): Suspends caller until child terminates and instructs the process manager to destroy child PCB that is in defunc (exit) state.
And, the value of k is not affected due to change in child process. Since either of the process modifies the shared values then copy-on-write procedure will takes place and new address space is created for that process.

If we observe, there is no child process with defunc status using "ps -Af" command.
rrajk      2134  2039 77 10:45 pts/0    00:00:09 ./fork3_wait
-------------------------------------------------------------------------------------------

Next: Synchronization of child and parent process execution
====

No comments:

Post a Comment

You might also like

Related Posts Plugin for WordPress, Blogger...