2008-10-21 4 views
30

में किसी एप्लिकेशन का एक उदाहरण सुनिश्चित करें, मैं WxPython में एक जीयूआई एप्लिकेशन पर काम कर रहा हूं, और मुझे यकीन नहीं है कि मैं कैसे सुनिश्चित कर सकता हूं कि मशीन पर किसी भी समय मेरे आवेदन की केवल एक प्रति चल रही है। आवेदन की प्रकृति के कारण, एक से अधिक बार चलने से कोई अर्थ नहीं आता है, और जल्दी से विफल हो जाएगा। Win32 के तहत, मैं बस एक नामित म्यूटेक्स बना सकता हूं और स्टार्टअप पर इसे देख सकता हूं। दुर्भाग्यवश, मुझे लिनक्स में किसी भी सुविधा की जानकारी नहीं है जो यह कर सकता है।लिनक्स

मैं कुछ ऐसा ढूंढ रहा हूं जो स्वचालित रूप से रिलीज़ हो जाएगा, एप्लिकेशन अप्रत्याशित रूप से क्रैश होना चाहिए। मैं लॉक फाइलों को मैन्युअल रूप से हटाने के साथ अपने उपयोगकर्ताओं को बोझ नहीं करना चाहता क्योंकि मैं दुर्घटनाग्रस्त हो गया था।

उत्तर

23

सेमफोरों का उपयोग करने सहित कई सामान्य तकनीकें हैं। जिसे मैं अक्सर देखता हूं वह स्टार्टअप पर "पिड लॉक फ़ाइल" बनाना होता है जिसमें चल रही प्रक्रिया का ढक्कन होता है। यदि प्रोग्राम शुरू होने पर फ़ाइल पहले से मौजूद है, तो इसे खोलें और अंदरूनी पिड को पकड़ें, यह देखने के लिए जांचें कि उस पिड के साथ कोई प्रक्रिया चल रही है या नहीं, अगर यह cmdline मान/proc/pid में यह देखने के लिए जांचता है कि यह है या नहीं आपके प्रोग्राम का एक उदाहरण है, यदि इसे छोड़ दिया जाता है, अन्यथा फ़ाइल को अपने पिड से ओवरराइट करें। पिड फ़ाइल का सामान्य नाम application_name.pid है।

+1

सम्मेलन से, यह/var/run/के अंतर्गत जाना चाहिए, सही? –

+0

जिज्ञासा से बाहर, विशेष रूप से फ़ाइल को खोलने के लिए फ़ाइल को म्यूटेक्स का काम नहीं करता है? – Menkboy

+0

मेनकबॉय, यदि फ़ाइल दुर्घटना की स्थिति में ठीक से बंद है, तो मुझे लगता है कि चीजों को पूरी तरह से सरल और सरल बनायेगा। धन्यवाद। –

0

यदि आप लॉक फ़ाइल बनाते हैं और इसमें पिड डालते हैं, तो आप इसके खिलाफ अपनी प्रक्रिया आईडी जांच सकते हैं और बता सकते हैं कि क्या आप दुर्घटनाग्रस्त हैं, नहीं?

मैंने इसे व्यक्तिगत रूप से नहीं किया है, इसलिए उचित मात्रा में नमक लें। : पी

+2

का उपयोग पीआईडी ​​फ़ाइल आम है, लेकिन मुद्दों के बिना नहीं है। एक के लिए, दौड़ की स्थिति हो सकती है। दूसरा, अगर ऐप मारे गए तो इसे साफ़ नहीं किया जा सकता है। –

0

क्या आप 'pidof' उपयोगिता का उपयोग कर सकते हैं? यदि आपका ऐप चल रहा है, तो पिडॉफ आपके ऐप की प्रोसेस आईडी को stdout पर लिख देगा। यदि नहीं, तो यह एक नई लाइन (एलएफ) प्रिंट करेगा और एक त्रुटि कोड वापस करेगा।

उदाहरण (bash, सादगी के लिए से):

