यह स्क्रिप्ट कैसे सुनिश्चित करती है कि केवल एक ही उदाहरण चल रहा है?


22

19 अगस्त 2013 को, रैंडल एल। श्वार्ट्ज ने इस शेल स्क्रिप्ट को पोस्ट किया , जिसका उद्देश्य लिनक्स पर यह सुनिश्चित करना था, "कि [केवल] स्क्रिप्ट का एक ही उदाहरण चल रहा है, दौड़ की स्थिति के बिना या लॉक फ़ाइलों को साफ करने के लिए":

#!/bin/sh
# randal_l_schwartz_001.sh
(
    if ! flock -n -x 0
    then
        echo "$$ cannot get flock"
        exit 0
    fi
    echo "$$ start"
    sleep 10 # for testing.  put the real task here
    echo "$$ end"
) < $0

यह विज्ञापन के रूप में काम करता है:

$ ./randal_l_schwartz_001.sh & ./randal_l_schwartz_001.sh
[1] 11863
11863 start
11864 cannot get flock
$ 11863 end

[1]+  Done                    ./randal_l_schwartz_001.sh
$

यहाँ मैं क्या समझता हूँ:

  • स्क्रिप्ट <एक $0उपधारा के STDIN (यानी फ़ाइल विवरणक 0) के लिए अपनी खुद की सामग्री (यानी से ) की एक प्रति ( ) की प्रतिलिपि बनाता है।
  • उपधारा के भीतर, स्क्रिप्ट flock -n -xफ़ाइल डिस्क्रिप्टर पर एक गैर-अवरोधक, अनन्य लॉक ( ) प्राप्त करने का प्रयास करती है 0
    • यदि वह प्रयास विफल हो जाता है, तो सबस्क्रिप्शन बाहर निकल जाता है (और इसलिए मुख्य स्क्रिप्ट आती है, क्योंकि इसके लिए कुछ और नहीं है)।
    • यदि इसके बजाय प्रयास सफल होता है, तो सबस्क्रिप्शन वांछित कार्य चलाता है।

यहाँ मेरे सवाल हैं:

  • स्क्रिप्ट को पुनर्निर्देशित करने की आवश्यकता क्यों होती है, उपधारा द्वारा विरासत में मिली फाइल डिस्क्रिप्टर के लिए, अपनी सामग्री की प्रति , किसी अन्य फाइल की सामग्री के बजाय, कहती है? (मैंने एक अलग फ़ाइल से रीडायरेक्ट करने की कोशिश की और ऊपर के रूप में फिर से चल रहा है, और निष्पादन आदेश बदल गया: गैर-पृष्ठभूमि वाले कार्य ने पृष्ठभूमि एक से पहले लॉक प्राप्त किया। इसलिए, शायद फ़ाइल की अपनी सामग्री का उपयोग करने से दौड़ की स्थिति से बचा जाता है? लेकिन कैसे?)
  • स्क्रिप्ट को फ़ाइल को पुनर्निर्देशित करने की आवश्यकता क्यों है, एक फाइल डिस्क्रिप्टर को जो सब-हेल्ड द्वारा विरासत में मिला है, एक फाइल की सामग्री की एक कॉपी, वैसे भी?
  • फ़ाइल डिस्क्रिप्टर 0पर एक शेल में एक विशेष लॉक रखने से एक ही स्क्रिप्ट की एक कॉपी को अलग शेल में चलने से रोकने के लिए फ़ाइल डिस्क्रिप्टर पर एक विशेष लॉक प्राप्त करने से रोकता है 0? गोले मानक फ़ाइल वर्णनकर्ता (के अपने, अलग प्रतियां नहीं है 0, 1है, और 2, यानी STDIN, STDOUT, और STDERR)?

जब आपने एक अलग फ़ाइल से रीडायरेक्ट करने के लिए अपने प्रयोग की कोशिश की तो आपकी सटीक परीक्षण प्रक्रिया क्या थी?
२:२३

1
मुझे लगता है कि आप इस लिंक का उल्लेख कर सकते हैं। stackoverflow.com/questions/185451/…
देब पिकर

जवाबों:


22

स्क्रिप्ट को पुनर्निर्देशित करने की आवश्यकता क्यों होती है, उपधारा द्वारा विरासत में मिली फाइल डिस्क्रिप्टर के लिए, अपनी सामग्री की प्रति, किसी अन्य फाइल की सामग्री के बजाय, कहती है?

