आईबीएम उदाहरण कोड, गैर-प्रवेश-योग्य कार्य मेरे सिस्टम में काम नहीं करता है


11

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

कोड दो मानों को मुद्रित करने वाले एक पाठ कार्यक्रम (अतुल्यकालिकता) के गैर रेखीय विकास में चर तक साझा पहुंच से जुड़े मुद्दों को दिखाने का प्रयास करता है जो लगातार "खतरनाक संदर्भ" में बदलते हैं।

#include <signal.h>
#include <stdio.h>

struct two_int { int a, b; } data;

void signal_handler(int signum){
   printf ("%d, %d\n", data.a, data.b);
   alarm (1);
}

int main (void){
   static struct two_int zeros = { 0, 0 }, ones = { 1, 1 };

   signal (SIGALRM, signal_handler); 
   data = zeros;
   alarm (1);
   while (1){
       data = zeros;
       data = ones;
   }
}

समस्याएँ तब सामने आईं जब मैंने कोड चलाने की कोशिश की (या बेहतर, प्रकट नहीं हुई)। मैं डिफ़ॉल्ट कॉन्फ़िगरेशन में gcc संस्करण 6.3.0 20170516 (डेबियन 6.3.0-18 + deb9u1) का उपयोग कर रहा था। गुमराह किया हुआ उत्पादन नहीं होता है। "गलत" जोड़ी मूल्यों को प्राप्त करने की आवृत्ति 0 है!

आखिर हो क्या रहा है? स्थैतिक वैश्विक चर का उपयोग करके फिर से प्रवेश में कोई समस्या क्यों नहीं है?


1
सुनिश्चित करें कि सभी कंपाइलर ऑप्टिमाइज़ेशन अक्षम हैं, और फिर से कोशिश करें
रोज़ा

