मैं सी में कुछ प्रशिक्षण सामग्री तैयार कर रहा हूं और मैं चाहता हूं कि मेरे उदाहरण ठेठ स्टैक मॉडल को फिट करने के लिए हैं।
लिनक्स, विंडोज, मैक ओएसएक्स (पीपीसी और x86), सोलारिस और सबसे हाल ही में यूनिक्स में सी स्टैक किस दिशा में बढ़ता है?
मैं सी में कुछ प्रशिक्षण सामग्री तैयार कर रहा हूं और मैं चाहता हूं कि मेरे उदाहरण ठेठ स्टैक मॉडल को फिट करने के लिए हैं।
लिनक्स, विंडोज, मैक ओएसएक्स (पीपीसी और x86), सोलारिस और सबसे हाल ही में यूनिक्स में सी स्टैक किस दिशा में बढ़ता है?
जवाबों:
स्टैक वृद्धि आमतौर पर ऑपरेटिंग सिस्टम पर ही निर्भर नहीं करती है, लेकिन प्रोसेसर पर यह चल रही है। उदाहरण के लिए सोलारिस, x86 और SPARC पर चलता है। Mac OSX (जैसा कि आपने बताया) PPC और x86 पर चलता है। लिनक्स मेरे बड़े होनकिन 'सिस्टम z' की हर चीज पर काम करता है जो एक छोटी छोटी कलाई घड़ी पर काम करता है ।
यदि सीपीयू किसी भी तरह का विकल्प प्रदान करता है, तो ओएस द्वारा उपयोग किया जाने वाला एबीआई / कॉलिंग कन्वेंशन निर्दिष्ट करता है कि आपको कौन सा विकल्प चाहिए अगर आप चाहते हैं कि आपका कोड हर किसी के कोड को कॉल करे।
प्रोसेसर और उनकी दिशा हैं:
उन आखिरी कुछ पर मेरी उम्र दिखाते हुए, 1802 चिप था जिसका उपयोग शुरुआती शटर को नियंत्रित करने के लिए किया जाता था (अगर दरवाजे खुले थे, तो मुझे संदेह था, मुझे शक है कि प्रसंस्करण शक्ति के आधार पर :-) और मेरा दूसरा कंप्यूटर, COMX-35 ( मेरे ZX80 के बाद )।
PDP11 से बटोरा विवरण यहाँ से, 8051 विवरण यहाँ ।
SPARC आर्किटेक्चर एक स्लाइडिंग विंडो रजिस्टर मॉडल का उपयोग करता है। वास्तुकला में दिखाई देने वाले विवरणों में रजिस्टर-खिड़कियों का एक परिपत्र बफर भी शामिल है, जो आंतरिक रूप से वैध और कैश्ड हैं, जब जाल उस से अधिक / कम होते हैं। देखें यहाँ जानकारी के लिए। जैसा कि SPARCv8 मैनुअल बताता है , SAVE और बाकी निर्देश ADD निर्देश और रजिस्टर-विंडो रोटेशन की तरह हैं। सामान्य ऋणात्मक के बजाय सकारात्मक स्थिरांक का उपयोग करने से उर्ध्वगामी वृद्धि होती है।
उपर्युक्त SCRT तकनीक एक और है - 1802 में कुछ का उपयोग किया गया या यह SCRT के लिए सोलह 16-बिट रजिस्टर (मानक कॉल और वापसी तकनीक) है। एक प्रोग्राम काउंटर था, आप SEP Rn
निर्देश के साथ पीसी के रूप में किसी भी रजिस्टर का उपयोग कर सकते हैं । एक स्टैक पॉइंटर था और दो को हमेशा एससीआरटी कोड एड्रेस, कॉल के लिए एक, रिटर्न के लिए एक को इंगित करने के लिए सेट किया गया था। किसी भी रजिस्टर को एक विशेष तरीके से व्यवहार नहीं किया गया था। ध्यान रखें कि ये विवरण स्मृति से हैं, वे पूरी तरह से सही नहीं हो सकते हैं।
उदाहरण के लिए, यदि R3 पीसी था, R4 SCRT कॉल एड्रेस था, R5 SCRT रिटर्न एड्रेस था और R2 "स्टैक" था (सॉफ्टवेयर में इसे लागू किया गया है तो उद्धरण), SEP R4
R4 को PC के रूप में सेट करेगा और SCRT को चलाना शुरू करेगा। कॉल कोड।
इसके बाद यह R2 "ढेर" (मुझे लगता है कि R6 अस्थायी भंडारण के लिए इस्तेमाल किया गया था) पर R3 संग्रहीत करेंगे इसे एडजस्ट करने या नीचे, R3 निम्नलिखित दो बाइट्स हड़पने, उन्हें लोड में R3, तो SEP R3
और नए पते पर चल रहा हो।
वापस लौटने के लिए, यह SEP R5
आर 2 स्टैक से पुराने पते को खींच लेगा, इसमें दो जोड़ देगा (कॉल के पते बाइट्स को छोड़ने के लिए), इसे R3 में लोड करें और SEP R3
पिछले कोड को चलाना शुरू करें।
सभी 6502/6809 / z80 स्टैक-आधारित कोड के बाद शुरू में अपने सिर को चारों ओर लपेटने के लिए बहुत कठिन है, लेकिन फिर भी एक बैंग-योर-हेड-विरुद्ध-द-दीवार तरह से सुरुचिपूर्ण है। इसके अलावा चिप की बड़ी बिक्री वाली विशेषताओं में से एक 16 16-बिट रजिस्टरों का एक पूर्ण सूट था, इस तथ्य के बावजूद कि आपने तुरंत उनमें से 7 (एससीआरटी के लिए 5, डीएमए के लिए दो और स्मृति से बाधित)। अह, वास्तविकता पर विपणन की विजय :-)
सिस्टम z वास्तव में काफी समान है, कॉल / रिटर्न के लिए अपने R14 और R15 रजिस्टरों का उपयोग करता है।
C ++ में (C के अनुकूल) stack.cc :
static int
find_stack_direction ()
{
static char *addr = 0;
auto char dummy;
if (addr == 0)
{
addr = &dummy;
return find_stack_direction ();
}
else
{
return ((&dummy > addr) ? 1 : -1);
}
}
static
। इसके बजाय आप एक पुनरावर्ती कॉल के लिए एक तर्क के रूप में पते को पास कर सकते हैं।
static
, यदि आप इसे एक से अधिक बार कॉल करते हैं, तो बाद की कॉल विफल हो सकती हैं ...
नीचे बढ़ने का लाभ पुराने सिस्टम में है स्टैक आमतौर पर मेमोरी के शीर्ष पर था। कार्यक्रम आम तौर पर नीचे से शुरू होने वाली मेमोरी को भर देते हैं। इस प्रकार के मेमोरी मैनेजमेंट ने स्टैक के निचले हिस्से को कहीं न कहीं समझदारी से मापने और जगह देने की आवश्यकता को कम कर दिया है।
MIPS और कई आधुनिक RISC आर्किटेक्चर (जैसे PowerPC, RISC-V, SPARC ...) में कोई निर्देश push
और pop
निर्देश नहीं हैं । उन कार्यों को स्पष्ट रूप से स्टैक पॉइंटर को मैन्युअल रूप से समायोजित करने के द्वारा किया जाता है फिर मूल्य को समायोजित पॉइंटर के लिए अपेक्षाकृत अधिक लोड / स्टोर करता है। सभी रजिस्टर (शून्य रजिस्टर को छोड़कर) सामान्य उद्देश्य हैं, इसलिए सिद्धांत रूप में कोई भी रजिस्टर एक स्टैक पॉइंटर हो सकता है, और स्टैक किसी भी दिशा में बढ़ सकता है जो प्रोग्रामर चाहता है
कहा कि, स्टैक आमतौर पर अधिकांश आर्किटेक्चर पर बढ़ता है, संभवतः मामले से बचने के लिए जब स्टैक और प्रोग्राम डेटा या हीप डेटा बढ़ता है और एक दूसरे से टकराता है। वहाँ भी महान पता कारण श-'s जवाब का उल्लेख किया है । कुछ उदाहरण: MIPS ABI नीचे की ओर बढ़ता है और स्टैक पॉइंटर के रूप में $29
(AKA $sp
) का उपयोग करता है, RISC-V ABI भी नीचे की तरफ बढ़ता है और स्टैक पॉइंटर के रूप में x2 का उपयोग करता है
इंटेल 8051 में स्टैक बड़ा हो जाता है, शायद इसलिए कि मेमोरी स्पेस इतना छोटा है (मूल संस्करण में 128 बाइट्स) जिसमें कोई ढेर नहीं है और आपको स्टैक को शीर्ष पर रखने की आवश्यकता नहीं है ताकि यह ढेर से अलग हो जाए नीचे से
आप https://en.wikipedia.org/wiki/Calling_conference में विभिन्न आर्किटेक्चर में स्टैक उपयोग के बारे में अधिक जानकारी प्राप्त कर सकते हैं
यह सभी देखें
अन्य उत्तरों के लिए एक छोटा सा जोड़, जो कि जहाँ तक मैं देख सकता हूँ, इस बिंदु को नहीं छुआ है:
स्टैक को नीचे की ओर बढ़ने से स्टैक के भीतर सभी पते स्टैक पॉइंटर के सापेक्ष एक सकारात्मक ऑफसेट होते हैं। नकारात्मक ऑफसेट की कोई आवश्यकता नहीं है, क्योंकि वे केवल अप्रयुक्त स्टैक स्थान की ओर इशारा करेंगे। जब प्रोसेसर स्टैकपॉइंट-रिलेटिव एड्रेसिंग को सपोर्ट करता है तो यह स्टैकिंग लोकेशन को सरल बनाता है।
कई प्रोसेसर के निर्देश हैं जो कुछ रजिस्टर के सापेक्ष केवल सकारात्मक ऑफसेट के साथ एक्सेस की अनुमति देते हैं। इनमें कई आधुनिक आर्किटेक्चर शामिल हैं, साथ ही कुछ पुराने भी हैं। उदाहरण के लिए, एआरएम थम्ब एबीआई 16 सिंगल-बिट निर्देश शब्द के भीतर एन्कोडेड पॉजिटिव ऑफसेट के साथ स्टैकपॉइंट-रिलेटिव एक्सेस के लिए प्रदान करता है।
यदि स्टैक ऊपर की ओर बढ़ता है, तो स्टैकपॉइंट के सापेक्ष सभी उपयोगी ऑफसेट नकारात्मक होंगे, जो कम सहज और कम सुविधाजनक है। यह रजिस्टर-सापेक्ष पते के अन्य अनुप्रयोगों के साथ भी है, उदाहरण के लिए एक संरचना के क्षेत्रों तक पहुँचने के लिए।
अधिकांश प्रणालियों पर, स्टैक नीचे बढ़ता है, और https://gist.github.com/cpq/8598782 पर मेरा लेख बताता है कि यह क्यों बढ़ता है। यह सरल है: मेमोरी के एक निश्चित भाग में दो बढ़ते हुए मेमोरी ब्लॉक (ढेर और स्टैक) को कैसे लेआउट करें? सबसे अच्छा उपाय यह है कि उन्हें विपरीत छोर पर रखा जाए और एक-दूसरे की तरफ बढ़ने दिया जाए।
यह नीचे बढ़ता है क्योंकि प्रोग्राम को आवंटित मेमोरी में "स्थायी डेटा" होता है अर्थात नीचे की तरफ प्रोग्राम के लिए कोड, फिर बीच में ढेर। आपको एक और निश्चित बिंदु की आवश्यकता है जिसमें से स्टैक को संदर्भित करना है, ताकि आप शीर्ष छोड़ दें। इसका मतलब यह है कि स्टैक नीचे बढ़ता है, जब तक कि यह संभावित रूप से ढेर पर वस्तुओं के समीप न हो।
इस मैक्रो को यूबी के बिना रनटाइम पर इसका पता लगाना चाहिए:
#define stk_grows_up_eh() stk_grows_up__(&(char){0})
_Bool stk_grows_up__(char *ParentsLocal);
__attribute((__noinline__))
_Bool stk_grows_up__(char *ParentsLocal) {
return (uintptr_t)ParentsLocal < (uintptr_t)&ParentsLocal;
}