2011-12-30 23 views
5

Làm cách nào để chúng tôi có được thông tin này? Tôi đoán nó là hệ điều hành phụ thuộc, và tôi đang chạy Windows vì vậy câu hỏi của tôi đề cập đến các API cửa sổ.Làm cách nào chúng tôi có thể thăm dò trạng thái ngăn xếp - bộ nhớ không sử dụng (có sẵn)

Có chức năng nào có thể làm điều đó cho chúng tôi - nhận bộ nhớ ngăn xếp còn lại cho chuỗi cuộc gọi không?

Ngoài ra, nếu chúng ta có thể tìm hiểu các chi tiết sau đây, chúng tôi sẽ có thể tính toán rằng ngày của riêng của chúng tôi:

  1. Lấy thread stack cơ sở địa chỉ. Phải có một số hàm nhận dạng chuỗi làm thông số và trả về một số thông tin về nó (Chẳng hạn như ... Địa chỉ cơ sở ngăn xếp?)
  2. Lấy kích thước ngăn xếp chủ đề. Nếu chủ đề đã được chúng tôi bắt đầu, chúng tôi có thể biết nó (Vì chúng tôi đã chỉ định nó khi gọi CreateThread). Nhưng nếu nó là chủ đề chính, được bắt đầu bởi hệ điều hành cho chương trình của chúng tôi, hoặc bất kỳ chủ đề nào khác chúng tôi đã không bắt đầu một cách rõ ràng, làm thế nào để chúng tôi tìm thấy nó?
  3. Lấy con trỏ ngăn xếp hiện tại. Vâng, đó là dễ dàng hơn. chúng tôi có thể kiểm tra nó bằng esp hoặc lấy địa chỉ của biến địa phương để nhận vị trí gần.

Đây là mục đích giáo dục, nhưng tôi đoán nó có thể được sử dụng để ngăn chặn thuật toán đệ quy gây tràn ngăn xếp - Thay vì sử dụng bất kỳ hàm giới hạn độ sâu tối đa nào.

+0

