我们在 Solaris 上有一个程序,堆栈空间不足。
在调查这个问题时,我简要了解了堆栈的 ulimit 是什么:
user@solaris-box:~$ ulimit -a
...
stack size (kbytes, -s) 8192
因此堆栈大小限制为 8 MB。但这是整个过程的极限吗?
如果我的进程有 10 个线程怎么办,每个线程只允许 819k 吗? (或者它们的某种组合,最多 8MiB?)
我找不到任何关于此的文档。
答案1
概括
对于主线程,您必须调用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()
(和)库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()
使用属性指定的堆栈stackaddr
,并且堆栈持续属性指定的字节数stacksize
。默认情况下,32 位进程的堆栈大小为 1 MB,64 位进程的堆栈大小为 2 MB(看pthread_attr_setstacksize(3C)
)。如果默认值同时用于stackaddr
和stacksize
属性,pthread_create()
则为新线程创建一个堆栈,其中 32 位进程至少为 1 MB,64 位进程至少为 2 MB。 (有关自定义堆栈大小,请参阅注释)。...
笔记
...
用户指定的堆栈大小必须大于值
PTHREAD_STACK_MIN
。最小堆栈大小可能无法容纳用户线程函数的堆栈帧start_func
。如果指定了堆栈大小,start_func
除了最低要求之外,它还必须满足要求以及它可能依次调用的函数。确定线程的运行时堆栈要求通常非常困难。
PTHREAD_STACK_MIN
指定执行一个 .txt 文件需要多少堆栈存储空间NULL
start_func
。堆栈存储的总运行时要求取决于执行运行时链接所需的存储、printf()
线程调用的库运行时(如 )所需的存储量。由于这些存储参数在程序运行之前是未知的,因此最好使用默认堆栈。如果您知道运行时要求或决定使用大于默认值的堆栈,那么指定您自己的堆栈是有意义的。