Many programs need to execute tasks simultaneously and Python provides us with a few different mechanisms for concurrent programming. One of those mechanisms is called forking, where a call is made to the underlying operating system to create a working copy of a program that’s already running. The program that created the new process is called the parent process, while the processes that are created by the parent are called the child process.
This post shows the most basic form of creating processes in Python and helps serve as a foundation to understanding forking. The example is derived from Programming Python: Powerful Object-Oriented Programming, and I added my own comments to help better explain the program.
import os # This is a function called by the child process def child(): # Use os.getpid() to get the pid for this process print('Hello from child', os.getpid()) # force the child process to exit right away # or the child process will return to the infinite loop in parent() os._exit(0) def parent(): while True: # Attempt to fork this program into a new process. When forking is complete # newpid will be non-zero for the original process, but it wil be # 0 in the child process newpid = os.fork() # The program now goes in two different directions at the same time # When newpid is 0, we call child() and the child process exits if newpid == 0: # Test if this is a child process child() else: # If are here, then we are still in the parent process # We print the pid of the parent and the child process (newpid) print('Hello from parent', os.getpid(), newpid) if input() == 'q': break if __name__ == '__main__': parent()
When run on my machine, the program shows the following output
Hello from parent 87800 87802 Hello from child 87802 k Hello from parent 87800 87803 Hello from child 87803 k Hello from parent 87800 87804 Hello from child 87804 k Hello from parent 87800 87805 Hello from child 87805 q
The hardest part to grasp about this program is that when os.fork() is called on line 19, the program actually launches a copy of itself. The operating system creates the new process and that new process gets a copy of all variables in memory and execution of the new and old programs continue after line 19. (Note: The OS may not exactly copy the parent process, but functionally speaking, the child process can be considered to be a copy of the parent).
The os.fork() function returns a number called a PID (process ID). We can test the pid to see if we are running in the parent or child process. When we are in the child process, the value returned by os.fork() is zero. So on line 23, we test for 0 and if newpid is zero, we call the child() function.
The alternative case is that we are still running in the parent process (bearing in mind, that the child process is also running at this point in time as well). If we are still in the parent process os.fork() returns a non-zero value. In that case, we use the else block to print the parent and child PID.
The parent process continues to loop until the user enters q to quit. Each time the loop iterates, a new child process is created by the parent. The parent prints its own PID (using os.getpid()) and the pid of the child on line 28.
The child process also uses os.getpid() to get its own PID. It prints its own PID on line 7 and then on line 11, we use os._exit(0) to force the child process to shut down. This is a critical step for this program! If we were to omit the call to os._exit on line 11, the child process would return to the parent function and enter the same infinite loop the parent is using.
This is the most basic example of creating child processes using Python. Keep in mind that processes do not share memory (unlike threads). In real world programs, processes often need to sycnchronize data from one process to another process using tools such as network sockets, databases, or files. When a child process is spawned it gets a copy of the memory of the parent process, but then functions as an independent program.
Lutz, Mark. Programming Python. Beijing, OReilly, 2013