2009-03-20 3 views
7

मैं EXT4 पर "बग" पर चर्चा का पालन कर रहा हूं जो फ़ाइलों को क्रैश में शून्य करने का कारण बनता है यदि कोई "temp फ़ाइल बनाएं, temp फ़ाइल लिखें, फ़ाइल को लक्षित करने के लिए temp का नाम बदलें" प्रक्रिया का उपयोग करता है। POSIX का कहना है कि जब तक fsync() कहा जाता है, तो आप यह सुनिश्चित नहीं कर सकते कि डेटा हार्डडिस्क पर पहुंचा दिया गया है।POSIX सिस्टम पर एकाधिक फ़ाइलों को संशोधित करने के लिए सुरक्षित और कुशल तरीका?

जाहिर कर रही है:

0) get the file contents (read it or make it somehow) 
1) open original file and truncate it 
2) write new contents 
3) close file 

भी fsync() के रूप में कंप्यूटर 2 के दौरान दुर्घटना कर सकते हैं) या fsync() के साथ अच्छा नहीं है और आप आंशिक रूप से लिखा फ़ाइल के साथ खत्म।

आमतौर पर यह सोचा गया है कि यह बहुत सुरक्षित है:

0) get the file contents (read it or make it somehow) 
1) open temp file 
2) write contents to temp file 
3) close temp file 
4) rename temp file to original file 

दुर्भाग्य से ऐसा नहीं है। यह EXT4 पर सुरक्षित बनाने के लिए आपको बस इतना करना होगा:

0) get the file contents (read it or make it somehow) 
1) open temp file 
2) write contents to temp file 
3) fsync() 
4) close temp file 
5) rename temp file to original file 

यह सुरक्षित होगा और दुर्घटना पर आप या तो नई फ़ाइल सामग्री या पुराने, कभी नहीं ध्यान केंद्रित किया सामग्री या आंशिक सामग्री होनी चाहिए। लेकिन अगर एप्लिकेशन बहुत सारी फाइलों का उपयोग करता है, तो प्रत्येक लिखने के बाद fsync() धीमा हो जाएगा।

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

संपादित करें: fsync() बंद temp फ़ाइल को कोरेंट ऑर्डर में बदल दिया है, कई कई फाइलें लिखने पर जोर दिया गया है।

उत्तर

0

आपको अपनी अंतिम लिस्टिंग में 0 4 स्वैप करने की आवश्यकता है - fsync(fd) फ़ाइल डिस्क्रिप्टर का उपयोग करता है। और मुझे नहीं लगता कि यह विशेष रूप से महंगा क्यों होगा - आप चाहते हैं कि डिस्क को डिस्क() को वैसे भी लिखा जाए। तो आप जो भी करना चाहते हैं उसके बीच लागत वही होगी और fsync() के साथ क्या होगा।

लागत (और तुम्हारे पास है) fdatasync(2) बचने मेटा डेटा सिंक कर रहा है बहुत ज्यादा, है, इसलिए हल्का लागत होना चाहिए है।

संपादित करें: तो मैं कुछ अत्यंत hacky परीक्षण कोड लिखा है:

0.595782 
6.338329 
6.116894 

कौन कर fsync() है ~ 10 गुना अधिक महंगा पता चलता है:

#include <unistd.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <sys/time.h> 
#include <time.h> 
#include <stdio.h> 
#include <string.h> 

static void testBasic() 
{ 
    int fd; 
    const char* text = "This is some text"; 

    fd = open("temp.tmp", O_WRONLY | O_CREAT); 
    write(fd,text,strlen(text)); 
    close(fd); 
    rename("temp.tmp","temp"); 
} 

static void testFsync() 
{ 
    int fd; 
    const char* text = "This is some text"; 

    fd = open("temp1", O_WRONLY | O_CREAT); 
    write(fd,text,strlen(text)); 
    fsync(fd); 
    close(fd); 
    rename("temp.tmp","temp"); 
} 

static void testFdatasync() 
{ 
    int fd; 
    const char* text = "This is some text"; 

    fd = open("temp1", O_WRONLY | O_CREAT); 
    write(fd,text,strlen(text)); 
    fdatasync(fd); 
    close(fd); 
    rename("temp.tmp","temp"); 
} 

#define ITERATIONS 10000 

