2011-12-30 4 views
5

हम यह जानकारी कैसे प्राप्त कर सकते हैं? मुझे लगता है कि यह ओएस निर्भर है, और मैं विंडोज़ चला रहा हूं इसलिए मेरा प्रश्न विंडोज एपीआई को संदर्भित करता है।हम स्टैक स्थिति को कैसे मतदान कर सकते हैं - अप्रयुक्त (उपलब्ध) मेमोरी

क्या कोई ऐसा फ़ंक्शन है जो हमारे लिए ऐसा कर सकता है - कॉलिंग थ्रेड के लिए शेष स्टैक मेमोरी प्राप्त करें?

वैकल्पिक रूप से, अगर हम निम्नलिखित विवरण प्राप्त कर सकता है, हम गणना करने के लिए है कि हमारे अपने पर सक्षम हो जाएगा:

  1. धागा ढेर आधार पते प्राप्त करें। कुछ फ़ंक्शन होना चाहिए जो पैरामीटर के रूप में थ्रेड पहचानकर्ता लेता है, और इसके बारे में कुछ जानकारी देता है (जैसे ... स्टैक बेस पता?)
  2. थ्रेड स्टैक आकार प्राप्त करें। अगर धागा हमारे द्वारा शुरू किया गया था, तो हम इसे जान सकते हैं (चूंकि हमने इसे CreateThread पर कॉल करते समय निर्दिष्ट किया है)। लेकिन अगर यह मुख्य धागा है, जिसे हमारे कार्यक्रम के लिए ओएस द्वारा शुरू किया गया था, या कोई अन्य धागा हम स्पष्ट रूप से शुरू नहीं किया था, तो हम इसे कैसे पा सकते हैं?
  3. वर्तमान स्टैक पॉइंटर प्राप्त करें। अच्छा, यह आसान है। हम या तो esp के साथ इसे देख सकते हैं, या एक नजदीक स्थान प्राप्त करने के लिए, स्थानीय चर का पता ले सकते हैं।

यह शैक्षिक उद्देश्यों के लिए है, लेकिन मैं इसे एक ढेर अतिप्रवाह के कारण से पुनरावर्ती एल्गोरिदम को रोकने के लिए इस्तेमाल किया जा सकता है लगता है - के बजाय किसी भी अधिकतम गहराई से समारोह सीमित इस्तेमाल करते हैं।

+0

