Solaris

堆棧大小的 ulimit - 每個程序或每個執行緒限制?

  • November 12, 2019

所以我們在 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()and setrlimit()) 庫函式來獲取/設置資源限制。

/getrlimit()``setrlimit() man注意粗體部分):

RLIMIT_STACK

程序堆棧的最大大小(以字節為單位)。系統不會自動將堆棧增長到超出此限制。

在一個程序中,setrlimit()將增加對堆棧大小的限制,但不會移動目前記憶體段以允許該增長。為了保證程序堆棧可以增長到限制,必須在執行要使用新堆棧大小的程序之前更改限制

在多執行緒程序中,setrlimit()如果呼叫執行緒不是主執行緒,則對呼叫執行緒的堆棧大小限制沒有影響setrlimit()對for的呼叫RLIMIT_STACK僅影響主執行緒的堆棧,並且應該僅從主執行緒進行,如果有的話。

信號被SIGSEGV發送到程序。如果程序正在持有或忽略 SIGSEGV,或者正在擷取SIGSEGV但尚未安排使用備用堆棧(請參閱參考資料),則在發送之前將設置sigaltstack(2)的處置。SIGSEGV``SIG_DFL

因此,由非主執行緒的程序創建的執行緒的堆棧大小不受程序RLIMIT_STACK資源限制的影響。而且您必須在程序啟動setrlimit() 之前(在父程序中)呼叫,以確保任何更大的堆棧大小限制實際上是有效的。

根據手冊pthread_create()

創建的新執行緒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)所需的儲存量。由於這些儲存參數在程序執行之前是未知的,因此最好使用預設堆棧。如果您知道您的執行時要求或決定使用大於預設值的堆棧,那麼指定您自己的堆棧是有意義的。

引用自:https://unix.stackexchange.com/questions/551491