【操作系統】線程棧如何分配

測試環境:
Linux centos-7.shared 3.10.0-693.5.2.el7.x86_64 #1 SMP Fri Oct 20 20:32:50 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

一個進程的虛擬地址空間一般可以大致劃分爲代碼區(text)、只讀數據區(rodata)、初始化數據區(data)、爲初始化數據區(bss)、堆(heap)、共享內存區(.so,mmap的地方)、棧(stack)、內核區(kernel)。

在這裏插入圖片描述

對於 Linux 進程或者說主線程,其 stack 是在 fork 的時候生成的,實際上就是複製了父親的 stack 空間地址,然後寫時拷貝 (cow) 以及動態增長。
然而對於主線程生成的子線程而言,其 stack 將不再是這樣的了,而是事先固定下來的。
線程棧不能動態增長,一旦用盡就沒了,這是和生成進程的 fork 不同的地方。

線程(非主線程)的棧的大小是固定的,其會在空閒的堆(堆頂附近自頂向下分配)或者是空閒棧(棧底附近自底向上分配),因此線程棧局部函數中分配的變量是存放到各自分配的棧空間,因此可以說是線程私有的,又因爲該線程棧的邊界是設定好的,因此該線程棧的大小的固定的。

測試

ulimit -a 查看操作系統的相關限制:
可以看到 stack size 的限制是 8192kb 也就是 8MB。
注意這裏的 8MB 是指每個被創建的 thread 的 stack 都是這麼大。

[parallels@centos-7 LinuxCode]$ ulimit -a
......
stack size              (kbytes, -s) 8192
......

測試代碼:
創建了 3 個 thread,執行 ThreadEntry。
編譯後跑起來!

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void*
ThreadEntry(void* args)
{
    (void) args;
    while (1)
    {
        sleep(1);
    }
}

int main()
{
    pthread_t tid1, tid2, tid3;
    pthread_create(&tid1, NULL, ThreadEntry, NULL);
    pthread_create(&tid2, NULL, ThreadEntry, NULL);
    pthread_create(&tid3, NULL, ThreadEntry, NULL);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);
    return 0;
}

從 heap 的頂部向下分配。
ps aux | grep a.out 查看 pid
cat /proc/[pid]/maps 這個顯示進程映射了的內存區域和訪問權限。
可以看到:在 heap 下面連續的幾個屬性爲 rw-p 的地址大小剛好都爲 8192kb。並且每個都在邊界穿插了一個大小爲 1000H(4096kb) 的邊界空間。

在這裏插入圖片描述

從 stack 底部向上分配
ulimit -s unlimited 設置 stack size 爲 unlimited,注意雖然設置了stack size爲無限,但是實際上其並不是無限的,而也是固定大小的線程棧,大小爲1mb。
然後 cat /proc/[pid]/maps 查看虛擬地址空間的映射。
可以看到,這種情況下線程棧是分配在 stack 底附近,自底向上生長的。
在這裏插入圖片描述
在這裏插入圖片描述

結論

不管線程棧是在堆分配還是在棧分配,其都是固定大小的,有邊界的。


參考:
https://blog.csdn.net/qq_16097611/article/details/82592873
https://blog.csdn.net/yangkuanqaz85988/article/details/52403726
https://blog.csdn.net/lijzheng/article/details/23618365

EOF

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章