2012-07-23 13 views
8

मुझे एक स्थान से दूसरे स्थान पर एक फ़ाइल कॉपी करने की आवश्यकता है, और यदि फ़ाइल पहले से मौजूद है (कोई ओवरराइटिंग नहीं है) तो मुझे अपवाद (या कम से कम किसी भी तरह पहचानने) की आवश्यकता है।एक सुरक्षित, परमाणु फ़ाइल-प्रति ऑपरेशन

मैं os.path.exists() के साथ पहले जांच सकता हूं लेकिन यह बेहद महत्वपूर्ण है कि फाइल को जांच और प्रतिलिपि के बीच थोड़ी सी मात्रा में नहीं बनाया जा सकता है।

क्या ऐसा करने का एक अंतर्निहित तरीका है, या परमाणु के रूप में एक क्रिया को परिभाषित करने का कोई तरीका है?

+0

क्या यह केवल गंतव्य का निर्माण है जिसे परमाणु होने की आवश्यकता है, लेकिन स्रोत सामग्री को पढ़ने के रूप में, केवल एक बिंदु-बिंदु का प्रतिनिधित्व करते हैं? –

+0

बस सृजन। मैं एक प्रोग्राम लिख रहा हूं जो ज़ोन फ़ाइल को/tmp पर प्रतिलिपि बनाता है, आवश्यक परिवर्तन करता है, और उसके बाद इसे अंत में कॉपी करता है। मुझे बस यह सुनिश्चित करने की ज़रूरत है कि दो लोग एक ही समय में कोशिश करें और संपादित करें, उनमें से एक अपने परिवर्तनों को खो नहीं देता है। – Rory

+2

ध्यान रखें कि 'नाम बदलें() 'केवल परमाणु है यदि स्रोत और गंतव्य एक ही फाइल सिस्टम पर हैं - तो हो सकता है कि आप गंतव्य निर्देशिका में अपनी अस्थायी फ़ाइल बनाना चाहें, न कि'/tmp' में। –

उत्तर

7

वहाँ यह करने के लिए एक तरह से वास्तव में है, atomically और सुरक्षित रूप से, प्रदान की सभी कलाकार यह उसी तरह है। क्या करें

  1. चेक फ़ाइल पहले से मौजूद है या नहीं)

    , यह lock-free whack-a-mole algorithm पर आधारित एक फिल्म है, और पूरी तरह से तुच्छ नहीं है, इसलिए सामान्य जवाब के रूप में साथ जाने के लिए "नहीं" के लिए स्वतंत्र महसूस। अगर ऐसा होता है तो रोको।

  2. Generate a unique ID
  3. स्रोत फ़ाइल को एक अस्थायी नाम के साथ लक्षित फ़ोल्डर में कॉपी करें, कहें, <target>.<UUID>.tmp
  4. का नाम बदलें प्रतिलिपि <target>-<UUID>.mole.tmp
  5. Look for any other files matching the pattern<target>-*.mole.tmp
    • यदि उनकी यूयूआईडी आपकी तुलना में अधिक है, attempt to delete it। (चिंता न करें अगर यह चला गया है।)
    • यदि उनकी यूयूआईडी आपकी तुलना में कम है, तो अपना खुद का हटाने का प्रयास करें। (फिर, चिंता न करें अगर यह चला गया है।) अब से, उनके यूयूआईडी का इलाज करें जैसे कि यह आपका स्वयं का था।
  6. यह देखने के लिए फिर से जांचें कि गंतव्य फ़ाइल पहले से मौजूद है या नहीं। यदि ऐसा है, तो अपनी अस्थायी फ़ाइल को हटाने का प्रयास करें। (चिंता न करें अगर यह खत्म हो गया है। याद रखें कि आपका यूयूआईडी चरण 5 में बदल सकता है।)
  7. यदि आपने पहले चरण 6 में इसे हटाने का प्रयास नहीं किया है, तो अपने अस्थायी फ़ाइल को अपने अंतिम नाम, <target> पर पुनर्नामित करने का प्रयास करें। (चिंता न करें अगर यह खत्म हो गया है, तो बस चरण 5 पर वापस जाएं।)

आप कर चुके हैं!

यह कैसे काम करता

प्रत्येक उम्मीदवार स्रोत फ़ाइल कल्पना कीजिए एक तिल अपने छेद से बाहर आ रहा है। आधे रास्ते से बाहर, यह किसी अन्य मोल को पूरी तरह से उभरा है, इससे पहले कि यह किसी भी प्रतिस्पर्धी मोल को जमीन पर वापस रोक देता है और फट जाता है। यदि आप इसे अपने सिर में चलाते हैं, तो आपको देखना चाहिए कि केवल एक तिल ही इसे हर तरह से बाहर कर देगा। इस प्रणाली को livelocking से रोकने के लिए, हम कुल ऑर्डरिंग जोड़ते हैं जिस पर तिल जा सकता है। बैम! एक   पीएचडी थीसिस   lock-free algorithm

