लिनक्स में स्टैक आवंटन कैसे काम करता है?


18

क्या OS स्टैक या कुछ और के लिए वैध आभासी स्थान की निश्चित राशि आरक्षित करता है? क्या मैं केवल बड़े स्थानीय चर का उपयोग करके स्टैक ओवरफ्लो का उत्पादन करने में सक्षम हूं?

मैंने Cअपनी धारणा का परीक्षण करने के लिए एक छोटा कार्यक्रम लिखा है । यह X86-64 CentOS 6.5 पर चल रहा है।

#include <string.h>
#include <stdio.h>
int main()
{
    int n = 10240 * 1024;
    char a[n];
    memset(a, 'x', n);
    printf("%x\n%x\n", &a[0], &a[n-1]);
    getchar();
    return 0;
}

कार्यक्रम चलाने &a[0] = f0ceabe0और देता है&a[n-1] = f16eabdf

खरीद नक्शे ढेर को दर्शाता है: 7ffff0cea000-7ffff16ec000. (10248 * 1024B)

फिर मैंने बढ़ाने की कोशिश की n = 11240 * 1024

कार्यक्रम चलाने &a[0] = b6b36690और देता है&a[n-1] = b763068f

खरीद नक्शे ढेर को दर्शाता है: 7fffb6b35000-7fffb7633000. (11256 * 1024B)

ulimit -s10240मेरे पीसी में प्रिंट ।

जैसा कि आप देख सकते हैं, दोनों ही स्थिति में स्टैक का आकार इससे बड़ा है जो ulimit -sदेता है। और स्टैक बड़े स्थानीय चर के साथ बढ़ता है। स्टैक का शीर्ष किसी तरह 3-5kB अधिक है &a[0](AFAIK लाल क्षेत्र 128B है)।

तो इस स्टैक मैप को कैसे आवंटित किया जाता है?

जवाबों:


14

ऐसा प्रतीत होता है कि स्टैक मेमोरी सीमा आवंटित नहीं है (वैसे भी, यह असीमित स्टैक के साथ नहीं हो सकता है)। https://www.kernel.org/doc/Documentation/vm/overcommit-accounting कहता है:

सी भाषा स्टैक ग्रोथ एक अंतर्निहित मरमप करती है। यदि आप पूर्ण गारंटी चाहते हैं और किनारे के करीब दौड़ना चाहते हैं, तो आपको अपने स्टैक को सबसे बड़े आकार में बदलना होगा जो आपको लगता है कि आपको आवश्यकता होगी। ठेठ स्टैक के उपयोग के लिए यह ज्यादा मायने नहीं रखता है लेकिन यह एक कोने का मामला है यदि आप वास्तव में परवाह करते हैं

हालांकि स्टैक को मिमी करना एक संकलक का लक्ष्य होगा (यदि इसके लिए एक विकल्प है)।

संपादित करें: एक x84_64 डेबियन मशीन पर कुछ परीक्षणों के बाद, मैंने पाया है कि स्टैक बिना किसी सिस्टम कॉल ( strace) के अनुसार बढ़ता है । तो, इसका मतलब यह है कि कर्नेल अपने आप बढ़ता है (यह "निहित" का अर्थ है ऊपर), अर्थात बिना स्पष्टmmap / mremapप्रक्रिया के ।

इसकी पुष्टि करते हुए विस्तृत जानकारी प्राप्त करना काफी कठिन था। मैं Mel Gorman द्वारा लिनक्स वर्चुअल मेमोरी मैनेजर को समझने की सलाह देता हूं । मुझे लगता है कि उत्तर धारा 4.6.1 में है है। पेज फॉल्ट को हैंडल करते हुए , अपवाद के साथ "क्षेत्र वैध नहीं है, लेकिन स्टैक जैसे एक विस्तार योग्य क्षेत्र के बगल में है" और इसी क्रिया "क्षेत्र का विस्तार करें और एक पृष्ठ आवंटित करें"। D.5.2 भी देखें स्टैक का विस्तार करना

लिनक्स मेमोरी प्रबंधन के बारे में अन्य संदर्भ (लेकिन स्टैक के बारे में कुछ भी नहीं के साथ):

EDIT 2: इस कार्यान्वयन में एक खामी है: कोने के मामलों में, एक ढेर-ढेर टक्कर का पता नहीं लगाया जा सकता है, यहां तक ​​कि उस मामले में भी जहां स्टैक सीमा से बड़ा होगा! इसका कारण यह है कि स्टैक में एक चर में लिखा गया आवंटित ढेर मेमोरी में समाप्त हो सकता है, जिस स्थिति में कोई पृष्ठ दोष नहीं है और कर्नेल को पता नहीं चल सकता है कि स्टैक को विस्तारित करने की आवश्यकता है। चर्चा में मेरा उदाहरण देखें जीएनयू / लिनक्स के तहत साइलेंट स्टैक-हीप टक्कर मैं gcc-help सूची में शुरू हुआ। उससे बचने के लिए, कंपाइलर को फ़ंक्शन कॉल पर कुछ कोड जोड़ने की आवश्यकता होती है; यह -fstack-checkजीसीसी के लिए किया जा सकता है (इयान लांस टेलर के जवाब और विवरण के लिए जीसीसी मैन पेज देखें)।


मेरे प्रश्न का सही उत्तर लगता है। लेकिन यह मुझे और भ्रमित करता है। मरमप कॉल कब ट्रिगर होगा? क्या यह कार्यक्रम में निर्मित एक syscall होगा?
आमोस

