हम अभी भी स्टैक को पीछे की तरफ क्यों बढ़ाते हैं?


46

C कोड संकलित करने और असेंबली को देखने पर, यह सब इस तरह से पीछे की तरफ बढ़ता है:

_main:
    pushq   %rbp
    movl    $5, -4(%rbp)
     popq    %rbp
    ret

-4(%rbp)- इसका मतलब यह है कि बेस पॉइंटर या स्टैक पॉइंटर वास्तव में ऊपर जाने के बजाय मेमोरी एड्रेस को नीचे ले जा रहे हैं? ऐसा क्यों है?

मैंने कोड परिवर्तित $5, -4(%rbp)किया $5, +4(%rbp), संकलित किया और कोड चलाया और कोई त्रुटि नहीं थी। तो हमें मेमोरी स्टैक पर अभी भी पीछे क्यों जाना है?


2
ध्यान दें कि -4(%rbp)बेस पॉइंटर को बिल्कुल भी स्थानांतरित नहीं करता है और यह +4(%rbp)संभवतः काम नहीं कर सकता है।
मार्गरेट ब्लूम

14
" हमें अभी भी पीछे क्यों जाना है " - आपको क्या लगता है कि आगे जाने का क्या फायदा होगा? अंत में, इससे कोई फर्क नहीं पड़ता, आपको बस एक चुनना है।
बरगी

31
"हम स्टैक को पीछे की तरफ क्यों बढ़ाते हैं?" - क्योंकि अगर हम किसी और से नहीं पूछेंगे कि mallocढेर पीछे की ओर क्यों बढ़ता है
स्लीबेटमैन

2
@MargaretBloom: स्पष्ट रूप से ओपी के मंच पर, CRT स्टार्टअप कोड को परवाह नहीं है कि mainक्या उसका RBP बंद है। यह निश्चित रूप से संभव है। (और हां, लेखन 4(%rbp)सहेजे गए आरबीपी मूल्य पर कदम होगा)। इर वास्तव में, यह मुख्य कभी नहीं करता है mov %rsp, %rbp, इसलिए मेमोरी एक्सेस कॉलर के आरबीपी के सापेक्ष है , अगर यही ओपी वास्तव में परीक्षण किया गया है !!! यदि यह वास्तव में संकलक आउटपुट से कॉपी किया गया था, तो कुछ निर्देश छोड़ दिए गए थे!
पीटर कॉर्डेस

1
यह मुझे लगता है कि "बैकवर्ड" या "फॉरवर्ड" (या "डाउन" और "अप") आपके दृष्टिकोण पर निर्भर करता है। यदि आप शीर्ष पर कम पते वाले स्तंभ के रूप में मेमोरी को आरेखित करते हैं, तो स्टैक पॉइंटर को घटाकर स्टैक बढ़ाना एक भौतिक स्टैक के अनुरूप होगा।
जेम्सडलिन

जवाबों:


86

इसका मतलब यह है कि बेस पॉइंटर या स्टैक पॉइंटर वास्तव में ऊपर जाने के बजाय मेमोरी एड्रेस को नीचे ले जा रहे हैं? ऐसा क्यों है?

हाँ, pushनिर्देश स्टैक पॉइंटर को घटाते हैं और स्टैक को लिखते हैं, जबकि popरिवर्स करते हैं, स्टैक से पढ़ते हैं और स्टैक पॉइंटर को बढ़ाते हैं।

यह कुछ हद तक ऐतिहासिक है कि सीमित मेमोरी वाली मशीनों के लिए, स्टैक को ऊंचा रखा गया और नीचे की तरफ उगाया गया, जबकि ढेर को नीचे रखा गया और पीछे की तरफ उगाया गया। "फ्री मेमोरी" का केवल एक ही अंतराल है - ढेर और स्टैक के बीच, और यह अंतर साझा किया जाता है, या तो एक अंतराल में बढ़ सकता है जैसा कि व्यक्तिगत रूप से आवश्यक है। इस प्रकार, प्रोग्राम केवल मेमोरी से बाहर निकलता है जब स्टैक और हीप बिना किसी फ्री मेमोरी को छोड़ते हुए टकराते हैं। 

