2010-03-22 12 views
8

साझा स्मृति का उपयोग करते समय, प्रत्येक प्रक्रिया साझा क्षेत्र को अपने संबंधित पता स्थान के एक अलग क्षेत्र में एमएमएपी कर सकती है। इसका अर्थ यह है कि साझा क्षेत्र के भीतर पॉइंटर्स को संग्रहीत करते समय, आपको साझा क्षेत्र की शुरुआत के store them as offsets की आवश्यकता होती है। दुर्भाग्यवश, यह परमाणु निर्देशों का उपयोग जटिल करता है (उदाहरण के लिए यदि आप lock free algorithm लिखने की कोशिश कर रहे हैं)। उदाहरण के लिए, कहें कि आपके पास एक एकल लेखक द्वारा बनाई गई साझा स्मृति में संदर्भ गिनती नोड्स का एक गुच्छा है। लेखक समय-समय पर सकारात्मक संदर्भ गणना के साथ एक मान्य नोड को इंगित करने के लिए परमाणु रूप से एक सूचक 'पी' अद्यतन करता है। पाठक परमाणु रूप से 'पी' लिखना चाहते हैं क्योंकि यह नोड (एक संरचना) की शुरुआत को इंगित करता है जिसका पहला तत्व संदर्भ संख्या है। चूंकि पी हमेशा एक वैध नोड को इंगित करता है, इसलिए रेफरी गिनती बढ़ाना सुरक्षित है, और इसे 'पी' को सुरक्षित करने और अन्य सदस्यों तक पहुंचने में सुरक्षित बनाता है। हालांकि, यह सब केवल तभी काम करता है जब सबकुछ एक ही पता स्थान पर होता है। नोड्स और 'पी' सूचक साझा स्मृति में जमा हो जाती है, तो ग्राहकों को एक रेस स्थिति भुगतना: = x + y पर ऑफसेट क्या ऑफसेट का उपयोग किये बिना साझा मेमोरी में पॉइंटर्स स्टोर करना संभव है?

  • वृद्धि refcount

    1. एक्स = पढ़ पी
    2. y

    चरण 2 के दौरान, पी बदल सकता है और x अब मान्य नोड को इंगित नहीं कर सकता है। एकमात्र कामकाज मैं सोच सकता हूं कि किसी भी तरह से सभी प्रक्रियाओं को साझा स्मृति को मैप करने के लिए सहमत होने के लिए मजबूर करना है, ताकि ऑफसेट्स के बजाय असली पॉइंटर्स mmap'd क्षेत्र में संग्रहीत किए जा सकें। क्या उसे करने का कोई तरीका है? मैं mmap दस्तावेज में MAP_FIXED देखता हूं, लेकिन मुझे नहीं पता कि मैं एक ऐसा पता कैसे चुन सकता हूं जो सुरक्षित होगा।

    संपादित करें: x86 पर इनलाइन असेंबली और 'लॉक' उपसर्ग का उपयोग करना संभवतः "जेड द्वारा ऑफसेट वाई के साथ वृद्धि पीआरटी एक्स" बनाना संभव है? अन्य आर्किटेक्चर पर समतुल्य विकल्प? बहुत सारी सभाएं नहीं लिखी हैं, यह नहीं पता कि आवश्यक निर्देश मौजूद हैं या नहीं।

  • उत्तर

    3

    निम्न स्तर पर 86 परमाणु inctruction एक ही बार में यह सब पेड़ चरणों कर सकते हैं:

    1. एक्स = पी पढ़
    2. y = x + वृद्धि ऑफसेट
    3. y पर refcount
    // 
         mov edi, Destination 
         mov edx, DataOffset 
         mov ecx, NewData 
    @Repeat: 
         mov eax, [edi + edx] //load OldData 
    //Here you can also increment eax and save to [edi + edx]   
         lock cmpxchg dword ptr [edi + edx], ecx 
         jnz @Repeat 
    // 
    
    +1

    यदि cmpxchg पहले से ही परमाणु पढ़ने और परमाणु लेखन करता है, तो 'लॉक' आवश्यक है? या यह सुनिश्चित करता है कि edi + edx परमाणु रूप से किया जाता है? मैंने कभी भी वास्तव में एमआईपीएस असेंबली का उपयोग किया है। –

    +0

    मेमोरी बस पर परमाणु पहुंच की गारंटी लॉक करें ताकि लॉक निर्देश आवश्यक हो। आप शायद एपीआई इंटरलाक्ड कॉम्पैयर एक्सचेंज का भी उपयोग कर सकते हैं (स्पष्टीकरण के लिए एमएसडीएन जांचें)। पहले लोड करें 32 बिट पॉइंटर को OldValue के रूप में और न्यूवैल्यू प्राप्त करने के लिए इसे बढ़ाने से पहले, इंटरलॉक कॉम्पैयर एक्सचेंज करने का प्रयास करें। InterlockedCompareExchange (गंतव्य + ऑफ़सेट, न्यूवेल्यू, ओल्डवैल्यू) तुलनात्मक मूल्य वापस कर देगा यदि पुराने धागे के समान नहीं है, तो कुछ अन्य धागे की तुलना में इसका आदान-प्रदान किया जाता है, इसलिए कोई विनिमय नहीं किया गया था और आपको प्रक्रिया को दोहराना होगा। –

    2

    हमारे पास कोड है जो आपकी समस्या का विवरण जैसा है। हम एक मेमोरी-मैप की गई फ़ाइल, ऑफ़सेट और फ़ाइल लॉकिंग का उपयोग करते हैं। हमें कोई विकल्प नहीं मिला है।

    3

    यह एक पर मामूली बात है यूनिक्स प्रणाली; बस साझा स्मृति कार्यों का उपयोग:

    shgmet, Shmat, shmctl, shmdt

    शून्य * Shmat (पूर्णांक Shmid, स्थिरांक शून्य * shmaddr, पूर्णांक shmflg);

    Shmat() साझा स्मृति खंड बुला प्रक्रिया के पता स्थान को Shmid से पहचान जुड़ जाता है। संलग्न पता निम्नलिखित मानदंडों में से एक के साथ shmaddr द्वारा निर्दिष्ट किया जाता:

    तो shmaddr शून्य है, तो सिस्टम चुनता एक उपयुक्त (अप्रयुक्त) पता जिस पर खंड संलग्न करने के लिए।

    बस अपना खुद का पता यहां निर्दिष्ट करें; जैसे 0x20000000000

    आप shmget हैं() हर प्रक्रिया में एक ही कुंजी और आकार का उपयोग करते हुए, आप एक ही साझा स्मृति खंड मिल जाएगा। यदि आप एक ही पते पर shmat(), वर्चुअल पते सभी प्रक्रियाओं में समान होंगे। कर्नेल इस बात पर परवाह नहीं करता कि आप किस पते का उपयोग करते हैं, जब तक कि यह सामान्य रूप से चीजों को निर्दिष्ट करता है, जहां तक ​​यह संघर्ष नहीं करता है।

    लिनक्स पर, सुनिश्चित करें जड़ बनाने, (नई [] भी ढेर पर जाँचें और malloc (से लौटे)/आप पता को छोड़ दें, तो आप सामान्य क्षेत्र है कि यह चीजों को डाल करने के लिए पसंद करती है देख सकते हैं।) SHMMAX को/proc/sys/कर्नेल/shmmax में आपके साझा मेमोरी सेगमेंट को समायोजित करने के लिए बड़ी संख्या में सेट करता है (डिफ़ॉल्ट 32 एमबी है)।

    परमाणु संचालन के लिए, आप उन्हें सभी लिनक्स कर्नेल स्रोत से प्राप्त कर सकते हैं, उदा।

    शामिल/ASM-86/atomic_64.h

    /* 
    * Make sure gcc doesn't try to be clever and move things around 
    * on us. We need to use _exactly_ the address the user gave us, 
    * not some alias that contains the same information. 
    */ 
    typedef struct { 
         int counter; 
    } atomic_t; 
    
    /** 
    * atomic_read - read atomic variable 
    * @v: pointer of type atomic_t 
    * 
    * Atomically reads the value of @v. 
    */ 
    #define atomic_read(v)   ((v)->counter) 
    
    /** 
    * atomic_set - set atomic variable 
    * @v: pointer of type atomic_t 
    * @i: required value 
    * 
    * Atomically sets the value of @v to @i. 
    */ 
    #define atomic_set(v, i)    (((v)->counter) = (i)) 
    
    
    /** 
    * atomic_add - add integer to atomic variable 
    * @i: integer value to add 
    * @v: pointer of type atomic_t 
    * 
    * Atomically adds @i to @v. 
    */ 
    static inline void atomic_add(int i, atomic_t *v) 
    { 
         asm volatile(LOCK_PREFIX "addl %1,%0" 
            : "=m" (v->counter) 
            : "ir" (i), "m" (v->counter)); 
    } 
    

    64-बिट संस्करण:

    typedef struct { 
         long counter; 
    } atomic64_t; 
    
    /** 
    * atomic64_add - add integer to atomic64 variable 
    * @i: integer value to add 
    * @v: pointer to type atomic64_t 
    * 
    * Atomically adds @i to @v. 
    */ 
    static inline void atomic64_add(long i, atomic64_t *v) 
    { 
         asm volatile(LOCK_PREFIX "addq %1,%0" 
            : "=m" (v->counter) 
            : "er" (i), "m" (v->counter)); 
    } 
    
    2

    आप यादृच्छिक पर कोई पता प्राप्त कर बनाने के लिए डर नहीं होना चाहिए, क्योंकि कर्नेल केवल उन्हीं पतों को अस्वीकार कर देगा जो इसे पसंद नहीं करते हैं (जो संघर्ष करते हैं)। मेरी shmat() जवाब ऊपर का उपयोग कर देखें, 0x20000000000

    mmap के साथ

    :

    शून्य * mmap (शून्य * addr, size_t लंबाई, पूर्णांक prot, झंडे int, int fd, off_t ऑफसेट);

    तो addr शून्य नहीं है, तो गिरी यह जहां को जगह मानचित्रण के बारे में एक संकेत के रूप में लेता है; लिनक्स पर, मानचित्रण अगले उच्च पृष्ठ सीमा पर बनाया जाएगा। का पता नया मैपिंग कॉल के परिणाम के रूप में वापस कर दिया गया है।

    झंडे तर्क निर्धारित करता है कि मानचित्रण के लिए अपडेट अन्य प्रक्रियाओं मानचित्रण ही क्षेत्र के लिए दिखाई दे रहे हैं, और क्या अपडेट अंतर्निहित फाइल करने के लिए के माध्यम से किया जाता है। यह व्यवहार झंडे में निम्नलिखित मूल्यों के ठीक एक सहित से निर्धारित होता है:

    MAP_SHARED शेयर इस मानचित्रण। मैपिंग के लिए अपडेट अन्य प्रक्रियाओं को दिखाता है जो इस फ़ाइल को मैप करते हैं, और अंतर्निहित फ़ाइल में ले जाया जाता है।फ़ाइल वास्तव में तब तक अपडेट नहीं की जा सकती जब तक msync (2) या munmap() को कॉल नहीं किया जाता है।

    त्रुटियों

    EINVAL हम addr, लंबाई, या ऑफसेट पसंद नहीं है (उदाहरण के लिए, वे बहुत बड़ी है, या एक पेज सीमा पर गठबंधन नहीं हैं)।

    +0

    दिलचस्प, मुझे लगता है कि आप केवल डिवाइस ड्राइवर या अन्य निचले स्तर की हैकर लिखते समय इसका उपयोग कर सकते हैं। यह एक संभावित रूप से आकर्षक समाधान है, लेकिन इसके लिए सभी प्रक्रियाओं को विभिन्न क्षेत्रों को mmaping करने की आवश्यकता होगी, जब तक उन्हें कोई ऐसा नहीं मिल जाता है, जहां वे सभी सहमत हो सकते हैं, और यदि कोई नई प्रक्रिया शुरू की गई थी तो मौजूदा मैपिंग को पसंद नहीं किया गया था, सभी पुराने लोग संभावित रूप से अपने डेटा को एक नए स्थान पर कॉपी करना होगा। कूल विचार फिर भी, ऊपर उठाया। –

    +0

    @shm skywalker, मुझे यह एक पुराना धागा पता है, लेकिन यह बहुत अच्छा है। साझा करने के लिए धन्यवाद :) मैं अब समझने की कोशिश कर रहा हूं, कर्नेल बिल्कुल क्यों अस्वीकार करेगा? क्या इस अस्वीकृति को रोकने के लिए कोई तरीका है - शायद एक अप्रयुक्त डमी अनुभाग बनाने के लिए लिंकर को कॉन्फ़िगर करके? – user1827356

    1

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

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

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