Python:在杀死后多次处理打印到控制台

时间:2013-12-16 23:13:45

标签: python fork

我有以下python3程序,它创建了许多工作进程并在我按下ctrl-c时杀死它们。 在将SIGTERM发送到子进程之前,主服务器在控制台print('[W] aghhh ... %d' % self.pid)上打印一行。问题是对于给定的过程,该行输出不止一次。像这样(实际的控制台输出):

[W] aghhh ... 15773
[W] aghhh ... 15773
[W] aghhh ... 15774
[W] aghhh ... 15773
[W] aghhh ... 15774
[W] aghhh ... 15775
[W] aghhh ... 15776

问题:这怎么可能? 什么是杀死子进程的正确方法?

代码:

import os
import time
import signal
import sys


class ChildProcess:
  def __init__(self, m):
    self.pid = None
    self.ttl = 10
    self.master = m
    self.pipe_in = None
    self.pipe_out = None

  def hey(self):
    self.ttl -= 1
    self.pipe_out.write('Hello worker %d\n' % self.pid)
    self.pipe_out.flush()

  def tell_me(self):
    msg = self.pipe_in.readline()
    print('[M] Worker process %d says: %s' % (self.pid, msg), end='')

  def live(self):
    r1, w1 = os.pipe()
    r2, w2 = os.pipe()
    pid = os.fork()
    self.pid = pid
    if pid:
      print('[M] Created worker process %d' % pid)
      os.close(w1)
      os.close(r2)
      self.pipe_in = os.fdopen(r1, 'rt')
      self.pipe_out = os.fdopen(w2, 'wt')
      self.master.add(self)
    else:
      print('[W] Worker process ready to rock')
      os.close(r1)
      os.close(w2)
      wr = os.fdopen(w1, 'wt')
      reader = os.fdopen(r2)
      while True:
        wr.write('Hello Master\n')
        wr.flush()
        msg = reader.readline()
        print('[W] Master says %s' % msg, end='')

  def die(self):
    print('[W] aghhh ... %d' % self.pid)
    os.kill(self.pid, signal.SIGTERM)


class Master:
  def __init__(self):
    self.workers = []

  def add(self, worker):
    self.workers.append(worker)

  def rulez(self, nbr=2):
    for i in range(nbr):
      worker = ChildProcess(self)
      worker.live()
    while True:
      for w in self.workers:
        w.tell_me()
        time.sleep(1)
        w.hey()

  def reap(self):
    for w in self.workers:
      w.die()


if __name__ == '__main__':
  master = Master()

  try:
    master.rulez(3)
  except KeyboardInterrupt:
    master.reap()

1 个答案:

答案 0 :(得分:2)

这是因为子进程正在执行与父进程相同的代码。所以对于子进程,当他们执行while True: wr.write('Hello Master\n') …时,如果他们在被父进程杀死之前收到SIG_INT,他们会将KeyboardInterrupt提升到调用方法,即master.rulez(3) }。

所以,是的,实际上KeyboardInterrupt中最多会有4 master.rulez(3)次提升。您可以通过在except KeyboardInterrupt打印内容来确认,或者更好,打印len(self.workers)。这会产生这样的东西:

...
[W] Master says Hello worker 769
^C3
2
1
[W] aghhh ... 769
0
[W] aghhh ... 769
[W] aghhh ... 769
[W] aghhh ... 770
[W] aghhh ... 770
[W] aghhh ... 771

请注意,当主人可能分叉其他孩子时,每个孩子都会分叉,因此“self.workers”中还有其他一些孩子。因此,对于第一个孩子,这将是空的,第二个孩子将是1,第三个孩子将是2。

您的代码可视化(针对两名员工):

Master
|
|
ChildProcess1 (init)
|
+---------------------+ (fork)
|                     |
self.workers.add(1)   While True: ...
|                     |
|                     (KeyboardInterrupt)
|                     master.reap()
ChildProcess2 (init)  (exit)
|
+---------------------+ (fork) <---- This copied self.workers also, which 
|                     |              already contains ChildProcess1
self.workers.add(2)   While True: ... 
while True: ...       |
(KeyboardInterrupt)   (KeyboardInterrupt)
master.reap()         master.reap()
ChildProcess1.die()   ChildProcess1.die()
ChildProcess2.die()   (exit)
(exit)

为了防止孩子继续执行master.rulez(3),您可以在子进程中捕获KeyboardInterrupt,然后在那里引发sys.exit()(或者它可以使用{{1另外)

代码:

os.kill()

结果:

...
[W] Master says Hello worker 779
^C3
[W] aghhh ... 779
[W] aghhh ... 780
[W] aghhh ... 781