2014-10-20 70 views
1

我正在通過Python執行外部程序。我想知道調用外部程序的最佳選擇,subprocess.Popen()subprocess.call()。另外,我需要測量經過時間,外部程序使用的內存和CPU的數量。我聽說過psutil,但我真的不知道該選哪個。測量已用時間,extern程序使用的內存和CPU的數量

+0

[ 'subprocess.call'](https://hg.python.org/cpython/file/4ea3cc4d1d9d/Lib/subprocess.py#l533)是'subprocess.Popen'的薄包裝。查看源代碼應該可以幫助您瞭解這些差異。 – mgilson 2014-10-20 22:01:56

回答

6

我也需要測量經過時間,由EXTERN程序

(我會假設你只需要在你的平臺的rusage,而且提供的信息使用的內存和CPU的數量,因爲Windows根本就沒有這種東西,所以我也假設你不關心Windows如果你需要額外的信息,只能用某種特定於平臺的方式(讀取Linux的proc文件系統,或者調用AIX的顯示器API或其他),你幾乎不能用stdlib來做這件事,而psutil的答案是唯一的答案。)

subprocess庫包裝調用fork,然後execv子項中的家庭功能和父項中的家庭功能waitpid。 (您可以通過與源開始call和追查下到從那裏的其他呼叫看到這一點。)

不幸的是,簡單的方法來從一個孩子得到的資源使用情況是調用wait3wait4,不waitwaitpid 。所以subprocess讓你瘋狂接近你想要的,但不是那裏。

但是,你有幾個選擇:

  • 如果你只有一個孩子的過程,getrusage(RUSAGE_CHILDREN)是你所需要的。
  • 您可以啓動該過程,然後在收穫子女之前使用psutil(或平臺特定的代碼)從proc.pid獲取資源信息。
  • 您可以使用psutil來完成所有操作,並將subprocess留下。
  • 您可以繼承subprocess.Popen以覆蓋其wait方法。

最後一個比聽起來簡單得多。如果你看到源代碼,實際上只有3個地方os.waitpid被調用,並且只有其中一個會影響你的代碼;我認爲這是_try_wait中的一個。所以(未經測試):

class ResourcePopen(subprocess.Popen): 
    def _try_wait(self, wait_flags): 
     """All callers to this function MUST hold self._waitpid_lock.""" 
     try: 
      (pid, sts, res) = _eintr_retry_call(os.wait4, self.pid, wait_flags) 
     except OSError as e: 
      if e.errno != errno.ECHILD: 
       raise 
      # This happens if SIGCLD is set to be ignored or waiting 
      # for child processes has otherwise been disabled for our 
      # process. This child is dead, we can't get the status. 
      pid = self.pid 
      sts = 0 
     else: 
      self.rusage = res 
     return (pid, sts) 

def resource_call(*popenargs, timeout=None, **kwargs): 
    """Run command with arguments. Wait for command to complete or 
    timeout, then return the returncode attribute and resource usage. 

    The arguments are the same as for the Popen constructor. Example: 

    retcode, rusage = call(["ls", "-l"]) 
    """ 
    with ResourcePopen(*popenargs, **kwargs) as p: 
     try: 
      retcode = p.wait(timeout=timeout) 
      return retcode, p.rusage 
     except: 
      p.kill() 
      p.wait() 
      raise 

現在:

retcode, rusage = resource_call(['spam', 'eggs']) 
print('spam used {}s of system time'.format(rusage.ru_stime)) 

(在許多平臺上使用這種方式時甚至不會工作...),與此相比,使用帶有psutil混合:

p = subprocess.Popen(['spam', 'eggs']) 
ps = psutil.Process(p.pid) 
p.wait() 
print('spam used {}s of system time'.format(ps.cpu_times().system)) 

當然,後者不是更復雜的原因不是很好,它更復雜,因爲它更加強大和靈活;您還可以獲取rusage不包含的各種數據,並且您可以在該過程正在運行時每秒獲取信息,而不必等待完成,並且可以在Windows上使用它,等等......

+0

感謝您精心製作的答案!我正在使用psutil方法,但是當我嘗試運行我的file.py時,回溯導致:psutil.NoSuchProcess:進程不再存在。這是來自最後一次印刷。我能做什麼? – 2014-10-21 00:39:00

+0

@ C.Porto:好的,一些'psutil'函數在獲得進程後不能使用,有些可以,列表取決於平臺。這就是爲什麼混合解決方案(使用'subprocess'來運行,使用'psutil'來衡量它們)是最糟糕的選擇......如果你不需要使用psutil 1.x和['psutil。 Popen'](http://pythonhosted.org//psutil/#popen-class)具有足夠的功能,請嘗試。我相信它給了你一個'psutil.Process'子類,並且保證能夠在'communic'和'wait'調用之間訪問它的東西,但是我可能是錯的。 – abarnert 2014-10-21 01:02:23

+0

顯然,在這個過程獲得成功後,我真的不允許看到過程信息(我在Mac OS X上),即使使用psutil.Popen,你知道是否有其他方式來測量內存和CPU嗎?該過程得到收穫)? – 2014-10-21 02:16:39

相關問題