चरण 4 अनावश्यक — क्यों न सिर्फ पहली जगह में है कि नाम का उपयोग लग सकता है? हालांकि, एक और प्रक्रिया चरण 0 में आपके   तिल   फ़ाइल को "अपनाने" दे सकती है, और इसे चरण 7 में विजेता बना सकती है, इसलिए यह बहुत महत्वपूर्ण है कि आप अभी भी सामग्री लिख रहे हैं! उसी फ़ाइल सिस्टम पर नामनाम परमाणु हैं, इसलिए चरण 4 सुरक्षित है।

+1

यह एक सुंदर एल्गोरिदम है। मुझे नहीं लगता कि मैं इसे आईआरएल करूँगा लेकिन दृष्टिकोण सरल है। – Rory

+0

क्या यह यूयूआईडी पर एक वृद्धिशील मूल्य का उत्पादन करता है? – coolfeature

+0

@coolfeature नहीं, ऑर्डरिंग केवल यह सुनिश्चित करने के लिए है कि विजेता अंततः चुना जाता है। –

11

ऐसा करने का कोई तरीका नहीं है; फ़ाइल कॉपी ऑपरेशन कभी परमाणु नहीं होते हैं और उन्हें बनाने का कोई तरीका नहीं है।

लेकिन आप फ़ाइल को यादृच्छिक, अस्थायी नाम के तहत लिख सकते हैं और फिर का नाम बदल सकते हैं। नाम बदलें नाम परमाणु होना चाहिए। यदि फ़ाइल पहले से मौजूद है, तो नाम विफल हो जाएगा और आपको एक त्रुटि मिलेगी।

[EDIT2]rename() केवल परमाणु है अगर आप एक ही फाइल सिस्टम में करते हैं। सुरक्षित तरीका गंतव्य के समान फ़ोल्डर में नई फ़ाइल बनाना है।

[संपादित करें] बहुत सी चर्चा है कि नाम हमेशा परमाणु है या नहीं और ओवरराइट व्यवहार के बारे में है। तो मैंने कुछ संसाधन खोद दिए।

लिनक्स पर, यदि गंतव्य मौजूद है और स्रोत और गंतव्य दोनों फाइलें हैं, तो गंतव्य चुपचाप ओवरराइट किया गया है (man page)। तो मैं वहां गलत था।

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

विंडोज़ पर, यदि कोई अन्य प्रक्रिया वर्तमान में फ़ाइल लिख रही है, तो यदि आप इसे लिखने के लिए खोलने का प्रयास करते हैं तो आपको एक त्रुटि मिलती है, इसलिए विंडोज के लिए एक फायदा।

यदि आपका कंप्यूटर डिस्क पर ऑपरेशन के दौरान क्रैश हो जाता है, तो फ़ाइल सिस्टम का कार्यान्वयन यह तय करेगा कि डेटा कितना दूषित हो जाता है। कुछ भी कोई एप्लिकेशन इसके बारे में नहीं कर सकता है। तो पहले से ही रोना बंद करें :-)

कोई अन्य दृष्टिकोण भी नहीं है जो बेहतर काम करता है या साथ ही साथ यह भी।

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

आप open(2) मोड O_CREAT के साथ उपयोग कर सकते हैं जो फ़ाइल को पहले से मौजूद होने पर फ़ंक्शन विफल कर देगा। लेकिन यह फ़ाइल को हटाने और अपनी प्रतिलिपि लिखने की दूसरी प्रक्रिया को रोक नहीं पाएगा।

या निर्देशिका बनाने के बाद से आप लॉक निर्देशिका बना सकते हैं, साथ ही साथ परमाणु होना चाहिए। लेकिन वह आपको ज्यादा खरीद नहीं करेगा, या तो। आपको लॉकिंग कोड स्वयं लिखना होगा और बिल्कुल सही बनाना होगा, 100% सुनिश्चित करें कि आप वास्तव में, आपदा के मामले में वास्तव में हमेशा लॉक निर्देशिका को हटा दें - जो आप नहीं कर सकते।

+0

केवल तभी जब आप नामकरण से पहले फ्लश और सिंक करते हैं। – dcolish

+0

जिज्ञासा से, इस काम का नाम बदलने वाला हिस्सा कैसा होगा? यूनिक्स पर 'os.rename' काम नहीं करेगा। – mgilson

+0

@dcolish सिंक करने की आवश्यकता नहीं है - फ्लशिंग या 'क्लोज़() 'आईएनजी पर्याप्त है। –

संबंधित मुद्दे