2016-12-31 6 views
8

यदि आप पृष्ठभूमि चाहते हैं, तो here देखें। संक्षेप में, सवाल यह है: " से bracket (mallocBytes n) free और allocaBytes के बीच वास्तविक अंतर क्या है"।"ब्रैकेट (mallocBytes n) मुक्त" और "allocaBytes" के बीच क्या अंतर है?

आम तौर पर सी, alloca स्टैक पर आवंटित और malloc ढेर पर आवंटित करता है। मुझे यकीन नहीं है कि हास्केल में इसके साथ क्या चल रहा है, लेकिन मैं गति के अलावा उपर्युक्त समीकरणों के बीच एक अंतर की अपेक्षा नहीं करता। यदि आपने पृष्ठभूमि लिंक पर क्लिक किया है, तो आप जानते हैं कि संकलित कोड bracket (mallocBytes n) free के परिणामस्वरूप "डबल फ्री या भ्रष्टाचार" होता है जबकि allocaBytes ठीक काम करता है (समस्या तब दिखाई नहीं दे रही है जब जीएचसीआई में, सबकुछ दोनों मामलों में ठीक काम करता है)।

अब तक मैंने दर्दनाक डिबगिंग में दो दिन बिताए हैं और मुझे पूरा भरोसा है कि bracket (mallocBytes n) free किसी भी तरह अस्थिर था और शेष कोड विश्वसनीय है। मैं यह जानना चाहता हूं कि bracket (mallocBytes n) free के साथ क्या सौदा है।

+1

क्या यह संभव है कि बफर * दोनों स्थितियों में ओवरराउन हो रहा है, और यह सिर्फ 'mallocBytes' और' free' त्रुटि को पकड़ता है जबकि दूसरा चुप भ्रष्टाचार की ओर जाता है? ऐसा प्रतीत होता है कि 'एलोकाबाइट्स' अंततः जीएचसी आदिम 'newPinnedByteArray # 'का उपयोग करता है, इसलिए यह एक साधारण' मॉलोक 'कॉल प्रतीत नहीं होता है। –

+0

मुझे नहीं लगता कि बफर बिल्कुल खत्म हो रहा है। मेरे पास अब परीक्षण हैं जो एन्कोडिंग/डिकोडिंग के माध्यम से पुष्टि करते हैं और WAVE डेटा ब्लॉक की तुलना करते हैं जो एन्कोडर और डिकोडर बाइंडिंग सही ढंग से काम करते हैं और डेटा को डीकोड किए गए डेटा को मूल से मेल खाते हैं। यदि बफर खत्म हो गया था, तो यह संभवतः डेटा भ्रष्टाचार का कारण बन जाएगा। – Mark

+0

जरूरी नहीं है। "अपरिभाषित व्यवहार", ठीक है, अपरिभाषित है। यह काम कर सकता है, यह आपकी बिल्ली को आग लगा सकता है। ध्यान दें कि 'फ्री' का एक डबल उपयोग आमतौर पर मेमोरी भ्रष्टाचार दिखाता है, न कि उस स्मृति स्थान पर 'फ्री' का वास्तविक दूसरा उपयोग। – Zeta

उत्तर

12

bracket (mallocBytes size) free सी malloc और free, जबकि allocaBytes size का उपयोग करेगा स्मृति कि GHCs कचरा संग्रहण द्वारा प्रबंधित का प्रयोग करेंगे। अपने आप में यही है, पहले से ही एक बड़ा अंतर है के बाद से allocaBytes की Ptr अप्रयुक्त (लेकिन आवंटित) स्मृति से घिरा हो सकता है:

import Control.Exception 
import Control.Monad (forM_) 
import Foreign.Marshal.Alloc 
import Foreign.Ptr 
import Foreign.Storable 

-- Write a value at an invalid pointer location 
hammer :: Ptr Int -> IO() 
hammer ptr = pokeElemOff ptr (-1) 0 >> putStrLn "hammered" 

main :: IO() 
main = do 
    putStrLn "Hammer time! Alloca!" 
    forM_ [1..10] $ \n -> 
    print n >> allocaBytes 10 hammer 

    putStrLn "Hammer time! Bracket" 
    forM_ [1..10] $ \n -> 
    print n >> bracket (mallocBytes 10) free hammer 

परिणाम:

