5

フォーラムに質問を投稿することはめったにありませんが、これは私を困惑させます。私はこれを引き起こしているものについて非常に興味があります(解決策もいいでしょうが、ほとんどの場合、私はこの問題を抱えている理由を知りたいです):

私は最近、PBSジョブによって開始されるリモートコマンドの呼び出しをラップするためのPythonスクリプトを作成しました。

#! /usr/bin/env python
#
# Copyright (c) 2009 Maciej Brodowicz
# Copyright (c) 2011 Bryce Lelbach
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

from datetime import datetime

from string import letters, digits

from types import StringType

from optparse import OptionParser

from threading import Thread

# subprocess instantiation wrapper. Unfortunately older Python still lurks on
# some machines.
try:
  from subprocess import Popen, STDOUT, PIPE
  from types import StringType

  class process:
    _proc = None
    _exec = None

    def __init__(self, cmd):
      self._proc = Popen(cmd, stderr = STDOUT, stdout = PIPE,
        shell = (False, True)[type(cmd) == StringType])

    def poll(self):
      return self._proc.poll()

    def pid(self):
      return self._proc.pid

    def _call(self):
      # annoyingly, KeyboardInterrupts are transported to threads, while most
      # other Exceptions aren't in python
      try:
        self._proc.wait()
      except Exception, err:
        self._exec = err

    def wait(self, timeout=None):
      if timeout is not None:
        thread = Thread(target=self._call)
        thread.start()

        # wait for the thread and invoked process to finish
        thread.join(timeout)

        # be forceful
        if thread.is_alive():
          self._proc.terminate()
          thread.join()

          # if an exception happened, re-raise it here in the master thread 
          if self._exec is not None:
            raise self._exec

          return (True, self._proc.returncode)

        if self._exec is not None:
          raise self._exec

        return (False, self._proc.returncode)

      else:
        return (False, self._proc.wait())

    def read(self):
      return self._proc.stdout.read()

except ImportError, err:
  # no "subprocess"; use older popen module
  from popen2 import Popen4
  from signal import SIGKILL
  from os import kill, waitpid, WNOHANG

  class process:
    _proc = None

    def __init__(self, cmd):
      self._proc = Popen4(cmd)

    def poll(self):
      return self._proc.poll()

    def pid(self):
      return self._proc.pid

    def _call(self):
      # annoyingly, KeyboardInterrupts are transported to threads, while most
      # other Exceptions aren't in python
      try:
        self._proc.wait()
      except Exception, err:
        self._exec = err

    def wait(self, timeout=None):
      if timeout is not None:
        thread = Thread(target=self._call)
        thread.start()

        # wait for the thread and invoked process to finish
        thread.join(timeout)

        # be forceful
        if thread.is_alive():
          kill(self._proc.pid, SIGKILL)
          waitpid(-1, WNOHANG)
          thread.join()

          # if an exception happened, re-raise it here in the master thread 
          if self._exec is not None:
            raise self._exec

          return (True, self._proc.wait())

        if self._exec is not None:
          raise self._exec

        return (False, self._proc.wait())

      else:
        return (False, self._proc.wait())

    def read(self):
      return self._proc.fromchild.read()

def run(cmd, timeout=3600):
  start = datetime.now() 
  proc = process(cmd)
  (timed_out, returncode) = proc.wait(timeout)
  now = datetime.now()

  output = ''

  while True:
    s = proc.read()

    if s:
      output += s
    else:
      break

  return (returncode, output, timed_out)

def rstrip_last(s, chars):
  if s[-1] in chars:
    return s[:-1]
  else:
    return s

# {{{ main
usage = "usage: %prog [options]" 

parser = OptionParser(usage=usage)

parser.add_option("--timeout",
                  action="store", type="int",
                  dest="timeout", default=3600,
                  help="Program timeout (seconds)")

parser.add_option("--program",
                  action="store", type="string",
                  dest="program",
                  help="Program to invoke") 

(options, cmd) = parser.parse_args()

if None == options.program:
  print "No program specified"
  exit(1)

(returncode, output, timed_out) = run(options.program, options.timeout)

if not 0 == len(output):
  print rstrip_last(output, '\n')

if timed_out:
  print "Program timed out"

exit(returncode)
# }}}

別のPythonスクリプトは、mpirunと同様に、PBSによって報告された利用可能なリソースに基づいてコマンドライン引数をまとめます。SSH経由でリモートコマンドを開始するためにpython-paramikoを使用します。最初はコマンドを直接実行しましたが、リモートで実行されているプロセスの1つがシグナル(SIGSEGVなど)で終了したときに、正しい終了コードを受信できませんでした。したがって、上記のスクリプトが必要です。

作業中の開発クラスターでこのスクリプトを実行すると、このスクリプトが4コアのDebian GNU / Linuxノードで微妙に機能しないことに気付きましたが、48コアのRHEL/Linuxノードでは機能します。

Debianノードの場合:

wash@hermione0:~/sandbox$ python --version
Python 2.6.7
wash@hermione0:~/sandbox$ uname -a
Linux hermione0 2.6.32-5-amd64 #1 SMP Wed Jan 12 03:40:32 UTC 2011 x86_64 GNU/Linux
wash@hermione0:~/sandbox$ time ./hpx_invoke.py --program='sleep 30' --timeout=5
Program timed out

real 0m30.025s
user 0m0.016s
sys  0m0.012s
wash@hermione0:~/sandbox$ 

RHELノードの場合:

[22:08:23]:wash@vega:/home/wash/sandbox$ python --version
Python 2.6.6
[22:09:28]:wash@vega:/home/wash/sandbox$ uname -a
Linux vega 2.6.32-131.4.1.el6.x86_64 #1 SMP Fri Jun 10 10:54:26 EDT 2011 x86_64 x86_64 x86_64 GNU/Linux
[22:09:30]:wash@vega:/home/wash/sandbox$ time ./hpx_invoke.py --program='sleep 30' --timeout=5
Program timed out

real 0m5.053s
user 0m0.040s
sys  0m0.020s
[22:09:41]:wash@vega:/home/wash/sandbox$ 

これを引き起こしている可能性がありますか?

PS私はこれらのボックスのシステム管理者です。

4

2 に答える 2

1

利用可能なパッケージの違いにより、「サブプロセスインスタンス化ラッパー」の異なるブランチがどちらのマシンでも使用されていると思います。一方のブランチでは、SIGTERM(terminate()呼び出し)を使用し、もう一方のブランチではSIGKILLを使用します。

そうは言っても、sleepどちらかの信号が与えられると、時期尚早に終了するようです。おそらく他の違いもありますが、わかりにくいです。どのマシンで何が起こるかを確認するために、デバッグコードを挿入するのが最善です。

于 2011-07-02T11:37:45.987 に答える
0

問題は、シェルとしてサブプロセスを呼び出すことであることが判明しました(両方のマシンにサブプロセスパッケージがあります)。RHELノードでは、/ bin / shが強制終了されると、呼び出されたプログラムも強制終了されます。Debianノードでは、/ bin / shプロセスのみが強制終了され、呼び出されたプログラムは存続します。

これを修正するために、スクリプトを変更してshell=Trueを使用しないようにしました。

于 2011-07-02T18:45:16.177 に答える