From adding more entries, it seems that sometimes killpg returns EPERM instead of ESRCH:
#!/usr/bin/python from signal import SIGTERM from sys import exit from time import sleep from os import * def slow(): fork() sleep(10) def fast(): sleep(1) child_pids = [] for child_func in [fast, slow, slow, fast]: pid = fork() if pid == 0: setsid() print child_func, getpid(), getuid(), geteuid() child_func() exit(0) else: child_pids.append(pid) print waitpid(-1, 0) for child_pid in child_pids: try: print child_pid, getpgid(child_pid) except OSError as e: print "Error getpgid %s: %s" %(child_pid, e) try: killpg(child_pid, SIGTERM) except OSError as e: print "Error killing %s: %s" %(child_pid, e)
Whenever killpg does not work with EPERM, getpgid has not previously been run with ESRCH. For example:
<function fast at 0x109950d70> 26561 503 503 <function slow at 0x109950a28> 26562 503 503 <function slow at 0x109950a28> 26563 503 503 <function fast at 0x109950d70> 26564 503 503 (26564, 0) 26561 Error getpgid 26561: [Errno 3] No such process Error killing 26561: [Errno 1] Operation not permitted 26562 26562 26563 26563 26564 Error getpgid 26564: [Errno 3] No such process Error killing 26564: [Errno 3] No such process
I have no idea why this is happening - be it legal behavior or a mistake in Darwin (inherited from FreeBSD or otherwise), etc.
It seems you could get around this this way by double checking EPERM by calling kill(child_pid, 0) ; if this returns an ESRCH, there is no real resolution problem. Of course, this looks pretty ugly in code:
for child_pid in child_pids: try: killpg(child_pid, SIGTERM) except OSError as e: if e.errno != 3: # 3 == no such process if e.errno == 1: try: kill(child_pid, 0) except OSError as e2: if e2.errno != 3: print "Error killing %s: %s" %(child_pid, e) else: print "Error killing %s: %s" %(child_pid, e)
abarnert
source share