आप किसी भी फ़ाइल का उपयोग कर सकते हैं, जब तक कि स्क्रिप्ट की सभी प्रतियां एक ही उपयोग न करें। का उपयोग करते हुए$0केवल स्क्रिप्ट पर लॉक : यदि आप स्क्रिप्ट की प्रतिलिपि बनाते हैं और इसे किसी अन्य उपयोग के लिए संशोधित करते हैं, तो आपको लॉक फ़ाइल के लिए एक नए नाम के साथ आने की आवश्यकता नहीं है। यह सुविधाजनक है।

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

(बेशक, अगर कुछ प्रक्रिया स्क्रिप्ट को चलाती है और इसे वास्तविक पथ के बजाय शून्य तर्क के रूप में एक बना हुआ मान देती है, तो यह टूट जाता है। लेकिन यह शायद ही कभी किया जाता है।)

(मैंने एक अलग फ़ाइल का उपयोग करने की कोशिश की और ऊपर के रूप में फिर से चल रहा है, और निष्पादन आदेश बदल गया)

क्या आप सुनिश्चित हैं कि उपयोग की गई फ़ाइल के कारण, और केवल यादृच्छिक भिन्नता नहीं थी? एक पाइपलाइन के साथ के रूप में, वास्तव में यह सुनिश्चित करने का कोई तरीका नहीं है कि आदेशों को चलाने के लिए क्या आदेश मिलता है cmd1 & cmd। यह ज्यादातर ओएस अनुसूचक के लिए है। मुझे अपने सिस्टम पर यादृच्छिक भिन्नता मिलती है।

स्क्रिप्ट को फ़ाइल को पुनर्निर्देशित करने की आवश्यकता क्यों है, एक फाइल डिस्क्रिप्टर को जो सब-हेल्ड द्वारा विरासत में मिला है, एक फाइल की सामग्री की एक कॉपी, वैसे भी?

ऐसा लगता है कि शेल अपने आप में केवल flockउपयोगिता को रखने के बजाय लॉक को पकड़े हुए फ़ाइल विवरण की एक प्रति रखता है। के साथ बनाया गया एक लॉक flock(2)तब जारी किया जाता है जब फाइल डिस्क्रिप्टर के पास यह बंद होता है।

flockदो मोड हैं, या तो एक फ़ाइल नाम के आधार पर लॉक लेने के लिए, और एक बाहरी कमांड चलाते हैं (जिस स्थिति flockमें आवश्यक ओपन फाइल डिस्क्रिप्टर रखता है), या बाहर से फाइल डिस्क्रिप्टर लेने के लिए, इसलिए एक बाहरी प्रक्रिया को होल्ड करने के लिए जिम्मेदार है। यह।

ध्यान दें कि फ़ाइल की सामग्री यहां प्रासंगिक नहीं है, और कोई प्रतियां नहीं बनाई गई हैं। उप-पुनर्निर्देशन अपने आप में किसी भी डेटा की प्रतिलिपि नहीं बनाता है, यह केवल फ़ाइल के हैंडल को खोलता है।

फ़ाइल डिस्क्रिप्टर 0 पर एक विशेष शेल को एक शेल में रखने से एक ही स्क्रिप्ट की एक कॉपी को रोका जा सकता है, एक अलग शेल में चल रहा है, फ़ाइल डिस्क्रिप्टर 0 पर एक विशेष लॉक प्राप्त करने से? क्या गोले की अपनी फ़ाइल, मानक फ़ाइल डिस्क्रिप्टर (0, 1, और 2, यानी STDIN, STDOUT और STDERR) की अलग-अलग प्रतियां नहीं हैं?

हां, लेकिन लॉक फाइल पर है , फाइल डिस्क्रिप्टर पर नहीं। फ़ाइल का केवल एक खुला उदाहरण एक बार में लॉक को पकड़ सकता है।


मुझे लगता execहै कि लॉक फाइल को हैंडल खोलने के लिए , आपको बिना सबस्क्रिप्शन के ही काम करने में सक्षम होना चाहिए :

$ cat lock.sh
#!/bin/sh

exec 9< "$0"

if ! flock -n -x 9; then
    echo "$$/$1 cannot get flock" 
    exit 0
fi

echo "$$/$1 got the lock"
sleep 2
echo "$$/$1 exit"

$ ./lock.sh bg & ./lock.sh fg ; wait; echo
[1] 11362
11363/fg got the lock
11362/bg cannot get flock
11363/fg exit
[1]+  Done                    ./lock.sh bg

1
के { }बजाय का उपयोग ( )करना भी काम करेगा और उपधारा से बचें।
आर ..

आगे जी + पोस्ट पर टिप्पणियों में नीचे, किसी ने भी लगभग उसी विधि का उपयोग करने का सुझाव दिया exec
डेविड जेड

@ आर .., ओह, ज़रूर। लेकिन यह वास्तविक स्क्रिप्ट के आसपास अतिरिक्त ब्रेसिज़ के साथ अभी भी बदसूरत है।
इलकाचू

