Orphaned Processes in Python - Super Fast Python

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.

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.

Learn more
 


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:

...

# 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:

...

# 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:

# 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.

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:

...

# 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:

...

# 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.

Parent: None

Orphaned: True


Python Multiprocessing Jump-Start

Loving The Tutorials?

Why not take the next step? Get the book.

Learn more
 


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)

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.

# 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.

# 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.

...

# create a new process

process = Process(target=level1)

# start the new process

process.start()

The main process will then block for a moment.

...

# block for a moment

sleep(1)

Finally, it will terminate the level1 child process so that the level2 child process will be orphaned.

...

# 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.

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.

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.

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().

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.

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.

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

I would also recommend specific chapters in the books:

Guides

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