static void testLoop(int type) 
{ 
    struct timeval before; 
    struct timeval after; 
    long seconds; 
    long usec; 
    int i; 

    gettimeofday(&before,NULL); 
    if (type == 1) 
    { 
     for (i = 0; i < ITERATIONS; i++) 
     { 
      testBasic(); 
     } 
    } 
    if (type == 2) 
    { 
     for (i = 0; i < ITERATIONS; i++) 
     { 
      testFsync(); 
     } 
    } 
    if (type == 3) 
    { 
     for (i = 0; i < ITERATIONS; i++) 
     { 
      testFdatasync(); 
     } 
    } 
    gettimeofday(&after,NULL); 

    seconds = (long)(after.tv_sec - before.tv_sec); 
    usec = (long)(after.tv_usec - before.tv_usec); 
    if (usec < 0) 
    { 
     seconds--; 
     usec += 1000000; 
    } 

    printf("%ld.%06ld\n",seconds,usec); 
} 

int main() 
{ 
    testLoop(1); 
    testLoop(2); 
    testLoop(3); 
    return 0; 
} 

अपने लैपटॉप है कि पैदा करता है पर। और fdatasync() थोड़ा सस्ता है।

मुझे लगता है कि समस्या मैं देख रहा हूँ कि हर आवेदन यह डेटा है सोचने के लिए जा रहा है है बहुत महत्वपूर्ण fsync करने के लिए() है, इसलिए एक मिनट से अधिक राईट विलय के प्रदर्शन फायदे का सफाया कर दिया जाएगा।

+0

नाम बदलने के साथ कोई fsync() के बिना 100,000 कॉन्फ़िगरेशन फ़ाइलों को लिख सकता है, और 100.000 fsync() धीमा हो जाएगा। – Raynet

+0

"आप चाहते हैं कि डिस्क को डिस्क पर लिखा गया हो() वैसे भी" आप किस बारे में बात कर रहे हैं? बंद केवल POSIX के अनुसार फ़ाइल विवरण को रद्द करना है। बफर फ्लश करने की कोई आवश्यकता नहीं है। http://pubs.opengroup.org/onlinepubs/9699919799/functions/close।एचटीएमएल – ArekBulski

1

मेरा स्वयं का जवाब अस्थायी फ़ाइलों पर संशोधनों को बनाए रखना होगा, और उन्हें सभी लिखने के बाद, एक fsync() करें और फिर उन सभी का नाम बदलें।

+1

fsync() प्रति-एफडी है - शायद आप सिंक() के बारे में सोच रहे हैं? –

+0

मुझे लगता है कि यह एक बेंचमार्क के लिए समय है - आप एक क्यों नहीं लिखते हैं, और हम देखेंगे कि प्रभाव क्या है? –

+0

मैंने एक त्वरित बेंचमार्क किया था, लिखने के बाद प्रत्येक फ़ाइल के लिए नाम बदलें परिदृश्य fsync() के साथ 10-20% धीमा है। मुझे लगता है कि fsync() सही आदेश है क्योंकि यह सिर्फ उस फ़ाइल को फ्लश करता है जिसे मैंने अभी लिखा है, मैं कुछ और फ्लश नहीं करना चाहता हूं। – Raynet

3

संक्षिप्त उत्तर यह है: ऐप परत में इसे हल करना गलत जगह है। EXT4 को यह सुनिश्चित करना होगा कि फ़ाइल बंद करने के बाद, डेटा समय-समय पर लिखा गया है।जैसा कि अब है, EXT4 इस लेखन को "लिखने" को अधिक लिखने के अनुरोधों को इकट्ठा करने में सक्षम बनाता है और उन्हें एक बार में बाहर निकाल देता है।

समस्या स्पष्ट है: कोई फर्क नहीं पड़ता कि आप क्या करते हैं, आप यह सुनिश्चित नहीं कर सकते कि आपका डेटा डिस्क पर समाप्त हो। कॉलिंग fdisk() मैन्युअल रूप से केवल चीजों को और खराब बनाता है: आप मूल रूप से EXT4 के अनुकूलन के रास्ते में आते हैं, जिससे पूरे सिस्टम को धीमा कर दिया जाता है।

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

EXT4 फाइल सिस्टम की आभासी प्रतिलिपि बना सकता है जिसमें डिस्क संशोधित नहीं होने पर परिवर्तन शामिल हैं (अभी तक)। लेकिन यह अंतिम लक्ष्य को प्रभावित नहीं करता है: एक ऐप नहीं जानता कि एफएस क्या करने जा रहा है और इसलिए, एफएस को यह सुनिश्चित करना होगा कि यह अपना काम करे।