9

फ़ाइल विवरण के माध्यम से एक फ़ाइल लॉक एक फ़ाइल से जुड़ी होती है । उच्च स्तर पर, स्क्रिप्ट के एक उदाहरण में संचालन का क्रम है:

  1. उस फ़ाइल को खोलें जिसमें लॉक लगा हुआ है ("लॉक फाइल")।
  2. लॉक फ़ाइल पर एक ताला ले लो।
  3. कार्य करना।
  4. लॉक फ़ाइल बंद करें। यह उस लॉक को रिलीज़ करता है जो फ़ाइल खोलकर बनाई गई फ़ाइल विवरण से जुड़ी होती है।

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

फ़ाइल खोलने से फ़ाइल विवरण बनता है । यह एक कर्नेल ऑब्जेक्ट है जिसमें प्रोग्रामिंग इंटरफेस में बहुत प्रत्यक्ष दृश्यता नहीं है। आप फ़ाइल वर्णनकर्ताओं के माध्यम से अप्रत्यक्ष रूप से एक फ़ाइल विवरण तक पहुँच प्राप्त करते हैं, लेकिन आम तौर पर आप इसे फ़ाइल तक पहुँचने (इसकी सामग्री या मेटाडेटा को पढ़ने या लिखने) के रूप में सोचते हैं। एक लॉक उन विशेषताओं में से एक है जो एक फ़ाइल या एक डिस्क्रिप्टर के बजाय फ़ाइल विवरण के लिए एक संपत्ति है।

शुरुआत में, जब एक फ़ाइल खोली जाती है, तो फ़ाइल विवरण में एक एकल फ़ाइल डिस्क्रिप्टर होता है, लेकिन अधिक डिस्क्रिप्टर या तो एक अन्य डिस्क्रिप्टर ( dupसिस्टम कॉल का परिवार) बनाकर या एक उपप्रकार (जिसके बाद माता-पिता और माता-पिता दोनों) बनाकर बनाया जा सकता है बच्चे के पास एक ही फ़ाइल विवरण तक पहुंच है)। एक फ़ाइल डिस्क्रिप्टर स्पष्ट रूप से बंद हो सकता है या जब यह प्रक्रिया मर जाती है। जब फ़ाइल से जुड़ी अंतिम फ़ाइल डिस्क्रिप्टर बंद हो जाती है, तो फ़ाइल विवरण बंद हो जाता है।

यहां बताया गया है कि ऊपर दिए गए ऑपरेशन का क्रम फ़ाइल विवरण को कैसे प्रभावित करता है।

  1. पुनर्निर्देशन <$0 फ़ाइल विवरण को बनाते हुए उप फ़ाइल में स्क्रिप्ट फ़ाइल खोलता है। इस बिंदु पर विवरण में एक एकल फाइल डिस्क्रिप्टर जुड़ा हुआ है: सब्स्क्रिप्शन में डिस्क्रिप्टर नंबर 0।
  2. उपधारा आह्वान करती है flock और इसके बाहर निकलने का इंतजार । जबकि झुंड चल रहा है, विवरण में दो विवरणक संलग्न हैं: उप-संख्या में संख्या 0 और झुंड प्रक्रिया में संख्या 0 है। जब झुंड ताला लेता है, तो वह फ़ाइल विवरण की एक संपत्ति सेट करता है। यदि किसी अन्य फ़ाइल विवरण में पहले से ही फ़ाइल लॉक है, तो लॉक लॉक नहीं ले सकता, क्योंकि यह एक विशेष लॉक है।
  3. उपधारा सामान करती है। चूँकि इसमें अभी भी लॉक के साथ विवरण पर एक ओपन फाइल डिस्क्रिप्टर है, इसलिए यह विवरण विद्यमान रहता है, और यह अपना लॉक रखता है क्योंकि कोई भी कभी भी लॉक को नहीं हटाता है।
  4. कोष्ठक समापन कोष्ठक पर मर जाता है। यह फ़ाइल विवरण पर अंतिम फ़ाइल डिस्क्रिप्टर को बंद कर देता है जिसमें लॉक होता है, इसलिए लॉक इस बिंदु पर गायब हो जाता है।

स्क्रिप्ट के पुनर्निर्देशन का उपयोग करने का कारण यह $0है कि शेल में फ़ाइल को खोलने का एकमात्र तरीका पुनर्निर्देशन है, और पुनर्निर्देशन को सक्रिय रखने का एकमात्र तरीका एक फ़ाइल विवरणक को खुला रखना है। उप-मानक अपने मानक इनपुट से कभी नहीं पढ़ता है, इसे बस खुला रखने की आवश्यकता है। ऐसी भाषा में, जो कॉल खोलने और बंद करने के लिए सीधी पहुँच देती है, आप उपयोग कर सकते हैं