linux# pidof myapp 
8947 
linux# pidof nonexistent_app 

linux# 
+0

यह साफ है, लॉकफाइल दृष्टिकोण से कम कोड आवश्यक है। डाउनसाइड यह बिल्कुल परमाणु नहीं है, लेकिन एकल उदाहरण पहचान के लिए, यह आवश्यक नहीं हो सकता है। –

+0

यदि आप ऐप – MattSmith

+3

के अंदर से "पिडॉफ ऐप" चला रहे हैं तो यह काम नहीं करेगा यदि यह एक ही नाम के साथ एक अलग चल रहा प्रोग्राम है, या यदि कोई अन्य उपयोगकर्ता प्रोग्राम चला रहा है तो यह भी काम नहीं करेगा। – CesarB

0

अब तक सबसे आम तरीका/var/रन/कहा जाता है [आवेदन] .pid जो केवल की पीआईडी ​​शामिल में एक फ़ाइल ड्रॉप करने के लिए है चल रही प्रक्रिया, या मूल प्रक्रिया। एक विकल्प के रूप में, आप सक्रिय प्रक्रिया में संदेश भेजने में सक्षम होने के लिए एक ही निर्देशिका में नामित पाइप बना सकते हैं, उदा। एक नई फाइल खोलने के लिए।

+0

पीआईडी ​​फ़ाइल का उपयोग करना आम है, लेकिन बिना किसी समस्या के है। एक के लिए, दौड़ की स्थिति हो सकती है। दूसरा, अगर ऐप मारे गए तो इसे साफ़ नहीं किया जा सकता है। –

1

एक पायथन मॉड्यूल की तलाश करें जो यूनिक्स पर SYSV semaphores में इंटरफेस करता है। सेमेफोरों में एक SEM_UNDO ध्वज होता है जो प्रक्रिया को क्रैश होने पर एक प्रक्रिया द्वारा जारी किए जाने वाले संसाधनों का कारण बनता है।

अन्यथा रूप में बर्नार्ड सुझाव दिया है, तो आप उपयोग कर सकते हैं

import os 
os.getpid() 

और यह/var/चलाने/APPLICATION_NAME को .pid लिखें। जब प्रक्रिया शुरू होती है, तो यह जांचना चाहिए कि क्या/var/run/application_name .pid ps तालिका में सूचीबद्ध है और यदि यह है, तो अन्यथा अपना स्वयं का पिड/var/run/application_name .pid में लिखें । निम्नलिखित var_run_pid में पीआईडी ​​आप से पढ़ा है/var/चलाने/APPLICATION_NAME को .pid

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

+1 एसईएसवी सेमफोरों का सुझाव देने के लिए +1, -1 कुछ अधिक कुशल (कहने के लिए, 'kill -0' - सिग्नल कॉल के माध्यम से सिग्नल कॉल के माध्यम से कॉल करने के बजाय' पीएस 'को कॉल करने का सुझाव देने के लिए, यदि कोई अतिरिक्त फोर्क/निष्पादन से बचने के लिए पसंद करता है) और रेस की स्थिति और पीआईडी ​​टकराव से बचने के लिए सलाहकार लॉकिंग (यानी झुंड) के बिना पिडफाइल का सुझाव देना। –

58

सही काम flock(LOCK_EX) का उपयोग कर सलाहकार ताला है; पायथन में, यह fcntl module में पाया जाता है।

pidfiles के विपरीत, इन ताले हमेशा स्वचालित रूप से जारी कर रहे हैं जब आपके प्रक्रिया किसी भी कारण से मर जाता है, कोई दौड़ की स्थिति विलोपन फाइल करने के लिए (के रूप में फ़ाइल नहीं जरूरत ताला जारी करने के लिए हटाए जाने के लिए करता है) संबंधित मौजूद है, और पीआईडी ​​को विरासत में लाने वाली एक अलग प्रक्रिया का कोई मौका नहीं है और इस प्रकार एक बालों को लॉक करने के लिए दिखाई दे रहा है।

