[linux] x86_64スレッドスタックがどのように割り当てられているか


一、背景
プロセスの空間アドレスは、コード領域(text)、読み取り専用データ領域(rodata)、初期化データ領域(data)、初期化データ領域(bss)、
スタック(heap)、共有メモリ領域(.so,mmapの場所)、スタック(stack)、カーネル領域(kernel).
ヒープヒープ:下から上へ成長
スタック:上から下へ成長
ここで、スタックはプロセススタック(またはプライマリラインスタック)と呼ぶことができますが、プライマリプロセス(プライマリスレッド)の外に新しいサブスレッドが作成されると、スレッドとプロセスはlinuxであまり区別されず、task_に格納されていることがわかります.structでは,区別はプロセスがforkを通過し,最初は親プロセスの仮想アドレス空間のみをコピーすることであるが,これらの仮想アドレス空間は書き込み時コピー(cow)技術を利用し,その中のセグメント(または領域),前述したデータ領域など,更新操作があればサブプロセスに新しい物理空間を割り当て,意味的に独立した空間アドレスを実現する.スレッドの場合、親プロセスの仮想アドレス空間もコピーされますが、プライベートではなく共有に設定すると、1つのプロセスに相当するすべてのスレッドが同じ仮想空間アドレスを共有します.
 
二、疑問
背景によると、スレッドは親プロセスの仮想アドレス空間を共有することを知っていますが、スレッドのスタックがプライベートであることも知っています.では、どのように実現されていますか.ここで直接答えを出すと、スレッド(非プライマリスレッド)のスタックのサイズは固定されており、空きスタック(スタックトップ付近でトップダウン割り当て)または空きスタック(スタックボトム付近でボトムアップ割り当て)であるため、スレッドスタックのローカル関数に割り当てられた変数は各自己割り当てスタック空間に格納されるため、スレッドプライベートと言える.また、スレッドスタックの境界が設定されているため、スレッドスタックのサイズは固定されている.
 
三、例説明
3.1 ulimit-a、stack size(-s)8192を見て、これは現在のシステムがプログラムを実行する時のスタックの大きさで、ここのスタックの大きさはスレッドスタックの大きさを指して、すべての新しいスレッドはすべてこの値の大きさでスレッドスタックの大きさを割り当てて、だからこれは各スレッドのスタックの大きさで、1つのプロセスのスタックの大きさの総和ではありませんて、単位kb
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 15661
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 65535
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 15661
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

3.2次のプログラムを実行し、cat/proc/{pid}/mapsにおける仮想アドレスの割り当て状況を確認する
#include 
#include 
#include 

void *task(void *a) {
    for (;;) {
        sleep(1000);
    }
}

int main() {
    pthread_t pid1, pid2, pid3;
    int rt = -1;
    rt = pthread_create(&pid1, NULL, task, NULL);
    rt = pthread_create(&pid2, NULL, task, NULL);
    rt = pthread_create(&pid3, NULL, task, NULL);
    pthread_join(pid1, NULL);     
    pthread_join(pid2, NULL);     
    pthread_join(pid3, NULL);     
    return 0;
}

cat/proc/{pid}/maps、[heap]の下に連続する7 f 341157 f 000-7 f 3411 d 7 f 000 rw-p、7 f 3411 d 80000-7 f 341258000 rw-p、7 f 3412581000-7 f 3412 d 81000 rw-pが見えます.この3つの位置は3つのスレッドスタックに対応し、大きさはちょうど8*2^20=8192 kb=8 mbです(注意深くこの3つのセグメントには、この3つのスレッドスタックを分割するために小さな境界値が挿入されています).ここから,heapの上部付近から下プロセスに割り当てられた固定サイズ8192 kb(ulimit aで一致)の3つのスレッドスタックが見られる.
00400000-00401000 r-xp 00000000 fd:01 1326537                            /tmp/c/test_pthread/main
00600000-00601000 r--p 00000000 fd:01 1326537                            /tmp/c/test_pthread/main
00601000-00602000 rw-p 00001000 fd:01 1326537                            /tmp/c/test_pthread/main
0212d000-0214e000 rw-p 00000000 00:00 0                                  [heap]
7f341157e000-7f341157f000 ---p 00000000 00:00 0 
7f341157f000-7f3411d7f000 rw-p 00000000 00:00 0 
7f3411d7f000-7f3411d80000 ---p 00000000 00:00 0 
7f3411d80000-7f3412580000 rw-p 00000000 00:00 0 
7f3412580000-7f3412581000 ---p 00000000 00:00 0 
7f3412581000-7f3412d81000 rw-p 00000000 00:00 0 
7f3412d81000-7f3412f41000 r-xp 00000000 fd:01 402248                     /lib/x86_64-linux-gnu/libc-2.23.so
7f3412f41000-7f3413141000 ---p 001c0000 fd:01 402248                     /lib/x86_64-linux-gnu/libc-2.23.so
7f3413141000-7f3413145000 r--p 001c0000 fd:01 402248                     /lib/x86_64-linux-gnu/libc-2.23.so
7f3413145000-7f3413147000 rw-p 001c4000 fd:01 402248                     /lib/x86_64-linux-gnu/libc-2.23.so
7f3413147000-7f341314b000 rw-p 00000000 00:00 0 
7f341314b000-7f3413163000 r-xp 00000000 fd:01 402247                     /lib/x86_64-linux-gnu/libpthread-2.23.so
7f3413163000-7f3413362000 ---p 00018000 fd:01 402247                     /lib/x86_64-linux-gnu/libpthread-2.23.so
7f3413362000-7f3413363000 r--p 00017000 fd:01 402247                     /lib/x86_64-linux-gnu/libpthread-2.23.so
7f3413363000-7f3413364000 rw-p 00018000 fd:01 402247                     /lib/x86_64-linux-gnu/libpthread-2.23.so
7f3413364000-7f3413368000 rw-p 00000000 00:00 0 
7f3413368000-7f341338e000 r-xp 00000000 fd:01 402246                     /lib/x86_64-linux-gnu/ld-2.23.so
7f341357f000-7f3413583000 rw-p 00000000 00:00 0 
7f341358d000-7f341358e000 r--p 00025000 fd:01 402246                     /lib/x86_64-linux-gnu/ld-2.23.so
7f341358e000-7f341358f000 rw-p 00026000 fd:01 402246                     /lib/x86_64-linux-gnu/ld-2.23.so
7f341358f000-7f3413590000 rw-p 00000000 00:00 0 
7fffa7bf4000-7fffa7c15000 rw-p 00000000 00:00 0                          [stack]
7fffa7d05000-7fffa7d08000 r--p 00000000 00:00 0                          [vvar]
7fffa7d08000-7fffa7d0a000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