fd = open($0)
flock(fd, LOCK_EX)
do stuff
close(fd)

आप वास्तव में शेल में संचालन का समान अनुक्रम प्राप्त कर सकते हैं यदि आप execबिलिन के साथ पुनर्निर्देशन करते हैं ।

exec <$0
flock -n -x 0
# do stuff
exec <&-

यदि यह मूल मानक इनपुट तक पहुँच रखना चाहता था, तो स्क्रिप्ट एक अलग फ़ाइल डिस्क्रिप्टर का उपयोग कर सकती है।

exec 3<$0
flock -n -x 0
# do stuff
exec 3<&-

या एक उपधारा के साथ:

(
  flock -n -x 3
  # do stuff
) 3<$0

स्क्रिप्ट फ़ाइल पर लॉक होना आवश्यक नहीं है। यह किसी भी फ़ाइल पर हो सकता है जिसे पढ़ने के लिए खोला जा सकता है (इसलिए इसका अस्तित्व है, यह एक फ़ाइल प्रकार होना चाहिए जिसे एक नियमित फ़ाइल या नामांकित पाइप के रूप में पढ़ा जा सकता है लेकिन निर्देशिका नहीं है, और स्क्रिप्ट प्रक्रिया होनी चाहिए इसे पढ़ने की अनुमति)। स्क्रिप्ट फ़ाइल में वह लाभ होता है जिसे वह मौजूद और पठनीय होने की गारंटी देता है (उस किनारे के मामले को छोड़कर जहां इसे स्क्रिप्ट को लागू करने के समय के बीच बाहरी रूप से हटा दिया गया था और स्क्रिप्ट को समय मिल जाता है<$0 पुनर्निर्देशन )।

जब तक flockसफल होता है, और स्क्रिप्ट एक फाइलसिस्टम पर होती है, जहां ताले छोटी गाड़ी नहीं होती हैं (कुछ नेटवर्क फाइल सिस्टम जैसे कि एनएफएस छोटी गाड़ी हो सकती हैं), मैं नहीं देखता कि एक अलग लॉक फाइल का उपयोग कैसे एक दौड़ की स्थिति की अनुमति दे सकता है। मुझे आपकी ओर से हेरफेर की त्रुटि पर संदेह है।


एक दौड़ की स्थिति है: आप नियंत्रित नहीं कर सकते हैं कि स्क्रिप्ट का कौन सा उदाहरण लॉक हो जाता है। सौभाग्य से, लगभग सभी उद्देश्यों के लिए, इससे कोई फर्क नहीं पड़ता।
मार्क

4
@ मर्क लॉक की दौड़ है, लेकिन यह दौड़ की स्थिति नहीं है। एक दौड़ की स्थिति है जब समय कुछ खराब होने की अनुमति दे सकता है, जैसे कि दो प्रक्रियाएं एक ही समय में एक ही महत्वपूर्ण खंड में हो सकती हैं। यह नहीं जानते कि कौन सी प्रक्रिया महत्वपूर्ण खंड में प्रवेश करेगी, उम्मीद की जा रही है कि यह नस्ल की स्थिति नहीं है।
गिल्स एसओ- बुराई को रोकना '

1
बस FYI करें, "फ़ाइल विवरण" में लिंक, ओपन ग्रुप स्पेक्स पेज की ओर इशारा करता है, जो कि अवधारणा के विशिष्ट विवरण के बजाय इंडेक्स पेज है, जो कि मुझे लगता है कि आप करना चाहते हैं। या फिर आप भी अपने पुराने जवाब यहाँ भी लिंक कर सकते हैं unix.stackexchange.com/a/195164/85039
सर्गी Kolodyazhnyy

5

लॉकिंग के लिए उपयोग की गई फ़ाइल महत्वहीन है, स्क्रिप्ट का उपयोग करता है $0क्योंकि यह एक ऐसी फ़ाइल है जो मौजूद है।

जिस क्रम में ताले प्राप्त किए जाते हैं वह कम या ज्यादा यादृच्छिक होगा, यह इस बात पर निर्भर करता है कि आपकी मशीन दो कार्यों को कितनी तेजी से शुरू करने में सक्षम है।

आप किसी भी फाइल डिस्क्रिप्टर का उपयोग कर सकते हैं, जरूरी नहीं कि 0. फाइल डिस्क्रिप्टर के लिए खोली गई फाइल पर लॉक हो , डिस्क्रिप्टर ही नहीं।

( flock -x 9 || exit 1
  echo 'Locking for 5 secs'; sleep 5; echo 'Done' ) 9>/tmp/lock &
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.