1. 程式人生 > >你知道執行緒棧嗎(私有棧)

你知道執行緒棧嗎(私有棧)

問題

1. local 變數的壓棧和出棧過程
void func1(){
    int a = 0;
    int b = 0;
}
系統中有一個棧頂指標,每次分配和回收local 變數時,其實就是移動棧指標。

2. static local變數的分配風險
void func2(){
    static int a = 0;
}
這個變數a可能會被分配多次,因為如果func2可能同時被多個執行緒呼叫,也就是函式在分配記憶體時是可能出現執行緒切換的。

問題:

void func3(){
int a;
int b;
}

void func4(){
int c;
int d;
}
假設,func3和func4分別被兩個執行緒呼叫,並且func3先於func4執行,並且4個變數壓棧的順序分別是a、b、c、d。按照上面第1個說明,這個時候棧頂指標指向d。
如果,這個時候func3先執行完,那麼這個時候,系統要回收b和a,但是b並不在棧頂,所以,無法移動棧頂指標,所以,b和a無法回收。最複雜的情況可能如下,壓棧的順序是a、c、d、b,這個時候b可以正常回收。當要回收a時,會不會誤把d當作a給回收了?應該怎麼解釋這個問題呢。
顯然,事實上並非上面所述,因為執行緒裡有一個很重要的屬性stacksize,它讓我們隱約感覺到,執行緒是擁有私有的棧空間的,如果這樣,abcd的壓棧出棧就不會有問題了,因為他們並不儲存在一起。

pthread執行緒棧

  1. #include <stdio.h>
  2. #include <pthread.h>
  3. void* thread1(void* a)  
  4. {  
  5.     char m[8388608];  
  6.     printf("thread1\n");  
  7. }  
  8. int main(){  
  9.     pthread_t pthread_id;  
  10.     pthread_attr_t thread_attr;  
  11.     int status;  
  12.     status = pthread_attr_init(&thread_attr);  
  13.     if(status != 0)  
  14.         printf("init error\n");  
  15.     size_t stacksize = 100;  
  16.     status = pthread_attr_getstacksize(&thread_attr, &stacksize);  
  17.     printf("stacksize(%d)\n", stacksize);  
  18.     //printf("size(%d)\n", sizeof(int));
  19.     status = pthread_create(&pthread_id, NULL, thread1, NULL);  
  20.     while(1)  
  21.     {}  
  22.     return 0;  
  23. }  

執行結果:

stacksize(8388608)
段錯誤

分析

pthread_attr_getstacksize可以獲得執行緒的私有棧的大小,我這個機器是8388608位元組,為8M,也就是私有棧最大是8M,所以,建立的一個執行緒函式裡有個區域性陣列長度為8M,顯示段錯誤(雖然陣列大小和私有棧一樣大,但是私有棧除了分配區域性變數外,還要儲存一些管理資訊,所以肯定要小於8M),如果將陣列長度減小一定的值,就可以看到thread1函式的列印資訊。

pthread執行緒記憶體佈局


我們從圖上可以看出,兩個執行緒之間的棧是獨立的,其他是共享的,所以,在操作共享區域的時候才有可能出現同步需要,操作棧不需要同步。

最後我們知道,pthread也提供了私有堆機制,關於私有堆機制在以後說明。