यदि आप अशुद्ध शट डाउन डिटेक्शन चाहते हैं, तो आप लॉक को पकड़ने के बाद फ़ाइल में मार्कर (जैसे कि पीआईडी, परंपरागत के लिए) लिख सकते हैं, और फिर क्लीन शटडाउन से पहले 0-बाइट स्थिति में फ़ाइल को छोटा कर सकते हैं (जबकि लॉक आयोजित किया जा रहा है); इस प्रकार, यदि तालाब नहीं है और फ़ाइल खाली नहीं है, तो एक अशुद्ध शटडाउन इंगित किया जाता है।

+1

मैं मानता हूं, यह ज्यादातर मामलों में शायद सबसे अच्छी विधि है। –

+1

यह लॉक करने का सही तरीका है, हालांकि पीआईडी ​​फ़ाइल लिखना भी अच्छा है जो सामान्य निकास पर साफ़ किया गया है, साथ ही साथ/var/lock/subsys (यदि यह मौजूद है) में एक प्रविष्टि बनाने के लिए भी अच्छा है। यह आपके प्रोग्राम को यह समझने की अनुमति देता है कि क्या यह अन्य चीजों के साथ दुर्घटना से फिर से शुरू हो रहा है। तो, दोनों मदद करता है। –

+0

@ टिंकर्टिम - एक बुरा सुझाव नहीं है, हालांकि यह एक से अधिक होने के बजाय pidfile झुंड() को समझ में आता है। –

24

पूरा ताला समाधान 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) 
+2

लॉक फ़ाइल मानना ​​सभी उपयोगकर्ताओं के लिए समान है, क्योंकि यह लॉक के लिए उपयोगी होना चाहिए, यह एक लेखन अनुमति समस्या बना सकता है। मैंने इस समस्या को एक उत्तर में वर्णित और संबोधित किया है। –

+0

मुझे नहीं पता क्यों, लेकिन यह पायथन 3.4.1 में काम नहीं करता है। बिना किसी त्रुटि के दो उदाहरण चलते हैं। – boreq

+0

@ बोरेक, क्या आप इस बारे में थोड़ा और स्पष्ट हो सकते हैं कि आप कैसे परीक्षण कर रहे हैं (उपयोग में फाइल सिस्टम, अपेक्षित व्यवहार, वास्तविक व्यवहार इत्यादि)? –

8

wxWidgets इस उद्देश्य के लिए एक wxSingleInstanceChecker वर्ग प्रदान करता है: wxPython doc, या wxWidgets doc। wxWidgets डॉक सी ++ में नमूना कोड है, लेकिन अजगर बराबर कुछ इस तरह (untested) होना चाहिए:

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

मैं आवेदनों की इन प्रकार के चलाने के लिए एक बुनियादी ढांचा बनाया है जब आप पारित करने में सक्षम होना चाहता हूँ पहले प्रयास किए गए उदाहरणों के कमांड लाइन तर्क। एक उदाहरण एक पूर्वनिर्धारित बंदरगाह पर सुनना शुरू कर देगा यदि उसे पहले से सुनकर कोई उदाहरण नहीं मिलता है। यदि कोई उदाहरण पहले से मौजूद है, तो यह सॉकेट पर अपने कमांड लाइन तर्क भेजता है और बाहर निकलता है।

code w/ explanation

6

इस उपयोगकर्ता zgoda द्वारा answer पर बनाता है। यह मुख्य रूप से लॉक फ़ाइल में लेखन पहुंच के साथ एक मुश्किल चिंता को संबोधित करता है। विशेष रूप से, यदि लॉक फ़ाइल को पहली बार 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 अनुमतियां आवश्यक हैं। आप किस निर्देशिका का उपयोग करने के लिए अपना निर्णय ले सकते हैं।

ध्यान दें कि लॉक फ़ाइल में फ़ाइल हैंडल खोलने की कोई आवश्यकता नहीं है।

4

यहाँ 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)