In POSIX-conformant operating systems, a process group denotes a collection of one or more processes. Process groups are used to control the distribution of signals. A signal directed to a process group is delivered individually to all of the processes that are members of the group.
Process groups are themselves grouped into sessions. Process groups are not permitted to migrate from one session to another, and a process may only create new process groups belonging to the same session as it itself belongs to. Processes are not permitted to join process groups that are not in the same session as they themselves are. [1]
Under certain circumstances one might want to spawn a process from a Python script that is independent of the original process such that the spawned or child process is independent of the parent process and cannot be terminated when te parent process, its process group or session is terminated.
Before we start with our Python action let's have a short look on our shell tools.
Using ps one can get all the information out processes. In this particular example we are interested in the various afore mentionen IDs [2]:
ps xao comm,pid,ppid,pgid,sid
To kill a process one commonly uses the kill command [3]:
kill -<signal> <pid>
where signal is e.g. 9 or 15 and pid is the process ID of th process to kill. To kill a process group or session one uses the same command, but negates the ID of the process group or session:
kill -<signal> -<id>
where <id> is the process group ID oder session ID.
This is achieved rather easily by using Python's os.setsid() method [4].
#!/usr/bin/env python
# -*- coding: utf8 -*-
import multiprocessing as mp
import time
import os
def printPIDs():
"""
Function printing process ID, process group ID and session ID.
"""
print "Child PID:", os.getpid(), os.getpgrp(), os.getsid(os.getpid())
class myChild(mp.Process):
"""
Derived Process class to demonstrate spawning of new session group.
"""
def __init__(self):
"""
Initialization of object and IDs of parent process.
"""
super(myChild,self).__init__()
printPIDs()
def run(self):
"""
The actual method executed in the new process.
The child process is made session leader by os.setsid() and thus
independent of the parent.
"""
os.setsid()
printPIDs()
time.sleep(600)
# Print the IDs of the parent process
printPIDs()
p = myChild()
p.start()
p.join()
This example code starts a new Python process by calling the start method of the Process class. This new process is a member of the process group of the parent process and its session. The first statement in the new process is setsid [4], which makes the new process to a session leader by changing its session ID and proces group ID.
Now we can start the script and check the output in your console, e.g.:
$ ./script/process-group.py
Child PID: 13130 13130 5979
Child PID: 13130 13130 5979
Child PID: 13131 13131 13131
In a new console we verify the output by calling ps:
$ ps xao comm,pid,ppid,pgid,sid | grep python
python 13130 5979 13130 5979
python 13131 13130 13131 13131
As we can see in the thir column the child process is now a session leader, but still has a parent: the Python parent process. And now we kill the session leader of our Python parent process and use ps to look whether the child process is still running:
$ kill -9 -5979
$ ps xao comm,pid,ppid,pgid,sid | grep python
python 13131 2335 13131 13131
After killing the parent process by killing the session the "parent process ID" of our child process has changed. In my example the new parent process ID is 2335, which is the 'init --user' process. And — good news! — our child process is still running.