質問

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

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

役に立ちましたか?

解決

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

他のヒント

Right Thingは、flock(LOCK_EX)を使用したアドバイザリロックです。 Pythonでは、これは fcntlモジュールにあります。

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

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

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)

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

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

これは、ユーザー回答に基づいて構築されています。 / zgoda "> zgoda 。主に、ロックファイルへの書き込みアクセスに関連する厄介な問題に対処します。特に、ロックファイルがrootによって最初に作成された場合、ユーザーfooの書き込み権限がないため、別のユーザー/var/run/<appname>/がこのファイルを書き換えることができなくなります。明らかな解決策は、すべてのユーザーに書き込み許可を持つファイルを作成することです。このソリューションは、別の答えにも基づいて構築されており、このようなカスタムアクセス許可でファイルを作成する必要があります。この懸念は、<=>を含むすべてのユーザーがプログラムを実行できる現実の世界では重要です。

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)

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

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

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

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)

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

バーナードが示唆したように、使用できます

import os
os.getpid()

そして/var/run/application_name.pidに書き込みます。プロセスが開始されると、/ var / run / application_name .pidのpidがpsテーブルにリストされているかどうかを確認し、リストが存在する場合は終了します。 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

semaphore.h で定義されている一連の関数-sem_open()sem_trywait()など-POSIXに相当します。

ロックファイルを作成してpidを入れると、プロセスIDを確認してクラッシュしたかどうかを確認できますか?

私は個人的にこれをやったことがないので、適切な量の塩で服用してください。 :p

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

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

linux# pidof myapp
8947
linux# pidof nonexistent_app

linux#

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

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

説明付きのコード

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top