Last Updated on September 12, 2022
An orphan process is a process that does not have a running parent process.
In this tutorial you will discover orphan processes and how to identify them in Python.
Let’s get started.
What is an Orphan Process
An orphan process is a process that does not have a parent process.
An orphan process is a computer process whose parent process has finished or terminated, though it remains running itself.
— Orphan process, Wikipedia.
A process is a running instance of a computer program.
Every Python program is executed in a Process, which is a new instance of the Python interpreter. This process has the name MainProcess and has one thread used to execute the program instructions called the MainThread. Both processes and threads are created and managed by the underlying operating system.
Sometimes we may need to create new child processes in our program in order to execute code concurrently.
Python provides the ability to create and manage new processes via the multiprocessing.Process class.
It is possible to create child processes and for the parent process to stop or be killed, leaving one or more child processes to continue running.
These child processes are called orphan processes.
Next, let’s consider how a child process may be orphaned.
Run loops using all CPUs, download your FREE book to learn how.
How To Orphan a Child Process
An orphan process is created when the parent of a child process is terminated.
This can happen for many reasons, both intentional and otherwise.
Some ways that a parent process may be terminated leaving the child process orphaned include:
- Parent process finishes.
- Parent raises an uncaught error or exception.
- Parent process calls sys.exit() or os._exit().
- The Process.terminate() or Process.kill() methods are called on the parent.
- A process calls os.kill() with the parent’s PID.
- Process is killed externally, e.g. via the kill command.
Now that we know what orphan processes are, let’s look at problems with orphan processes.
Problem of Orphan Processes
Orphan processes can be a problem in some situations.
Typically, we can get a handle on child processes via the parent process. This is helpful for querying the status of the process or forcefully killing child processes.
If a process is orphaned, it can be difficult to programmatically get a handle on it.
In turn, it can be challenging to query the status of orphaned processes or to terminate them.
Some solutions could be:
- Other processes keep a reference to processes that could be orphaned, e.g. Process instances.
- Store process identifiers (PIDs) for child processes that could be orphaned, e.g. in a file.
- Child processes can check if they are orphaned and take action accordingly.
Next, let’s take a look at how a process can check if it is orphaned.
Free Python Multiprocessing Course
Download your FREE multiprocessing PDF cheat sheet and get BONUS access to my free 7-day crash course on the multiprocessing API.
Discover how to use the Python multiprocessing module including how to create and start child processes and how to use a mutex locks and semaphores.
How To Check if a Process is Orphaned
A process is orphaned if it has no parent process, or if the parent process is not running.
By this definition, the MainProcess in a Python program is an orphan.
A process can check if it is orphaned by calling the multiprocessing.parent_process() function.
This function returns the multiprocessing.Process instance for the parent process for the current process.
If this value is None, the process is orphaned.
For example:
1 2 3 4 |
... # check for no parent process if multiprocessing.parent_process() is None: print('Orphaned') |
Alternatively, a child process may have a multiprocessing.Process instance returned from the multiprocessing.parent_process() function, but the process may not be running. In which case, the process is orphaned.
We can check if the parent process is running via a call to the Process.is_alive() function.
For example:
1 2 3 4 5 |
... # get the parent process parent = multiprocessing.parent_process() if parent is not None and not parent.is_alive(): print('Orphaned') |
We can wrap this into a function that returns True if a process is orphaned and False otherwise.
For example:
1 2 3 4 5 6 7 8 9 |
# return True if the current process is orphaned, False otherwise def is_orphan(): # get the parent process parent = parent_process() # check if orphaned if parent is None or not parent.is_alive(): return True # not orphaned return False |
Now that we know how to identify an orphaned process, let’s look at some examples of orphaned processes.
Overwhelmed by the python concurrency APIs?
Find relief, download my FREE Python Concurrency Mind Maps
Example MainProcess is an Orphan
Technically, the main process for a Python program is an orphan process.
This is because the main process does not have a parent process.
We can demonstrate this with an example. We can call the multiprocessing.parent_process() function and confirm that it returns None.
For example:
1 2 3 4 |
... # get the parent process parent = parent_process() print(f'Parent: {parent}') |
We can also test our is_orphan() function developed in the previous section.
For example:
1 2 3 |
... # check if orphan print(f'Orphaned: {is_orphan()}') |
Tying this together, the complete example of checking if the main process is an orphan process is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# SuperFastPython.com # example confirming that the main process is an orphan process from multiprocessing import parent_process # return True if the current process is orphaned, False otherwise def is_orphan(): # get the parent process parent = parent_process() # check if orphaned if parent is None or not parent.is_alive(): return True # not orphaned return False # entry point if __name__ == '__main__': # get the parent process parent = parent_process() print(f'Parent: {parent}') # check if orphan print(f'Orphaned: {is_orphan()}') |
Running the example first gets the parent process of the main process and prints it directly.
In this case, we can confirm that the parent of the main process is None. This highlights that technically the main process is an orphan process.
Next, we can test our is_orphan() function for the main process.
This returns True, confirming that our function operates as expected.
1 2 |
Parent: None Orphaned: True |
Example of Orphaned Child Process
We can explore how to create an orphaned child process.
In this example we will create a child process, then from this child process create another child process.
- MainProcess (level0)
- Child Process (level1)
- Child Process (level2)
- Child Process (level1)
The main process will then terminate the first child process and leave the second child process as an orphan process.
First, we must define a function to be executed by the second level child process. This function will first check if it is an orphan (it won’t be), then sleep for a moment, then check if it is an orphan again (it will be).
We can call our is_orphan() function developed above to check if the process is an orphan.
The level2() function below implements this.
1 2 3 4 5 6 7 8 |
# task for level 3 child def level2(): # check if orphaned print(f'Orphaned: {is_orphan()}', flush=True) # block for a moment sleep(3) # check if orphaned print(f'Orphaned: {is_orphan()}', flush=True) |
Next, we need a function to be executed by the first-level child process.
This is the process created directly by the MainProcess that will in turn create the second level process that executes level2() listed above.
The level1() function below implements this.
1 2 3 4 5 6 |
# task for level 1 child def level1(): # create a new process process = Process(target=level2) # start the new process process.start() |
Finally, the main process will create a new child process that executes the level1() function, then start this process.
1 2 3 4 5 |
... # create a new process process = Process(target=level1) # start the new process process.start() |
The main process will then block for a moment.
1 2 3 |
... # block for a moment sleep(1) |
Finally, it will terminate the level1 child process so that the level2 child process will be orphaned.
1 2 3 |
... # terminate the child process process.terminate() |
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# SuperFastPython.com # example of creating an orphan process from time import sleep from multiprocessing import Process from multiprocessing import parent_process # return True if the current process is orphaned, False otherwise def is_orphan(): # get the parent process parent = parent_process() # check if orphaned if parent is None or not parent.is_alive(): return True # not orphaned return False # task for level 3 child def level2(): # check if orphaned print(f'Orphaned: {is_orphan()}', flush=True) # block for a moment sleep(3) # check if orphaned print(f'Orphaned: {is_orphan()}', flush=True) # task for level 1 child def level1(): # create a new process process = Process(target=level2) # start the new process process.start() # entry point if __name__ == '__main__': # create a new process process = Process(target=level1) # start the new process process.start() # block for a moment sleep(1) # terminate the child process process.terminate() |
Running the example first creates a child process in the main process.
The main process then blocks.
The first-level child process then creates a second-level child process and finishes running.
The second-level child process checks if it is orphaned, which it is not, then blocks.
The main process then terminates the first-level child process and finishes running.
The first-level child process had already finished running, but was still alive. This is because it had at least one non-daemon child process still running. Once terminated, it was no longer alive.
At this time the main process is no longer “running” but is still alive because there is still at least one non-daemon process running.
The second-level process wakes-up, checks if it is orphaned, which it is, then the program terminates as all non-daemon processes have finished.
1 2 |
Orphaned: False Orphaned: True |
Common Questions
This section lists some common questions about orphan processes.
Do you have any questions about orphan processes?
List your questions below in the comments and I may add them to this section.
Will Calling sys.exit() Create an Orphan Process?
No.
The sys.exit() function will raise a SystemExit exception and signal to the interpreter that the process should exit.
This may stop the process from “running” but it will stay alive if it has child processes.
For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# SuperFastPython.com # example of creating an orphan process from time import sleep from multiprocessing import Process from multiprocessing import parent_process import sys # return True if the current process is orphaned, False otherwise def is_orphan(): # get the parent process parent = parent_process() # check if orphaned if parent is None or not parent.is_alive(): return True # not orphaned return False # task for level 3 child def level2(): # check if orphaned print(f'Orphaned: {is_orphan()}', flush=True) # block for a moment sleep(3) # check if orphaned print(f'Orphaned: {is_orphan()}', flush=True) # task for level 1 child def level1(): # create a new process process = Process(target=level2) # start the new process process.start() # attempt to terminate sys.exit(0) # entry point if __name__ == '__main__': # create a new process process = Process(target=level1) # start the new process process.start() # block for a moment sleep(1) # check if alive print(f'Child Alive: {process.is_alive()}') |
Running the example shows that the first-level child process stops running and attempts to exit, yet remains running according to both the parent and the second-level child process.
1 2 3 |
Orphaned: False Child Alive: True Orphaned: False |
Can We Terminate the MainProcess To Create an Orphan?
Yes, but not easily.
We can call sys.exit(0) to exit the main process, yet it stays alive.
For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# SuperFastPython.com # example of creating an orphan process from time import sleep from multiprocessing import Process from multiprocessing import parent_process import sys # return True if the current process is orphaned, False otherwise def is_orphan(): # get the parent process parent = parent_process() # check if orphaned if parent is None or not parent.is_alive(): return True # not orphaned return False # function executed in a new process def task(): # check if orphaned print(f'Orphaned: {is_orphan()}', flush=True) # block for a moment sleep(3) # check if orphaned print(f'Orphaned: {is_orphan()}', flush=True) print(f'Parent Alive: {parent_process().is_alive()}', flush=True) # entry point if __name__ == '__main__': # create a new process process = Process(target=task) # start the new process process.start() # block for a moment sleep(1) # all done print('MainProcess all done') # process terminates itself sys.exit(0) |
Running the example shows that the child is not an orphan and that the parent process is still running.
1 2 3 4 |
Orphaned: False MainProcess all done Orphaned: False Parent Alive: True |
We can attempt to get the multiprocessing.Process instance for the main process via multiprocessing.current_process() function and have the main process terminate itself, but Python reports no such function or attribute.
For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# SuperFastPython.com # example of creating an orphan process from time import sleep from multiprocessing import Process from multiprocessing import parent_process from multiprocessing import current_process # return True if the current process is orphaned, False otherwise def is_orphan(): # get the parent process parent = parent_process() # check if orphaned if parent is None or not parent.is_alive(): return True # not orphaned return False # function executed in a new process def task(): # check if orphaned print(f'Orphaned: {is_orphan()}', flush=True) # block for a moment sleep(3) # check if orphaned print(f'Orphaned: {is_orphan()}', flush=True) # entry point if __name__ == '__main__': # create a new process process = Process(target=task) # start the new process process.start() # block for a moment sleep(1) # all done print('MainProcess all done') # process terminates itself p = current_process() p.terminate() |
Running the example reports that there is no such method as terminate() on the multiprocessing.Process class.
Although, of course there is, e.g. multiprocessing.Process.terminate().
1 2 3 4 5 6 |
Orphaned: False MainProcess all done Traceback (most recent call last): ... AttributeError: 'NoneType' object has no attribute 'terminate' Orphaned: False |
We get the same result if the child tries to terminate the parent process itself.
For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# SuperFastPython.com # example of creating an orphan process from time import sleep from multiprocessing import Process from multiprocessing import parent_process from multiprocessing import current_process # return True if the current process is orphaned, False otherwise def is_orphan(): # get the parent process parent = parent_process() # check if orphaned if parent is None or not parent.is_alive(): return True # not orphaned return False # function executed in a new process def task(): # check if orphaned print(f'Orphaned: {is_orphan()}', flush=True) # block for a moment sleep(3) # get the parent process and terminate it p = parent_process() p.terminate() # check if orphaned print(f'Orphaned: {is_orphan()}', flush=True) # entry point if __name__ == '__main__': # create a new process process = Process(target=task) # start the new process process.start() # block for a moment sleep(1) # all done print('MainProcess all done') |
Running the example again reports that there is no such function as terminate() on the multiprocessing.Process class, although we know there is.
1 2 3 4 5 6 |
Orphaned: False MainProcess all done Process Process-1: Traceback (most recent call last): ... AttributeError: 'NoneType' object has no attribute 'terminate' |
Nevertheless, the main process can terminate itself via the os._exit() function which will create an orphan child process.
For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# SuperFastPython.com # example of creating an orphan process from time import sleep from multiprocessing import Process from multiprocessing import parent_process import os # return True if the current process is orphaned, False otherwise def is_orphan(): # get the parent process parent = parent_process() # check if orphaned if parent is None or not parent.is_alive(): return True # not orphaned return False # function executed in a new process def task(): # check if orphaned print(f'Orphaned: {is_orphan()}', flush=True) # block for a moment sleep(3) # check if orphaned print(f'Orphaned: {is_orphan()}', flush=True) print(f'Parent Alive: {parent_process().is_alive()}', flush=True) # entry point if __name__ == '__main__': # create a new process process = Process(target=task) # start the new process process.start() # block for a moment sleep(1) # all done print('MainProcess all done') # process terminates itself os._exit(0) |
Running the example shows that the parent process is no longer running and the child was correctly orphaned.
1 2 3 4 |
Orphaned: False MainProcess all done Orphaned: True Parent Alive: False |
Further Reading
This section provides additional resources that you may find helpful.
Python Multiprocessing Books
- Python Multiprocessing Jump-Start, Jason Brownlee (my book!)
- Multiprocessing API Interview Questions
- Multiprocessing API Cheat Sheet
I would also recommend specific chapters in the books:
- Effective Python, Brett Slatkin, 2019.
- See: Chapter 7: Concurrency and Parallelism
- High Performance Python, Ian Ozsvald and Micha Gorelick, 2020.
- See: Chapter 9: The multiprocessing Module
- Python in a Nutshell, Alex Martelli, et al., 2017.
- See: Chapter: 14: Threads and Processes
Guides
- Python Multiprocessing: The Complete Guide
- Python Multiprocessing Pool: The Complete Guide
- Python ProcessPoolExecutor: The Complete Guide
APIs
References
Takeaways
You now know how to identify an orphaned process in Python.
Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.
Photo by Robert Gunnarsson on Unsplash
Do you have any questions?