有时候发现线上运行的程序没有修改 ulimit -n ,导致文件描述符不够用,但是又不能随便重启服务,或者碰到运行中的程序可能不定时会 segment fault ,但是由于没 ulimit -c ,无法得到 core 文件调试。但是 linux 的 ulimit 命令只对当前会话有效,已经启动的进程是没法用这个命令修改限制的。这时候如果能动态修改进程的 rlimit 就会方便很多。linux 内核版本 2.6.36 以后,多了一个 prlimit 调用,可以实现动态修改某个进程的 rlimit。
例如,这个 C 写的小程序就可以放开指定进程的 core dump 限制。
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>
int main(int argc, char** argv) {
pid_t pid;
struct rlimit new_limit;
int result;
if (argc < 2) {
return 1;
}
pid = atoi(argv[1]);
new_limit.rlim_cur = RLIM_INFINITY;
new_limit.rlim_max = RLIM_SAVED_MAX;
result = prlimit(pid, RLIMIT_CORE, &new_limit, NULL);
return result;
}
rlimit 相关常量在 /usr/include/bits/resource.h 中有定义。这里 prlimit 的具体定义也可以通过 man 2 prlimit 查看文档。
如果有幸可以用上 util-linux 2.21 以上版本(http://www.kernel.org/pub/linux/utils/util-linux/),那么直接就会有一个 prlimit 命令可用。那就非常方便了。
但是如果内核版本较低,没有这功能又怎么办呢?其实还可以通过万能的 gdb 钩进进程调用 setrlimit 来实现。于是,可以包装一个脚本出来:
#!/bin/bash
f="0"
while getopts ":c:d:e:f:i:l:m:n:p:q:r:s:t:u:v:x" opt; do
if [ "$f" == "1" ]; then
echo "too many arguments" >&2;
exit 1
fi
f="1"
v="$OPTARG"
case $opt in
c) r=4 ;; # RLIMIT_CORE core file size
d) r=2 ;; # RLIMIT_DATA data seg size
e) r=13 ;; # RLIMIT_NICE scheduling priority
f) r=1 ;; # RLIMIT_FSIZE file size
i) r=11 ;; # RLIMIT_SIGPENDING pending signals
l) r=8 ;; # RLIMIT_MEMLOCK max locked memory
m) r=5 ;; # RLIMIT_RSS max memory size
n) r=7 ;; # RLIMIT_NOFILE open files
q) r=12 ;; # RLIMIT_MSGQUEUE POSIX message queues
r) r=14 ;; # RLIMIT_RTPRIO real-time priority
s) r=3 ;; # RLIMIT_STACK stack size
t) r=0 ;; # RLIMIT_CPU cpu time
u) r=6 ;; # RLIMIT_NPROC max user processes
v) r=9 ;; # RLIMIT_AS virtual memory
x) r=10 ;; # RLIMIT_LOCKS file locks
?) echo "bad argument $opt" >&2; exit 1 ;;
esac
done
shift $(($OPTIND - 1))
if echo "$v" | grep -q -E "^\-?[0-9]+$"; then
true
else
echo "bad rlimti value $v" >&2
exit 2
fi
if [ `echo "$v==-1 || ($v>=0 && $v<1048576)"|bc` == '0' ];then
echo "bad rlimti value $v" >&2
exit 2
fi
pid=$1
bin=`readlink /proc/$pid/exe`
if [ -z "$bin" ]; then
echo "process $pid not found" >&2
exit 3
fi
cmd='
set $rlim=&{-1ll,-1ll}
print getrlimit('$r',$rlim)
set *$rlim[0]='$v'
print setrlimit('$r',$rlim)
quit
'
result=`echo "$cmd" | gdb $bin $pid 2>/dev/null | grep '(gdb) \$2 ='`
result="${result##*= }"
exit $result
如
sudo ./rlimit.sh -c -1 12345
就可以 12345 放开进程的 core dump
参数同 ulimit ,-1 表示 unlimited。不过用这种方法,只能调整 soft rlimit ,而且没法超过原有的 hard rlimit。
修改以后,可以通过 cat /proc/<pid>/limits 查看效果
补充(感谢zolker):
对于 redhat 系,如 centos 6.2 之后可以通过
echo -n 'Max processes=10240:10240' >/proc/<pid>/limits
来修改。这里的 limit 的名称就是
cat /proc/<pid>/limits
里看到的