@ मानोस मैं मानता हूं कि अगर किसी फ़ंक्शन कॉल पर या एलोका () कहा जाता है, तो मरमप कॉल चालू हो जाएगा।
vinc17

जिन लोगों को नहीं पता है, उनके लिए mmap का उल्लेख करना एक अच्छा विचार होगा।
फहीम मीठा

@FaheemMitha मैंने कुछ जानकारी जोड़ी है। उन लोगों के लिए जो मिमीप नहीं जानते हैं, ऊपर उल्लिखित मेमोरी FAQ देखें। यहाँ, स्टैक के लिए, यह "गुमनाम मानचित्रण" होता, ताकि अप्रयुक्त स्थान कोई भौतिक मेमोरी न ले, लेकिन जैसा कि मेल गोर्मन ने समझाया, कर्नेल मैपिंग (वर्चुअल मेमोरी) और एक ही समय में भौतिक आवंटन करता है। ।
vinc17

1
@ मैक्स मैंने ओपी के कार्यक्रम की कोशिश की ulimit -s, जैसे कि ओपी की शर्तों के तहत 10240 देने के साथ , और मुझे उम्मीद के मुताबिक एक एसआईजीईजीवी मिलता है (यह वही है जो पोसिक्स द्वारा आवश्यक है: "यदि यह सीमा पार हो गई है, तो थ्रेड के लिए एसआईईजीएसवीवी उत्पन्न होगा। ")। मुझे ओपी के कर्नेल में एक बग पर संदेह है।
vinc17

6

लिनक्स कर्नेल 4.2

न्यूनतम परीक्षण कार्यक्रम

फिर हम इसका न्यूनतम NASM 64-बिट प्रोग्राम के साथ परीक्षण कर सकते हैं:

global _start
_start:
    sub rsp, 0x7FF000
    mov [rsp], rax
    mov rax, 60
    mov rdi, 0
    syscall

सुनिश्चित करें कि आप ASLR को बंद कर देते हैं और पर्यावरण चर को हटा देते हैं क्योंकि वे स्टैक पर जाएंगे और जगह लेंगे:

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
env -i ./main.out

सीमा मेरे ulimit -s( मेरे लिए 8MiB) से थोड़ी नीचे है । ऐसा लगता है कि अतिरिक्त सिस्टम V के कारण है क्योंकि शुरू में डेटा पर्यावरण के अलावा स्टैक पर रखा गया था: विधानसभा में लिनक्स 64 कमांड लाइन पैरामीटर | स्टैक ओवरफ़्लो

यदि आप इसके बारे में गंभीर हैं, तो TODO एक न्यूनतम initrd छवि बनाता है जो स्टैक शीर्ष से लिखना शुरू करता है और नीचे जाता है, और फिर इसे QEMU + GDB के साथ चलाता है । डाल दियाdprintfस्टैक एड्रेस को प्रिंट करने वाले लूप पर , और एक ब्रेकपॉइंट पर acct_stack_growth। यह गौरवशाली होगा।

सम्बंधित:


2

डिफ़ॉल्ट रूप से, अधिकतम स्टैक आकार को 8MB प्रति प्रक्रिया के लिए कॉन्फ़िगर किया गया है,
लेकिन इसका उपयोग करके इसे बदला जा सकता हैulimit :

KB में डिफ़ॉल्ट दिखा रहा है:

$ ulimit -s
8192

असीमित पर सेट करें:

ulimit -s unlimited

वर्तमान शेल और उपधाराओं और उनके बच्चे की प्रक्रियाओं को प्रभावित करना।
(ulimit शेल शेल कमांड है)

आप
cat /proc/$PID/maps | grep -F '[stack]'
लिनक्स पर प्रयोग के साथ वास्तविक स्टैक एड्रेस रेंज दिखा सकते हैं ।


इसलिए जब कोई प्रोग्राम वर्तमान शेल द्वारा लोड किया जाता है, तो OS ulimit -sप्रोग्राम के लिए KB के मेमोरी सेगमेंट को वैध बना देगा । मेरे मामले में यह 10240KB है। लेकिन जब मैं एक स्थानीय सरणी char a[10240*1024]और सेट घोषित करता हूं a[0]=1, तो प्रोग्राम सही तरीके से बाहर निकलता है। क्यों?
आमोस

अंतिम तत्व को भी सेट करने का प्रयास करें। और सुनिश्चित करें कि वे दूर अनुकूलित नहीं हैं।
vinc17

@ मेमोस मुझे लगता है कि vinc17 का क्या मतलब है कि आपने एक मेमोरी क्षेत्र का नाम दिया है जो आपके प्रोग्राम में स्टैक पर फिट नहीं होगा , लेकिन जैसा कि आप वास्तव में इसे उस हिस्से में एक्सेस नहीं करते हैं जो फिट नहीं होता है , मशीन कभी नोटिस नहीं करती है - यह नहीं है यहां तक ​​कि जानकारी प्राप्त करें
वोल्कर सिएगल

@ मेमोस ट्राई int n = 10240*1024; char a[n]; memset(a,'x',n);... सेग फॉल्ट।
गोल्डीलॉक्स

2
@ मेमोस तो, जैसा कि आप देख सकते हैं, a[]आपके 10MB स्टैक में आवंटित नहीं किया गया है। संकलक ने देखा हो सकता है कि एक पुनरावर्ती कॉल नहीं हो सकता है और विशेष आवंटन किया है, या कुछ और जैसे कि एक स्टैक या कुछ अप्रत्यक्ष।
vinc17
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.