ध्यान दें कि [इटेनियम में * दो स्टैक *] हैं (http://blogs.msdn.com/b/oldnewthing/archive/2005/04/21/410397.aspx)। अब तक, हर कोई केवल स्थानीय परिवर्तनीय ढेर पर देख रहा है, लेकिन रजिस्टर स्टैक भी है (जहां अन्य चीजों के साथ वापसी पते जाते हैं)। तो यह तकनीक स्टैक ओवरफ़्लो को रोकने के लिए पर्याप्त नहीं है क्योंकि आप केवल एक ढेर की जांच कर रहे हैं। –

+0

दिलचस्प ... लेकिन अगर यह "रजिस्टर स्टैक" वास्तव में * हार्डवेयर * आश्रित है, तो इंटेल या जो भी लागू होता है, उसे स्थानीय चर आवंटन के लिए नियमित कॉल स्टैक का उपयोग नहीं करना चाहिए यदि रजिस्टर स्टैक भरा हुआ हो; आखिरकार, स्थानीय चरों को स्टोर करने के लिए रजिस्टरों का उपयोग करना जरूरी नहीं है, है ना? – Jong

+0

कोई कानून नहीं है जो कहता है कि सभी प्रोसेसर x86 की तरह होना चाहिए। (और Itanium रजिस्टर रजिस्टर विंडो के साथ शायद ही एकमात्र प्रोसेसर है।) –

उत्तर

5

आप NtCurrentTeb(), जो आप TEB के लिए सूचक हो जाता है का उपयोग कर सकते यह अपनी पहली सदस्य के रूप में NT_TIB है। :

typedef struct _NT_TIB 
{ 
    PEXCEPTION_REGISTRATION_RECORD ExceptionList; 
    PVOID StackBase; 
    PVOID StackLimit; 
    PVOID SubSystemTib; 
    // .... 
} NT_TIB, *PNT_TIB; 
+0

कोई TEB क्या है, वैसे भी? –

+2

थ्रेड एनवायरनमेंट ब्लॉक (http://msdn.microsoft.com/en-us/library/windows/hardware/ms686708) जो मुझे NT_TIB गुम है ... :-( – alk

+0

@Pete विल्सन: थ्रेड पर्यावरण ब्लॉक, जहां सभी प्रकार के थ्रेड-विशिष्ट डेटा संग्रहीत किए जाते हैं। – wj32

-1

संपादित करें: शैक्षणिक उद्देश्यों के लिए यह एक उत्कृष्ट उत्कृष्ट प्रश्न है! इसके लिए एक उथल-पुथल है। स्टैक स्पेस तय किया गया है संपादित करें: इस बिंदु पर कि एक प्रक्रिया या धागा निष्पादन शुरू होता है; मुझे लगता है कि आपको ढेर का मतलब होना चाहिए, जिससे स्मृति गतिशील रूप से आवंटित की जाती है (उदाहरण के लिए, malloc() द्वारा। इस प्रश्न की एक अच्छी चर्चा यहां on MSDN पर है। मुझे सटीक एपीआई कॉल नहीं दिख रहा है जिसे आप ढूंढ रहे हैं: आप 'कि लिए चारों ओर से प्रहार करने के लिए होगा;। यह बहुत दूर नहीं किया जा सकता

HTH

+0

'CreateThread' को कॉल करते समय आप थ्रेड के स्टैक आकार को निर्दिष्ट कर सकते हैं। हो सकता है कि अंतरिक्ष निर्धारित होने के बाद तय हो जाए, लेकिन प्रत्येक थ्रेड में एक और ढेर आकार हो सकता है। .NET धागे आमतौर पर ~ 1 एमबी स्टैक होते हैं, सी प्रोग्राम के सामान्य धागे में 64 केबी लगता है। मैंने विभिन्न आकारों के साथ कई प्रयोग किए हैं, यहां तक ​​कि 1 गीगाबाइट (अधिकतम विंडोज़ 1.4 जीबी थी), जो आपको करने देता है .. अनंत रिकर्सन, काफी ज्यादा। संक्षेप में, ढेर का आकार ** ** ** तय नहीं है। – Jong

+0

आम तौर पर जब थ्रेड बनाते हैं तो आप उस थ्रेड के लिए स्टैक आकार निर्दिष्ट करते हैं। उदाहरण के लिए यदि आप pthread_create() का उपयोग करते हैं, तो आप उन गुणों को पास कर सकते हैं जिनमें स्टैक आकार शामिल है। आप वर्तमान थ्रेड के लिए क्रमशः pthread_attr_getstackaddr() और pthread_attr_getstacksize() के साथ स्टैक बेस पता और आकार भी प्राप्त कर सकते हैं। मैं विंडोज़ पर नहीं जानता, हालांकि। – user1118321

+0

@ जोंग: हाँ। वास्तव में दो ढेर "आकार" हैं: आरक्षित और प्रतिबद्ध। ढेर को आसानी से आकार दिया जा सकता है बशर्ते पर्याप्त पता स्थान ऊपर की ओर हो। – wj32

1

नहीं सीधे ओपी के सवाल का जवाब देने, लेकिन यह विचार उस पर उल्लेख की चर्चा करते हुए अंत है: "... यह टी इस्तेमाल किया जा सकता ओ एक ढेर अतिप्रवाह के कारण से पुनरावर्ती एल्गोरिदम रोक -। के बजाय समारोह सीमित किसी भी अधिकतम गहराई का उपयोग कर "

विंडोज एपीआई विधि SetThreadStackGuarantee() जो जब एक ढेर अतिप्रवाह फेंक एक न्यूनतम ढेर आकार उपलब्ध रखने के लिए निर्धारित कर सकते हैं प्रदान करता है अपवाद। वीसी रनटाइम लाइब्रेरी विधि _resetstkoflw() के साथ इस विधि को स्टैक ओवरफ़्लो से पुनर्प्राप्त करना संभव हो सकता है।

विवरण के लिए एमएसडीएन पर this देखें।

1
  1. धागा ढेर आधार पते प्राप्त करें: wj32 showed के रूप में, धागा जानकारी ब्लॉक के StackBase का उपयोग करें।

  2. थ्रेड स्टैक आकार प्राप्त करें: आरक्षित आरक्षित आकार (जो कि यह अधिकतम आकार है) निर्धारित करें। StackLimit शो lowest commited address है, जो दिखा सकता है कि ढेर कितनी बड़ी हो गई है, इसकी सीमा नहीं है। यह भी नहीं कि CreateThread पर जाने वाले स्टैक आकार प्रारंभिक प्रतिबद्ध आकार है, आरक्षित आकार नहीं जब तक आप STACK_SIZE_PARAM_IS_A_RESERVATION ध्वज पास नहीं करते। आपके प्रोग्राम का स्टैक आकार linker parameter द्वारा निर्दिष्ट किया गया है और यदि आप निर्दिष्ट नहीं करते हैं तो 1 एमबी तक डिफ़ॉल्ट है। तो सबसे अधिक संभावना है कि सभी धागे में 1 एमबी स्टैक आरक्षण हो।

    के अंतिम पृष्ठ के बाद से stack is a guard page आप क़यास StackPage से शुरू करते हैं और प्रत्येक में कम ढेर पेज VirtualQuery जाँच गार्ड पेज कि ढेर के अंत होगा खोजने के लिए कर सकते हैं। यह निश्चित रूप से कार्यान्वयन परिभाषित व्यवहार पर निर्भर है।

  3. वर्तमान ढेर सूचक प्राप्त करें: आप StackLimit का उपयोग अपने ढेर की अधिकतम प्रतिबद्ध आकार पाने के लिए कर सकता है, लेकिन यह है कि वर्तमान सूचक के रूप में ही नहीं है। esp स्पष्ट रूप से वर्तमान स्टैक स्थिति है और StackLimit से अधिक हो सकता है।

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

संपादित

गार्ड पेज से काम नहीं चलेगा जाँच पर मेरे विचार। मैंने एक परीक्षण कार्यक्रम लिखा और गार्ड पेज प्रतिबद्ध सीमा पर सेट है, इसलिए यह काम नहीं करता है। लेकिन मुझे लगता है कि स्टैक पर कहीं भी VirtualQuery चलाना स्टैक पर सबसे कम पते के AllocationBase देगा, क्योंकि रिजर्व आकार एक बार आवंटित किया गया था। निम्न उदाहरण कार्रवाई में यह पता चलता है:

#include <windows.h> 
#include <WinNT.h> 
#include <stdio.h> 

DWORD GetThreadStackSize() 
{ 
    SYSTEM_INFO systemInfo = {0}; 
    GetSystemInfo(&systemInfo); 

    NT_TIB *tib = (NT_TIB*)NtCurrentTeb(); 
    DWORD_PTR stackBase = (DWORD_PTR)tib->StackBase; 

    MEMORY_BASIC_INFORMATION mbi = {0}; 
    if (VirtualQuery((LPCVOID)(stackBase - systemInfo.dwPageSize), &mbi, sizeof(MEMORY_BASIC_INFORMATION)) != 0) 
    { 
     DWORD_PTR allocationStart = (DWORD_PTR)mbi.AllocationBase; 
     return stackBase - allocationStart; 
    } 
    return 0; 
} 

DWORD WINAPI ThreadRtn(LPVOID param) 
{ 
    DWORD stackSize = GetThreadStackSize(); 
    printf("%d\n", stackSize); 
    return 0; 
} 

int main() 
{ 
    ThreadRtn(NULL); 
    HANDLE thread1 = CreateThread(NULL, 65535, ThreadRtn, NULL, 0, NULL); 
    WaitForSingleObject(thread1, -1); 
    HANDLE thread2 = CreateThread(NULL, 65535, ThreadRtn, NULL, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL); 
    WaitForSingleObject(thread2, -1); 

    return 0; 
} 

यह आउटपुट:

के रूप में यह होना चाहिए।

+0

वास्तव में काम करता है। और मैं इसके साथ कुछ और परीक्षण करने में कामयाब रहा, जैसे किसी विधि को कितने बाइट्स लेते हैं और किसी दिए गए बिंदु पर कितने बाइट स्टैक पर रहते हैं। – Jong

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