यह एक ऐसा मामला है जहां निर्दयी अनुकूलन बहुत दूर चला गया है और परिणामों को बर्बाद कर दिया गया है। स्वर्ण नियम: अनुकूलन को अंतिम परिणाम कभी नहीं बदलना चाहिए। यदि आप इसे बनाए नहीं रख सकते हैं, तो आपको अनुकूलित नहीं करना चाहिए।

जब तक त्सो का मानना ​​है कि यह एक तेजी से एफएस के बजाय एक जो सही ढंग से कार्य करने के लिए अधिक महत्वपूर्ण है, मैं EXT4 में नवीनीकृत और सभी बग रिपोर्ट को बंद के बारे में इस "काम करता है के रूप में त्सो द्वारा डिजाइन" है के लिए नहीं सुझाव देते हैं।

[संपादित करें] इस पर कुछ और विचार। आप फ़ाइल के बजाय डेटाबेस का उपयोग कर सकते हैं। चलिए एक पल के लिए संसाधन अपशिष्ट को अनदेखा करते हैं। क्या कोई गारंटी दे सकता है कि डेटाबेस, जो डेटाबेस का उपयोग करता है, दुर्घटनाग्रस्त हो जाएगा? शायद। डेटाबेस डेटा लिख ​​सकता है और हर मिनट या तो fsync() को कॉल कर सकता है। लेकिन फिर, आप वही कर सकते हैं:

while True; do sync ; sleep 60 ; done 

फिर से, एफएस में बग इसे हर मामले में काम करने से रोकती है। अन्यथा, लोग इस बग से इतना परेशान नहीं होंगे।

आप Windows रजिस्ट्री की तरह पृष्ठभूमि कॉन्फ़िगरेशन डिमन का उपयोग कर सकते हैं। डेमॉन एक बड़ी फाइल में सभी विन्यास लिखेंगे। यह सबकुछ लिखने के बाद fsync() को कॉल कर सकता है। आपकी कॉन्फ़िगरेशन के लिए समस्या हल हो गई है। अब आपको अपने ऐप्स लिखने वाले अन्य सभी चीज़ों के लिए ऐसा करने की ज़रूरत है: टेक्स्ट दस्तावेज़, छवियां, जो भी हो। मेरा मतलब है कि लगभग कोई यूनिक्स प्रक्रिया एक फाइल बनाता है। यह पूरे यूनिक्स विचार का मजाकिया आधार है!

स्पष्ट रूप से, यह एक व्यवहार्य मार्ग नहीं है। तो जवाब बनी हुई है: आपकी तरफ कोई समाधान नहीं है। जब तक वे अपनी बग ठीक नहीं करते हैं तब तक त्सो और अन्य एफएस डेवलपर्स को परेशान करते रहें।

+0

ठीक है, मैं अभी भी ऐसा करने के लिए एक समाधान देख रहा हूं, मैं इस व्यवहार पर निर्भर नहीं होना चाहता हूं जिसे spec पर परिभाषित नहीं किया गया है। – Raynet

+0

रेनेट, त्सो ने कुछ लिखा है जो काम नहीं करता है। इस मुद्दे को ठीक करने तक आप ऐसा कुछ नहीं कर सकते हैं। –

+0

शायद, लेकिन मैं अभी भी कोड रखना पसंद करूंगा जो काम करता है और इस पर निर्भर नहीं करता कि कैसे त्सो या किसी और ने पॉज़िक्स स्पेक पढ़ा है। – Raynet

0

मुद्दा आप अच्छी तरह से छानबीन कर रहा है का उल्लेख है, तो आप निश्चित रूप से इस पढ़ना चाहिए: https://www.academia.edu/9846821/Towards_Efficient_Portable_Application-Level_Consistency

fsync सुरक्षित नाम बदलने व्यवहार और निर्देशिका fsync सुरक्षित नई फ़ाइल व्यवहार के तहत छोड़ा जा सकता है के तहत छोड़ा जा सकता है। दोनों कार्यान्वयन विशिष्ट हैं और POSIX द्वारा गारंटी नहीं है।

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

  • कोई संबंधित समस्या नहीं^_^