setrlimit 101

在利用seccomp和ptrace对程序在系统调用行为上做出限制之后,剩下的就需要在资源上(如运行时间、使用内存)做出限制,这个需求可以使用setrlimit来实现。因此学习一下它的使用。

无论是setrlimit或者是getrlimit都是通过以下结构体进行资源定义:

struct rlimit {
   rlim_t rlim_cur;  /* Soft limit */
   rlim_t rlim_max;  /* Hard limit (ceiling for rlim_cur) */
};

它们的的函数原型如下:

int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);

软限制是内核直接应用执行的资源量限制,硬限制则是作为软限制值的一个上界而存在。对于拥有CAP_SYS_RESOURCE权限的进程来说,可以任意调整软限制或者硬限制值,而对于非特权进程,只能在硬限制范围内调整软限制值,或者不可逆的降低硬限制值。

另外对于无限制,有一个特殊的值RLIM_INFINITY用来指示无限制,这个值在64位系统下应该为$2^{64}-1=18446744073709551615$

以下整理了一个常用的资源限制表:

资源名 参数 单位 备注
地址空间 RLIMIT_AS byte 即虚拟内存,向下取整到系统页大小,会影响brk,mmap,mremap等系统调用,一旦超出限制,程序会以ENOMEM错误退出。另外,如果无法通过sigaltstack自动扩容栈空间,将会生成SIGSEGV信号并终止程序。
转储文件大小 RLIMIT_CORE byte 程序能够生成的转储文件大小。为0时不生成转储文件,大于0时将会截断多余的部分。
CPU时间 RLIMIT_CPU s 限制进程能够消耗的CPU时间,如果达到了软限制,将会发送SIGXCPU信号,虽然这个信号默认行为是终止进程,但是是可以被重编程的。如果继续消耗CPU时间,达到硬限制时将会发送SIGKILL信号并强制终止程序。
数据段大小 RLIMIT_DATA byte 限制程序的数据段大小,包括初始化数据,未初始化数据以及堆大小。取值将会向下取整到系统页大小,影响brksbrkmmap等系统调用。一旦达到软限制将会抛出ENOMEM错误。
生成文件大小 RLIMIT_FSIZE byte 进程能够创建的最大文件大小,超出限制时将会发送一个可重编程的SIGXFSZ信号,如果这个信号没有终止程序,相应的系统调用(如writetruncate)将会以EFBIG错误退出。
栈空间大小 RLIMIT_STACK byte 程序所能使用的最大栈空间大小,一旦达到限制,将会生成SIGSEGV信号。
打开文件描述符数量 RLIMIT_NOFILE - 限制程序最大能打开的文件描述符数量,影响openpipedup等系统调用,如果超出限制,将会产生EMFILE错误。
进程数量 RLIMIT_NPROC - 限制程序能够产生的最大进程数(在linux下,这个更精确的定义是线程数),一旦超出限制,fork系统调用将会以EAGAIN错误失败退出。注意,对于拥有CAP_SYS_ADMIN或者CAP_SYS_RESOURCE能力的进程来说这个限制无效。

在上表中可以发现几个问题,首先涉及到内存的资源有RLIMIT_ASRLIMIT_DATARLIMIT_STACK,这三个内存的关系在这里有比较直观的介绍:memory-layout-of-c-program, 所以一般情况下直接对地址空间大小进行限制即可。

第二个是与时间相关的限制,可以看到只有一个CPU时间,并且其单位为秒,如果直接在Ubuntu终端使用time命令对程序执行进行测量,会发现三个时间:user cpu time、system cpu time和wall time。wall time如字面义,墙上时钟时间,即程序执行过程中实际流逝的时间。前两者则分别是用户程序和系统消耗的cpu时间。cpu time和wall time 两者是区分程序是否是并行程序的重要标准。对于传统的竞赛题目及代码来说,由于都是单核模型,wall time是一定不小于cpu time的。

回到上述限制本身,只能通过setrlimit限制其CPU时间,但是却能在程序结束时获得其较为精确的资源使用情况,假设我们程序要求是1.5s,直接设置CPU时间为2s甚至是3s,结束后再检查真实CPU运行时间即可。

当然,也需要对wall time做出限制,比如一个while(true)sleep(100);这样的程序基本上不会消耗什么CPU时间,却会一直占据资源,具体做法可以使用其他进程对执行程序进行监控并获取真实时间。

注意在linux系统下,有一些信号是无法被重编程的,如SIGKILLSIGSTOP等等,在子进程退出时,父进程可以通过wait系统调用拿到子进程的退出状态码以及退出时收到的信号,据此判断退出状态。


文章作者: crazyX
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 crazyX !
评论
 本篇
setrlimit 101 setrlimit 101
在利用seccomp和ptrace对程序在系统调用行为上做出限制之后,剩下的就需要在资源上(如运行时间、使用内存)做出限制,这个需求可以使用setrlimit来实现。因此学习一下它的使用。 无论是setrlimit或者是getrlimi
2018-11-05
下一篇 
ptrace 101 ptrace 101
有一些其他的需求暂时没找到办法用seccomp实现,于是继上次探索seccomp之后,又开始了研究ptrace的使用。 介绍ptrace(2) - 系统调用 - process trace 使用ptrace可以暂停被监控程序,获取设置寄
2018-11-03
  目录