यदि स्टैक और हीप दोनों एक ही दिशा में बढ़ते हैं, तो दो अंतराल हैं, और स्टैक वास्तव में ढेर के अंतर में नहीं बढ़ सकता है (इसके विपरीत भी समस्याग्रस्त है)।

मूल रूप से, प्रोसेसर के पास कोई समर्पित स्टैक हैंडलिंग निर्देश नहीं था। हालाँकि, जैसा कि हार्डवेयर में स्टैक समर्थन जोड़ा गया था, यह नीचे की ओर बढ़ने के इस पैटर्न पर ले गया, और प्रोसेसर आज भी इस पैटर्न का अनुसरण करते हैं।

कोई तर्क दे सकता है कि 64-बिट मशीन पर कई अंतरालों को अनुमति देने के लिए पर्याप्त पता स्थान है - और सबूत के रूप में, कई अंतराल जरूरी हैं जब एक प्रक्रिया में कई धागे होते हैं। यद्यपि यह कई अंतर प्रणालियों के साथ चीजों को बदलने के लिए पर्याप्त प्रेरणा नहीं है, लेकिन विकास की दिशा यकीनन मनमानी है, इसलिए परंपरा / संगतता पैमाने को टिप्स देती है।


आपको ढेर की दिशा बदलने, वरना समर्पित धक्का और पॉपिंग निर्देश (जैसे के उपयोग पर देने के लिए में सीपीयू ढेर से निपटने निर्देश बदलने के लिए होगा push, pop, call, ret, अन्य)।

ध्यान दें कि MIPS इंस्ट्रक्शन सेट आर्किटेक्चर के पास समर्पित pushऔर नहीं है pop, इसलिए किसी भी दिशा में स्टैक को बढ़ाना व्यावहारिक है - आप अभी भी सिंगल थ्रेड प्रक्रिया के लिए एक-गैप मेमोरी लेआउट चाहते हैं, लेकिन स्टैक को ऊपर और ढेर तक बढ़ा सकते हैं। नीचे की ओर। यदि आपने ऐसा किया है, हालांकि, कुछ सी वेरिएग कोड को स्रोत में या अंडर-हुड पैरामीटर पारित होने में समायोजन की आवश्यकता हो सकती है।

(वास्तव में, चूंकि MIPS पर कोई समर्पित स्टैक हैंडलिंग नहीं है, इसलिए हम स्टैक पर पुश करने के लिए प्री या पोस्ट इंक्रीमेंट या प्री या पोस्ट डिक्रीमेंट का उपयोग कर सकते हैं, जब तक कि हम स्टैक को पॉप करने के लिए सटीक रिवर्स का उपयोग करते थे, और यह भी मान लें कि ऑपरेटिंग सिस्टम चुने हुए स्टैक उपयोग मॉडल का सम्मान करता है। वास्तव में, कुछ एम्बेडेड सिस्टम और कुछ शैक्षणिक प्रणालियों में, MIPS स्टैक को ऊपर की तरफ उगाया जाता है।)


32
यह सिर्फ नहीं है pushऔर popसबसे आर्किटेक्चर पर, लेकिन यह भी कहीं अधिक महत्वपूर्ण बाधा-हैंडलिंग, call, ret, और जो कुछ भी पकाया में ढेर के साथ बातचीत की है।
डेडुप्लिकेटर

3
एआरएम में सभी चार स्टैक फ्लेवर हो सकते हैं।
मार्गरेट ब्लूम