मुझे लगता है कि ... लेकिन मैं कौन से विकल्प बदलूंगा? मुझे पता नहीं है। :-(
डेनियल बंदेइरा

5
यह एक प्रोग्रामिंग प्रश्न (स्टैक ओवरफ्लो) जैसा दिखता है। यह खुराक अच्छी तरह से यहाँ नहीं लगता है। (क्षमा करें, मैं वहां कम उप-साइटें थी; यह बहुत कटा हुआ है। लेकिन यह तरीका है कि यह है।)
ctrl-alt-delor

1
सबसे सरल री-एंट्रेंट कोड अपरिवर्तनीय है।
ctrl-alt-delor

पहली बार में, मुझे लगता है कि सवाल gcc और Linux वातावरण से संबंधित होगा। उदाहरण के लिए, विकासशील, OS का शेड्यूलिंग (हैंडलर रूटीन को कॉल करने से पहले रुकावट संकेत के बाद अधिक प्रोग्राम टेक्स्ट निष्पादित करना)।
डैनियल बांदीरा

जवाबों:


12

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

यह सिर्फ़ एक सिग्नल हैंडलर और मुख्य धागे के बीच सादा वेनिला डेटा-रेस UB (अनफाइंड बिहेवियर) है: केवल sig_atomic_tइसके लिए सुरक्षित होने की गारंटी है । दूसरों को काम करने के लिए हो सकता है, जैसे आपके मामले में जहां एक 8-बाइट ऑब्जेक्ट को x86-64 पर एक निर्देश के साथ लोड या संग्रहीत किया जा सकता है, और कंपाइलर उस asm को चुनने के लिए होता है। (@ इकारस के जवाब से पता चलता है)।

MCU प्रोग्रामिंग देखें - लूप करते समय C ++ O2 ऑप्टिमाइज़ेशन टूट जाता है - सिंगल-कोर माइक्रोकंट्रोलर पर एक रुकावट हैंडलर मूल रूप से सिंगल थ्रेडेड प्रोग्राम में सिग्नल हैंडलर के समान होता है। उस मामले में यूबी का परिणाम है कि एक लोड लूप से बाहर फहराया गया।

डेटा-रेस UB की वजह से वास्तव में हो रही छेड़छाड़ के आपके टेस्ट-केस को 32-बिट मोड में विकसित किया गया / परीक्षण किया गया, या एक पुराने डम्बर कंपाइलर के साथ जो अलग-अलग सदस्यों को लोड करता था।

आपके मामले में, कंपाइलर दुकानों को अनंत लूप से बाहर निकाल सकता है क्योंकि कोई भी यूबी-फ्री प्रोग्राम कभी भी उनका निरीक्षण नहीं कर सकता है। dataहै _Atomicया नहींvolatile , और लूप में कोई अन्य दुष्प्रभाव नहीं हैं। इसलिए कोई रास्ता नहीं है कि कोई भी पाठक इस लेखक के साथ समन्वय कर सके। यह वास्तव में तब होता है जब आप अनुकूलन सक्षम के साथ संकलित करते हैं ( Godbolt मुख्य के नीचे एक खाली लूप दिखाता है)। मैंने संरचना को दो में भी बदल दिया long long, और gcc movdqaलूप से पहले एक सिंगल -बाइट स्टोर का उपयोग करता है । (यह परमाणु की गारंटी नहीं है , लेकिन यह लगभग सभी सीपीयू पर प्रचलन में है, यह मानते हुए कि यह संरेखित है, या इंटेल पर केवल कैश-लाइन सीमा पार नहीं करता है। पूर्णांक असाइनमेंट x86 पर स्वाभाविक रूप से संरेखित चर परमाणु पर क्यों है? )

इसलिए सक्षम किए गए अनुकूलन के साथ संकलन करना भी आपके परीक्षण को तोड़ देगा, और आपको हर बार समान मूल्य दिखाएगा। C एक पोर्टेबल असेंबली भाषा नहीं है।

volatile struct two_intकंपाइलर को मजबूर नहीं करेगा कि वह उन्हें ऑप्टिमाइज़ करे, लेकिन इसे पूरे स्ट्रक्चर को एटमॉइकल लोड / स्टोर करने के लिए मजबूर नहीं करेगा । (यह नहीं रोक या तो ऐसा करने से यह, हालांकि।) नोट है कि volatileहै नहीं डेटा-दौड़ यूबी से बचने, लेकिन व्यवहार में यह अंतर-धागा संचार के लिए पर्याप्त है और कैसे लोगों को (इनलाइन एएसएम के साथ) हाथ से लुढ़का एटोमिक्स बनाया गया था सामान्य CPU आर्किटेक्चर के लिए C11 / C ++ 11 से पहले। वे कैश-सुसंगत इतना volatileहै अभ्यास ज्यादातर के लिए इसी तरह के _Atomicसाथmemory_order_relaxed शुद्ध लोड और शुद्ध दुकान के लिए, यदि प्रकार के लिए इस्तेमाल किया पर्याप्त है कि संकलक एक एकल अनुदेश का उपयोग करेगा ताकि आप फाड़ नहीं मिलता है संकीर्ण। और निश्चित रूप सेvolatile आईएसओ सी मानक बनाम लेखन कोड से किसी भी गारंटी देता है कि एक ही एएसएम को compiles का उपयोग कर की जरूरत नहीं है _Atomicऔर mo_relaxed।


यदि आपके पास एक फ़ंक्शन था global_var++;जो एक intया long longउस पर था जो आप सिग्नल हैंडलर से मुख्य और अतुल्यकालिक रूप से चलाते हैं , तो यह डेटा-रेस यूबी बनाने के लिए पुन: प्रवेश का उपयोग करने का एक तरीका होगा।

यह कैसे संकलित किया जाता है (स्मृति गंतव्य inc या जोड़ने के लिए, या लोड / inc / store को अलग करने के लिए) यह परमाणु या उसी धागे में संकेत संचालकों के संबंध में नहीं होगा। देखें Can संख्या 'पूर्णांक संख्या' के लिए परमाणु हो ++? x86 पर और C ++ में परमाणु के बारे में अधिक जानकारी के लिए। (C11 की विशेषता stdatomic.hऔर _Atomicविशेषता C ++ 11 के std::atomic<T>टेम्पलेट के बराबर कार्यक्षमता प्रदान करती है )

एक व्यवधान या अन्य अपवाद एक निर्देश के बीच में नहीं हो सकता है, इसलिए एक मेमोरी-डेस्ट ऐड परमाणु परमाणु है। संदर्भ एक-कोर सीपीयू पर स्विच करता है। केवल (कैश सुसंगत) डीएमए लेखक एकल-कोर सीपीयू पर एक उपसर्ग के add [mem], 1बिना एक वेतन वृद्धि "कदम" कर सकता है lock। वहाँ कोई अन्य कोर नहीं है कि एक और धागा चल सकता है।

तो यह संकेतों के मामले के समान है: सिग्नल को संभालने वाले थ्रेड के सामान्य निष्पादन के बजाय एक सिग्नल हैंडलर चलता है , इसलिए इसे एक निर्देश के बीच में नहीं संभाला जा सकता है।


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

17

को देखते हुए godbolt संकलक एक्सप्लोरर (लापता में जोड़ने के बाद #include <unistd.h>), एक देखता है कि लगभग किसी भी x86_64 संकलक के लिए कोड उत्पन्न का उपयोग करता है QWORD चाल लोड करने के लिए onesऔर zerosएक एकल अनुदेश में।

        mov     rax, QWORD PTR main::ones[rip]
        mov     QWORD PTR data[rip], rax

आईबीएम साइट का कहना है कि On most machines, it takes several instructions to store a new value in data, and the value is stored one word at a time.जो 2005 में ठेठ सीपीस के लिए सच था, लेकिन जैसा कि अब कोड शो सच नहीं है। संरचना को बदलने के बजाय दो ints के बजाय दो longs होना चाहिए।

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

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


3
से डेटा प्रकार परिवर्तित करने का प्रयास intकरने के लिए long long, और 32 बिट करने के लिए संकलन। सबक यह है कि आप कभी नहीं जानते हैं कि क्या / कब टूट जाएगा।
ctrl-alt-delor

2
इसका मतलब है, मेरी मशीन में, इस दो मानों का कार्य एक परमाणु संचालन है? (x86_64 वास्तुकला के संकलन पर विचार करते हुए)
डैनियल बंदेइरा

1
long longअभी भी x86-64 के लिए एक निर्देश के लिए संकलित करता है: 16-बाइट movdqa। जब तक आप अपने Godbolt लिंक की तरह अनुकूलन को अक्षम नहीं करते। (जीसीसी का डिफॉल्ट -O0डिबग मोड है, जो स्टोर / रीलोड शोर से भरा है और आमतौर पर देखने में दिलचस्प नहीं है।)
पीटर कॉर्डेस

मैंने सभी टिप्पणियों को पढ़ने के बाद "लंबे लंबे" प्रकार को बदल दिया। परिणाम दिलचस्प था: प्रतीक्षित परिणाम प्राप्त किए गए थे और, कुछ काउंटरों को स्थापित करते हुए, यह दूसरों की धारणाओं को सुधारने में सक्षम था कि कैसे बेमेल डेटा की दर बाकी कोड से प्रभावित होती है। सभी मदद के लिए धन्यवाद!
डैनियल बंदेइरा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.