堆棧大小的 ulimit - 每個程序或每個執行緒限制?
所以我們在 Solaris 上有一個程序,它的堆棧空間不足。
在對此進行調查時,我簡要了解了堆棧的 ulimit 是什麼:
user@solaris-box:~$ ulimit -a ... stack size (kbytes, -s) 8192
所以堆棧大小限制為 8 兆字節。但這是整個過程的極限嗎?
如果我的程序有 10 個執行緒怎麼辦,每個執行緒只允許 819k 嗎?(或它們的一些混合,高達 8MiB?)
我找不到任何關於此的文件。
概括
對於主執行緒,您必須在程序啟動之前
setrlimit()
呼叫(可能通過使用ulimit
),以確保較大的堆棧大小有效。對於由程序啟動的執行緒,您需要使用
pthread_attr_setstacksize()
,因為執行緒堆棧大小完全不受來自/的stack size
資源限制的影響。setrlimit()``getrlimit()
程式碼需要看起來像這樣:
pthread_attr attr; pthread_attr_init( &attr ); // 32MB stack size example - should **NOT** hardcode this // but get it from an environment variable or property setting size_t stacksize = 32UL * 1024UL * 1024UL; pthread_attr_setstacksize( &attr, stacksize ); pthread_create( &tid, &attr, start_func, thread_arg );
您可以從目前堆棧大小限制中獲取執行緒堆棧大小:
struct rlimit limits; getrlimit( RLIMIT_STACK, &limits ); size_t stacksize = limits.rlim_cur; // use rlim_max for hard limit
(請注意,如果您使用的是創建自己的執行緒的庫,則該庫可能有自己記錄的設置執行緒堆棧大小的方法,例如 OpenMPI。)
詳細解答
資源限制由
ulimit
實用程序從命令行設置。如果你跑
truss -f -a -vall -o /tmp/truss.out /usr/bin/ulimit -a
,你會看到address space limit (kbytes) (-M) unlimited core file size (blocks) (-c) unlimited cpu time (seconds) (-t) unlimited data size (kbytes) (-d) unlimited file size (blocks) (-f) unlimited locks (-x) not supported locked address space (kbytes) (-l) not supported message queue size (kbytes) (-q) not supported nice (-e) not supported nofile (-n) 1024 nproc (-u) 29995 pipe buffer size (bytes) (-p) 5120 max memory size (kbytes) (-m) not supported rtprio (-r) not supported socket buffer size (bytes) (-b) 5120 sigpend (-i) 128 stack size (kbytes) (-s) 8192 swap size (kbytes) (-w) not supported threads (-T) not supported process size (kbytes) (-v) unlimited
如果你調查一下
/tmp/truss.out
,你會看到7752: execve("/usr/bin/ulimit", 0xFFFF80FFBFFFF9E8, 0xFFFF80FFBFFFFA00) argc = 2 7752: argv: /usr/bin/ulimit -a 7752: sysinfo(SI_MACHINE, "i86pc", 257) = 6 much deleted extraneous data (loading shared libraries, etc)... 7752: getrlimit(RLIMIT_VMEM, 0xFFFF80FFBFFFD4B0) = 0 7752: cur = RLIM64_INFINITY max = RLIM64_INFINITY 7752: getrlimit(RLIMIT_CORE, 0xFFFF80FFBFFFD4B0) = 0 7752: cur = RLIM64_INFINITY max = RLIM64_INFINITY 7752: getrlimit(RLIMIT_CPU, 0xFFFF80FFBFFFD4B0) = 0 7752: cur = RLIM64_INFINITY max = RLIM64_INFINITY 7752: getrlimit(RLIMIT_DATA, 0xFFFF80FFBFFFD4B0) = 0 7752: cur = RLIM64_INFINITY max = RLIM64_INFINITY 7752: getrlimit(RLIMIT_FSIZE, 0xFFFF80FFBFFFD4B0) = 0 7752: cur = RLIM64_INFINITY max = RLIM64_INFINITY 7752: getrlimit(RLIMIT_NOFILE, 0xFFFF80FFBFFFD4B0) = 0 7752: cur = 1024 max = 65536 7752: sysconfig(_CONFIG_CHILD_MAX) = 29995 7752: pathconf("/", _PC_PIPE_BUF) = 5120 7752: pathconf("/", _PC_PIPE_BUF) = 5120 7752: sysconfig(_CONFIG_SIGQUEUE_MAX) = 128 7752: getrlimit(RLIMIT_STACK, 0xFFFF80FFBFFFD4B0) = 0 7752: cur = 8388608 max = RLIM64_INFINITY 7752: getrlimit(RLIMIT_VMEM, 0xFFFF80FFBFFFD4B0) = 0 7752: cur = RLIM64_INFINITY max = RLIM64_INFINITY 7752: write(1, " a d d r e s s s p a c".., 942) = 942
我們看到它
ulimit
使用(getrlimit()
andsetrlimit()
) 庫函式來獲取/設置資源限制。每/頁(
getrlimit()``setrlimit()
man
注意粗體部分):RLIMIT_STACK
程序堆棧的最大大小(以字節為單位)。系統不會自動將堆棧增長到超出此限制。
在一個程序中,
setrlimit()
將增加對堆棧大小的限制,但不會移動目前記憶體段以允許該增長。為了保證程序堆棧可以增長到限制,必須在執行要使用新堆棧大小的程序之前更改限制。在多執行緒程序中,
setrlimit()
如果呼叫執行緒不是主執行緒,則對呼叫執行緒的堆棧大小限制沒有影響。setrlimit()
對for的呼叫RLIMIT_STACK
僅影響主執行緒的堆棧,並且應該僅從主執行緒進行,如果有的話。信號被
SIGSEGV
發送到程序。如果程序正在持有或忽略 SIGSEGV,或者正在擷取SIGSEGV
但尚未安排使用備用堆棧(請參閱參考資料),則在發送之前將設置sigaltstack(2)
的處置。SIGSEGV``SIG_DFL
因此,由非主執行緒的程序創建的執行緒的堆棧大小不受程序
RLIMIT_STACK
資源限制的影響。而且您必須在程序啟動setrlimit()
之前(在父程序中)呼叫,以確保任何更大的堆棧大小限制實際上是有效的。創建的新執行緒
pthread_create()
使用stackaddr
屬性指定的堆棧,堆棧繼續屬性指定的字節數stacksize
。預設情況下,32 位程序的堆棧大小為 1 兆字節,64 位程序的堆棧大小為 2 兆字節(請參閱 參考資料pthread_attr_setstacksize(3C)
)。stackaddr
如果和stacksize
屬性 都使用預設值,pthread_create()
則為新執行緒創建一個堆棧,其中 32 位程序至少 1 兆字節,64 位程序至少 2 兆字節。(有關自定義堆棧大小,請參閱 NOTES )。…
筆記
…
使用者指定的堆棧大小必須大於 value
PTHREAD_STACK_MIN
。最小堆棧大小可能無法容納使用者執行緒函式的堆棧幀start_func
。如果指定了堆棧大小start_func
,除了最低要求外,它還必須滿足要求和它可能依次呼叫的函式。通常很難確定執行緒的執行時堆棧要求。
PTHREAD_STACK_MIN
指定執行一個NULL
start_func
. 堆棧儲存的總執行時要求取決於執行執行時連結所需的儲存量,以及printf()
執行緒呼叫的庫執行時(as)所需的儲存量。由於這些儲存參數在程序執行之前是未知的,因此最好使用預設堆棧。如果您知道您的執行時要求或決定使用大於預設值的堆棧,那麼指定您自己的堆棧是有意義的。