ulimit 堆栈大小 - 每个进程还是每个线程限制?

ulimit 堆栈大小 - 每个进程还是每个线程限制?

我们在 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()

创建的新线程pthread_create()使用属性指定的堆栈stackaddr,并且堆栈持续属性指定的字节数stacksize。默认情况下,32 位进程的堆栈大小为 1 MB,64 位进程的堆栈大小为 2 MB(pthread_attr_setstacksize(3C))。如果默认值同时用于stackaddrstacksize属性, pthread_create()则为新线程创建一个堆栈,其中 32 位进程至少为 1 MB,64 位进程至少为 2 MB。 (有关自定义堆栈大小,请参阅注释)。

...

笔记

...

用户指定的堆栈大小必须大于值 PTHREAD_STACK_MIN。最小堆栈大小可能无法容纳用户线程函数的堆栈帧start_func。如果指定了堆栈大小,start_func除了最低要求之外,它还必须满足要求以及它可能依次调用的函数。

确定线程的运行时堆栈要求通常非常困难。PTHREAD_STACK_MIN指定执行一个 .txt 文件需要多少堆栈存储空间NULL start_func。堆栈存储的总运行时要求取决于执行运行时链接所需的存储、printf()线程调用的库运行时(如 )所需的存储量。由于这些存储参数在程序运行之前是未知的,因此最好使用默认堆栈。如果您知道运行时要求或决定使用大于默认值的堆栈,那么指定您自己的堆栈是有意义的。

相关内容