Hammer time! Alloca! 
1 
hammered 
2 
hammered 
3 
hammered 
4 
hammered 
5 
hammered 
6 
hammered 
7 
hammered 
8 
hammered 
9 
hammered 
10 
hammered 
Hammer time! Bracket 
1 
hammered 
<program crashes> 

आप देख सकते हैं, हालांकि हम है arr[-1] = 0, allocaBytes ने उस त्रुटि को खुशी से अनदेखा किया। हालांकि, free यदि आप -1 पर स्थिति लिखते हैं तो आपके चेहरे में अक्सर उड़ जाएगा (अक्सर)। अगर किसी अन्य आवंटित स्मृति क्षेत्र * पर स्मृति भ्रष्टाचार हो तो यह आपके चेहरे पर भी उड़ाएगा।

इसके अलावा, allocaBytes के साथ, यह संभावना है कि पॉइंटर कहीं पहले से आवंटित स्मृति में इंगित करता है, न कि किसी की शुरुआत के लिए, उदा।

nursery = malloc(NURSERY_SIZE); 

// ... 

pointer_for_user = nursery + 180; 

// pointer_for_user[-1] = 0 is not as 
// much as a problem, since it doesn't yield undefined behaviour 

इसका क्या अर्थ है? खैर, allocaBytes आपके चेहरे पर उड़ने की संभावना कम है, लेकिन लागत पर आपको यह नहीं पता कि आपका सी-कोड संस्करण स्मृति भ्रष्टाचार का कारण बन जाएगा या नहीं। इससे भी बदतर, जैसे ही आप allocaBytes द्वारा लौटे गए सीमाओं के बाहर लिखते हैं, तो संभवतः आप अन्य Haskell चुपचाप मानते हैं।

हालांकि, हम यहां अपरिभाषित व्यवहार के बारे में बात कर रहे हैं। उपरोक्त कोड आपके सिस्टम पर क्रैश हो सकता है या नहीं। यह allocaBytes भाग में भी क्रैश हो सकता है।

यदि मैं आप थे, तो मैं trace the malloc and free calls होगा।


* मैं एक बार मेरे प्रोग्राम के बीच में त्रुटि एक "मुक्त के दोहरे उपयोग" था। सब कुछ डीबग किया, अधिकांश "खराब" दिनचर्या को फिर से लिखें। दुर्भाग्यवश, डीबग बिल्ड में गायब हो गई त्रुटि, लेकिन रिलीज बिल्ड में पुनरावृत्ति हुई। यह पता चला कि main की पहली दस पंक्तियों में, मैंने b[i - 1] को i = 0 के साथ गलती से लिखा था।

+0

कॉल का पता लगाने के बारे में अच्छा बिंदु, मैं इसे आज़माउंगा। – Mark

+1

आप सही थे, मैंने फ्रेम आकार और ब्लॉक आकार को मिश्रित किया, और चूंकि ब्लॉक आकार अक्सर फ्रेम आकार से अधिक होता है, बफर की सीमा पर लिखे गए कुछ बाइट थे। – Mark

5

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

withBuffer :: Int -> (Ptr a -> IO b) -> IO b 
withBuffer n = bracket begin end 
    where begin = do 
      a <- mallocBytes (n + 4096) 
      mapM_ (\i -> pokeByteOff (a `plusPtr` n) i (0xa5 :: Word8)) [0..4095] 
      return a 
     end = \a -> do 
      page <- mapM (\i -> peekByteOff (a `plusPtr` n) i) [0..4095] 
      when (any (/= (0xa5 :: Word8)) page) $ do 
      putStrLn $ unlines $ map (hexline page) [0,16..4095] 
      error "corruption detected" 
      free a 
     hexline bytes off = unwords . map hex . take 16 . drop off $ bytes 
     hex = printf "%02x" 
+0

आपकी मदद के लिए धन्यवाद, मुझे लगता है कि मुझे पता है कि अब कहां देखना है ;-) – Mark

+0

आह, एक अच्छी पुरानी कैनरी। वैसे, मैं शब्द बदल दूंगा, क्योंकि आपका कोड "कोई पृष्ठ नहीं जोड़ता" (अतिरिक्त स्मृति पृष्ठ संरेखण पर शुरू नहीं हो सकता है)। साथ ही, आप 'हेक्सलाइन' को' हेक्सलाइन बाइट्स ऑफ = unwords से सरल बना सकते हैं। नक्शा हेक्स। 16 ले लो $ बाइट्स ड्रॉप करें। – Zeta

+0

धन्यवाद, यह किया गया। –

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