Lưu ý rằng [Itanium có * hai ngăn xếp *] (http://blogs.msdn.com/b/oldnewthing/archive/2005/04/21/410397.aspx). Cho đến nay, tất cả mọi người chỉ nhìn vào ngăn xếp biến cục bộ, nhưng cũng có ngăn xếp đăng ký (đó là nơi các địa chỉ trả về đi, trong số những thứ khác). Vì vậy, kỹ thuật này là không đủ để ngăn chặn tràn ngăn xếp bởi vì bạn đang kiểm tra chỉ một trong các ngăn xếp. –

+0

Thú vị ... Nhưng nếu "ngăn xếp đăng ký" này thực sự * phụ thuộc phần cứng *, không nên Intel hoặc bất cứ ai thực hiện nó sử dụng ngăn xếp cuộc gọi thông thường để phân bổ biến cục bộ nếu ngăn xếp đăng ký đầy; Sau khi tất cả, sử dụng đăng ký để lưu trữ các biến địa phương không phải là phải, phải không? – Jong

+0

Không có luật nào nói rằng tất cả các bộ vi xử lý phải giống như x86. (Và Itanium không phải là bộ vi xử lý duy nhất có cửa sổ đăng ký.) –

Trả lời

5

bạn có thể sử dụng NtCurrentTeb(), mà được bạn một con trỏ đến TEB này có NT_TIB như thành viên đầu tiên của mình. :

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

TEB là gì? –

+2

Khối Môi trường Chủ đề (http://msdn.microsoft.com/en-us/library/windows/hardware/ms686708) từ những gì tôi thiếu NT_TIB ... :-( – alk

+0

@Pete Wilson: Khối môi trường chủ đề, – wj32

-1

chỉnh sửa: Đây là câu hỏi tuyệt vời xuất sắc cho mục đích giáo dục! Có một upvote cho điều đó. Không gian ngăn xếp được sửa cố định chỉnh sửa: tại điểm quá trình hoặc luồng bắt đầu thực hiện; Tôi nghĩ rằng bạn phải có nghĩa là heap, từ đó bộ nhớ được tự động phân bổ (bởi malloc(), ví dụ .. Có một cuộc thảo luận tốt về câu hỏi này trên đây on MSDN.Tôi không thấy các cuộc gọi API chính xác bạn đang tìm kiếm: bạn sẽ phải poke xung quanh cho rằng;. nó không thể là quá xa

HTH

+0

Khi gọi 'CreateThread', bạn có thể chỉ định kích thước ngăn xếp của luồng. Có lẽ không gian được cố định khi nó được xác định, nhưng mỗi luồng có thể có kích thước ngăn xếp khác. NET chủ đề có thường ~ 1MB ngăn xếp, chủ đề thông thường của một chương trình C có 64KB tôi nghĩ. Tôi đã thực hiện nhiều thí nghiệm với các kích thước khác nhau, thậm chí 1 gigabyte (Các cửa sổ tối đa cho phép là 1,4 GB), cho phép bạn thực hiện .. đệ quy vô hạn, khá nhiều. Tóm lại, kích thước ngăn xếp là ** không ** cố định. – Jong

+0

Nói chung khi tạo một chuỗi bạn chỉ định kích thước ngăn xếp cho chuỗi đó. Ví dụ: nếu bạn sử dụng pthread_create(), bạn có thể chuyển các thuộc tính bao gồm kích thước ngăn xếp. Bạn cũng có thể nhận được địa chỉ và kích thước cơ sở stack cho luồng hiện tại với pthread_attr_getstackaddr() và pthread_attr_getstacksize(), tương ứng. Tôi không biết trên Windows, mặc dù. – user1118321

+0

@Jong: Có. Thực sự có hai "kích thước" ngăn xếp: dự trữ và cam kết. Ngăn xếp có thể được thay đổi kích thước một cách dễ dàng với điều kiện là có đủ không gian địa chỉ trở lên. – wj32

1

Không trả lời trực tiếp câu hỏi của OP, nhưng đề cập đến ý tưởng được đề cập ở cuối: "... nó có thể được sử dụng o dừng thuật toán đệ quy gây ra tràn ngăn xếp - Thay vì sử dụng bất kỳ chức năng giới hạn độ sâu tối đa nào. "

API Windows cung cấp phương thức ngăn xếp tối thiểu để có sẵn khi ném tràn ngăn xếp ngoại lệ. Phương pháp này cùng với phương pháp thư viện thời gian chạy VC _resetstkoflw() có thể làm cho nó có thể phục hồi từ tràn ngăn xếp.

Xem this trên MSDN để biết chi tiết.

1
  1. Lấy thread stack cơ sở địa chỉ: Như wj32 showed, sử dụng StackBase của khối thông tin chủ đề.

  2. Lấy kích thước ngăn xếp chủ đề: Xác định kích thước ngăn xếp được dành riêng cho chủ đề (có kích thước tối đa) khác nhau. Những gì các chương trình StackLimitlowest commited address, có thể hiển thị bao nhiêu stack đã từng phát triển, không phải là giới hạn của nó. Cũng không phải kích thước ngăn xếp bạn vượt qua tới CreateThread là kích thước cam kết ban đầu, không phải là kích thước dự trữ trừ khi bạn vượt qua cờ STACK_SIZE_PARAM_IS_A_RESERVATION. Kích thước ngăn xếp của chương trình của bạn được chỉ định bởi một linker parameter và mặc định là 1MB nếu bạn không chỉ định. Vì vậy, hầu hết tất cả các chủ đề đều có đặt trước 1MB.

    Kể từ trang cuối cùng của stack is a guard page bạn có thể tưởng tượng bắt đầu từ StackPage và kiểm tra từng trang ngăn xếp thấp hơn VirtualQuery để tìm trang gaurd sẽ là phần cuối của ngăn xếp. Đây là khóa học hoàn toàn dựa vào hành vi được xác định thực hiện.

  3. Lấy con trỏ ngăn xếp hiện tại: Bạn có thể sử dụng StackLimit để nhận kích thước cam kết tối đa của ngăn xếp, nhưng không giống như con trỏ hiện tại. esp rõ ràng là vị trí ngăn xếp hiện tại và có thể cao hơn StackLimit.

Lưu ý khi đặt trước và cam kết. Trong Windows dành riêng có nghĩa là các địa chỉ ảo đã được dành riêng để sử dụng và không thể được lấy cho một cái gì đó khác. Địa chỉ dành riêng không tiêu thụ bất kỳ bộ nhớ vật lý hoặc ảo nào. Một khi nó được cam kết nó địa chỉ sẽ được ánh xạ tới bộ nhớ vật lý hoặc ảo và có thể được sử dụng. Chủ đề người dùng Windows có kích thước ngăn xếp cố định ngăn xếp - không gian địa chỉ được dành riêng cho ngăn xếp và không thể tăng lên và kích thước cam kết biến - ngăn xếp sẽ chỉ sử dụng bộ nhớ (cam kết) khi cần.

Sửa

những suy nghĩ của tôi về kiểm tra trang guard sẽ không hoạt động. Tôi đã viết một chương trình thử nghiệm và trang bảo vệ được đặt ở giới hạn cam kết, do đó không hoạt động. Nhưng tôi đã thấy rằng việc chạy một VirtualQuery ở bất cứ nơi nào trên ngăn xếp sẽ cung cấp cho các AllocationBase của địa chỉ thấp nhất trên ngăn xếp, kể từ khi kích thước dự trữ được phân bổ cùng một lúc. Ví dụ sau đây cho thấy điều này trong hành động:

#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; 
} 

này kết quả đầu ra:

như nó phải.

+0

Hoạt động, thực sự. Và tôi đã thực hiện thêm một vài thử nghiệm với nó, chẳng hạn như số byte mà một phương thức nhận được và số byte còn lại trên stack tại một điểm đã cho. – Jong

Các vấn đề liên quan