3.3 stackベース付近に割り当てる、ulimit-s unlimited(ここではstack sizeが無限に設定されているが、実際には無限ではなく、固定サイズのスレッドスタックであり、サイズは1 mbである)
ulimit -s unlimited 
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 15661
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 65535
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) unlimited
cpu time               (seconds, -t) unlimited
max user processes              (-u) 15661
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

cat/proc/{pid}/maps,[stack]を上に見ると,2 b 4 f 2001002000-2 b 4 f 22012000 rw-p,2 b 4 f 22013000-2 b 4 f 2041000 rw-p,2 b 4 f 2044000-2 b 4 f 20614000 rw-pの3つの大きさはいずれも1 mbであり,サイズは一致しています(プログラムに1 mb近くのローカル変数データを割り当ててテストすると、現在のスレッドスタックが無限ではないか、固定サイズが1 mb程度に近いかがわかります).従って,この場合,スレッドスタックはstack基板付近に割り当てられ,基板から上向きに成長した.
00400000-00401000 r-xp 00000000 fd:01 1326537                            /tmp/c/test_pthread/main
00600000-00601000 r--p 00000000 fd:01 1326537                            /tmp/c/test_pthread/main
00601000-00602000 rw-p 00001000 fd:01 1326537                            /tmp/c/test_pthread/main
0180a000-0182b000 rw-p 00000000 00:00 0                                  [heap]
2b4f1f802000-2b4f1f828000 r-xp 00000000 fd:01 402246                     /lib/x86_64-linux-gnu/ld-2.23.so
2b4f1f832000-2b4f1f835000 rw-p 00000000 00:00 0 
2b4f1fa27000-2b4f1fa28000 r--p 00025000 fd:01 402246                     /lib/x86_64-linux-gnu/ld-2.23.so
2b4f1fa28000-2b4f1fa29000 rw-p 00026000 fd:01 402246                     /lib/x86_64-linux-gnu/ld-2.23.so
2b4f1fa29000-2b4f1fa2a000 rw-p 00000000 00:00 0 
2b4f1fa2a000-2b4f1fa42000 r-xp 00000000 fd:01 402247                     /lib/x86_64-linux-gnu/libpthread-2.23.so
2b4f1fa42000-2b4f1fc41000 ---p 00018000 fd:01 402247                     /lib/x86_64-linux-gnu/libpthread-2.23.so
2b4f1fc41000-2b4f1fc42000 r--p 00017000 fd:01 402247                     /lib/x86_64-linux-gnu/libpthread-2.23.so
2b4f1fc42000-2b4f1fc43000 rw-p 00018000 fd:01 402247                     /lib/x86_64-linux-gnu/libpthread-2.23.so
2b4f1fc43000-2b4f1fc47000 rw-p 00000000 00:00 0 
2b4f1fc47000-2b4f1fe07000 r-xp 00000000 fd:01 402248                     /lib/x86_64-linux-gnu/libc-2.23.so
2b4f1fe07000-2b4f20007000 ---p 001c0000 fd:01 402248                     /lib/x86_64-linux-gnu/libc-2.23.so
2b4f20007000-2b4f2000b000 r--p 001c0000 fd:01 402248                     /lib/x86_64-linux-gnu/libc-2.23.so
2b4f2000b000-2b4f2000d000 rw-p 001c4000 fd:01 402248                     /lib/x86_64-linux-gnu/libc-2.23.so
2b4f2000d000-2b4f20011000 rw-p 00000000 00:00 0 
2b4f20011000-2b4f20012000 ---p 00000000 00:00 0 
2b4f20012000-2b4f20212000 rw-p 00000000 00:00 0 
2b4f20212000-2b4f20213000 ---p 00000000 00:00 0 
2b4f20213000-2b4f20413000 rw-p 00000000 00:00 0 
2b4f20413000-2b4f20414000 ---p 00000000 00:00 0 
2b4f20414000-2b4f20614000 rw-p 00000000 00:00 0 
7ffd283e1000-7ffd28402000 rw-p 00000000 00:00 0                          [stack]
7ffd2842c000-7ffd2842f000 r--p 00000000 00:00 0                          [vvar]
7ffd2842f000-7ffd28431000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

 
四、まとめ
スレッドスタックがスタックに割り当てられてもスタックに割り当てられても、固定サイズで境界があります.また、特定のスレッドで使用されているため、スレッドがプライベートとも言える(これは通常の使用状況ですが、いずれかのスレッドのローカル変数アドレスを別のスレッドに渡す場合も間接的にアクセスできますが、通常は他のスレッドのローカル変数にアクセスしません)