web-dev-qa-db-ja.com

Linuxでアプリケーションの単一インスタンスを確保する

私はWxPythonでGUIアプリケーションに取り組んでいますが、マシン上で常にアプリケーションのコピーが1つだけ実行されていることを確認する方法がわかりません。アプリケーションの性質上、複数回実行しても意味がなく、すぐに失敗します。 Win32では、名前付きミューテックスを作成して、起動時に確認できます。残念ながら、これを実行できるLinuxの機能はわかりません。

アプリケーションが予期せずクラッシュした場合に自動的にリリースされるものを探しています。クラッシュしたため、ロックファイルを手動で削除しなければならないという負担をユーザーに負わせたくありません。

32
Matt Green

セマフォの使用を含むいくつかの一般的な手法があります。私が最もよく使用するのは、実行中のプロセスのpidを含む「pidロックファイル」を起動時に作成することです。プログラムの起動時にファイルがすでに存在する場合は、ファイルを開いて内部のpidを取得し、そのpidを持つプロセスが実行されているかどうかを確認します。実行されている場合は、/ proc/pidのコマンドライン値を確認します。 =それがプログラムのインスタンスであるかどうかを確認し、終了した場合は終了します。それ以外の場合は、ファイルをpidで上書きします。 pidファイルの通常の名前はapplication_name.pidです。

25
Robert Gamble

正しいことは、flock(LOCK_EX)を使用したアドバイザリロックです。 Pythonでは、これは fcntl module にあります。

Pidfilesとは異なり、これらのロックは、何らかの理由でプロセスが停止したときに常に自動的に解放され、ファイルの削除に関連する競合状態は存在しません(ファイルは削除されないため) 必要 ロックを解放するために削除される必要があります)、別のプロセスがPIDを継承し、古いロックを検証しているように見える可能性はありません。

クリーンシャットダウンの検出が必要な場合は、ロックを取得した後にマーカー(従来の場合は、PIDなど)をファイルに書き込み、クリーンシャットダウンの前に(ロックが保持されている間)ファイルを0バイトのステータスに切り捨てることができます。 );したがって、ロックが保持されておらず、ファイルが空でない場合は、クリーンでないシャットダウンが示されます。

60
Charles Duffy

fcntlモジュールを使用した完全なロックソリューション:

import fcntl
pid_file = 'program.pid'
fp = open(pid_file, 'w')
try:
    fcntl.lockf(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
    # another instance is running
    sys.exit(1)
28
zgoda

wxWidgetsは、この目的のためにwxSingleInstanceCheckerクラスを提供します: wxPython doc 、または wxWidgets doc 。 wxWidgetsドキュメントにはC++のサンプルコードがありますが、python同等のものは次のようになります(テストされていません):

  name = "MyApp-%s" % wx.GetUserId()
  checker = wx.SingleInstanceChecker(name)
  if checker.IsAnotherRunning():
      return False
8
Brian Victor

これは、ユーザーによる answerzgoda に基づいています。これは主に、ロックファイルへの書き込みアクセスに関係するトリッキーな懸念に対処します。特に、ロックファイルが最初にrootによって作成された場合、ユーザーfooの書き込み権限がないため、別のユーザーfooはこのファイルの書き換えを正常に試行できなくなります。明らかな解決策は、すべての人に書き込み権限を持つファイルを作成することのようです。このソリューションは、私が別の answer に基づいて構築されており、そのようなカスタム権限でファイルを作成する必要があります。この懸念は、プログラムがrootを含むすべてのユーザーによって実行される可能性がある現実の世界では重要です。

import fcntl, os, stat, tempfile

app_name = 'myapp'  # <-- Customize this value

# Establish lock file settings
lf_name = '.{}.lock'.format(app_name)
lf_path = os.path.join(tempfile.gettempdir(), lf_name)
lf_flags = os.O_WRONLY | os.O_CREAT
lf_mode = stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH  # This is 0o222, i.e. 146

# Create lock file
# Regarding umask, see https://stackoverflow.com/a/15015748/832230
umask_original = os.umask(0)
try:
    lf_fd = os.open(lf_path, lf_flags, lf_mode)
finally:
    os.umask(umask_original)

# Try locking the file
try:
    fcntl.lockf(lf_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
    msg = ('Error: {} may already be running. Only one instance of it '
           'can run at a time.'
           ).format('appname')
    exit(msg)

上記のコードの制限は、ロックファイルが予期しないアクセス許可ですでに存在している場合、それらのアクセス許可は修正されないことです。

ロックファイルのディレクトリとして/var/run/<appname>/を使用したかったのですが、このディレクトリを作成するにはroot権限が必要です。どのディレクトリを使用するかは、独自に決定できます。

ロックファイルへのファイルハンドルを開く必要がないことに注意してください。

6
Acumenus

これがTCPポートベースのソリューションです:

# Use a listening socket as a mutex against multiple invocations
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('127.0.0.1', 5080))
s.listen(1)
4
shuckc

_semaphore.h_ --sem_open()sem_trywait()などで定義されている関数のセットはPOSIXに相当すると思います。

1
Menkboy

UNIX上のSYSVセマフォにインターフェイスするpythonモジュールを探します。セマフォにはSEM_UNDOフラグがあり、プロセスがクラッシュした場合にプロセスによって保持されているリソースが解放されます。

そうでなければ、バーナードが示唆したように、あなたは使うことができます

import os
os.getpid()

そしてそれを/var/run/application_name.pidに書き込みます。プロセスが開始すると、/ var/run/application_name。pidのpidがpsテーブルにリストされているかどうかを確認し、リストされている場合は終了します。リストされていない場合は、独自のpidを/ var/run /に書き込みます。 application_name。pid。次のvar_run_pidは、/ var/run/application_name。pidから読み取ったpidです。

cmd = "ps -p %s -o comm=" % var_run_pid
app_name = os.popen(cmd).read().strip()
if len(app_name) > 0:
    Already running
1
Matthew Smith

後続の試行されたインスタンスのコマンドライン引数を最初のインスタンスに渡せるようにする場合に、これらの種類のアプリケーションを実行するための基本的なフレームワークを作成しました。インスタンスがすでにリッスンしているインスタンスが見つからない場合、インスタンスは事前定義されたポートでリッスンを開始します。インスタンスがすでに存在する場合は、コマンドライン引数をソケット経由で送信して終了します。

説明付きコード

0
Zach Dwiel

ロックファイルを作成してその中にpidを入れると、プロセスIDをそれと照合して、クラッシュしたかどうかを判断できます。

私は個人的にこれをしていませんので、適量の塩を服用してください。 :p

0
Bernard

'pidof'ユーティリティを使用できますか?アプリが実行されている場合、pidofはアプリのプロセスIDをstdoutに書き込みます。そうでない場合は、改行(LF)を出力し、エラーコードを返します。

例(簡単にするためにbashから):

linux# pidof myapp
8947
linux# pidof nonexistent_app

linux#
0
Adam Liss

最も一般的な方法は、実行中のプロセスまたは親プロセスのPIDのみを含む[application] .pidというファイルを/ var/run /にドロップすることです。別の方法として、同じディレクトリに名前付きパイプを作成して、アクティブなプロセスにメッセージを送信できるようにすることもできます。新しいファイルを開きます。

0
user29832