14
इसके लायक यह है, मुझे नहीं लगता कि "विकास की दिशा मनमानी है" इस अर्थ में कि या तो विकल्प समान रूप से अच्छा है। नीचे बढ़ने पर वह संपत्ति होती है जो सहेजे गए रिटर्न पतों सहित, पहले के स्टैक फ्रेमों के बफर क्लोबर्स के अंत में बह जाती है। बड़े होने पर वह संपत्ति होती है जो बफर क्लॉबर्स के अंत में एक ही या बाद में भंडारण को समाप्त कर देती है (यदि बफर नवीनतम में नहीं है, तो बाद में हो सकते हैं) कॉल फ्रेम, और संभवतः केवल अप्रयुक्त स्थान (सभी को एक गार्ड मानते हैं) स्टैक के बाद पेज)। इसलिए सुरक्षा के नजरिए से, बड़ा होना बेहतर लगता है
R ..

6
@ आर ..: बफ़रिंग खत्म नहीं करता है बफर ओवररन कारनामे, क्योंकि कमजोर कार्य आमतौर पर पत्ती कार्य नहीं होते हैं: वे अन्य कार्यों को कॉल करते हैं, बफर के ऊपर एक रिटर्न एड्रेस रखते हैं। लीफ फ़ंक्शंस जो अपने कॉलर से पॉइंटर प्राप्त करते हैं, अपने स्वयं के रिटर्न पते को अधिलेखित करने के लिए आकर्षक हो सकते हैं। उदाहरण यदि कोई फ़ंक्शन स्टैक पर एक बफ़र आवंटित करता है और इसे पास करता है gets(), या strcpy()जो इनलेट नहीं करता है, तो उन लाइब्रेरी फ़ंक्शंस में वापसी ओवरराइट लिखित पते का उपयोग करेगी। वर्तमान में नीचे की ओर बढ़ते ढेर के साथ, यह तब होता है जब उनका कॉलर वापस लौटता है।
पीटर कॉर्डेस

5
@PeterCordes: वास्तव में मेरी टिप्पणी ने उल्लेख किया है कि अतिप्रवाहित बफर की तुलना में समान-स्तर या अधिक हाल के स्टैक फ्रेम अभी भी संभावित रूप से स्पष्ट हैं, लेकिन यह बहुत कम है। उस मामले में जहां क्लोबर्बिंग फ़ंक्शन एक पत्ता फ़ंक्शन होता है जिसे सीधे उस फ़ंक्शन द्वारा बुलाया जाता है जिसका बफर (जैसे strcpy) है, एक आर्च पर जहां रिटर्न एड्रेस को एक रजिस्टर में रखा जाता है जब तक कि इसे स्पिल करने की आवश्यकता नहीं होती है, लौटना के लिए कोई एक्सेस नहीं है पता।
आर ..

8

आपके विशिष्ट सिस्टम में स्टैक उच्च मेमोरी एड्रेस से शुरू होता है और कम मेमोरी एड्रेस में "बढ़ता" है। (कम से उच्च तक सममित मामला भी मौजूद है)

और जब से आप -4 और +4 से बदल गए और यह भाग गया इसका मतलब यह नहीं है कि यह सही है। एक रनिंग प्रोग्राम का मेमोरी लेआउट अधिक जटिल है और कई अन्य कारकों पर निर्भर करता है जो इस तथ्य पर योगदान कर सकते हैं कि आप इस बेहद सरल प्रोग्राम पर तुरंत दुर्घटनाग्रस्त नहीं हुए थे।


1

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

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

ध्यान दें कि स्टैक फ्रेम और उनका अनुक्रमण कंप्यूटर भाषाओं का एक पक्ष पहलू है इसलिए यह कंपाइलर का कोड जनरेटर है जिसे खराब असेंबली भाषा प्रोग्रामर के बजाय "अप्राकृतिकता" से निपटना पड़ता है।

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


2
"अब कई प्रणालियों पर इन दिनों, स्टैक फ्रेम के लिए एक अलग रजिस्टर है" आप समय के पीछे हैं। अमीर डिबग सूचना प्रारूपों ने आजकल बड़े पैमाने पर फ्रेम पॉइंटर्स की आवश्यकता को हटा दिया है।
पीटर ग्रीन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.