(bash のコマンドだと ulimit だけどシステムコールだと get/setrlimit だし、ulimit? rlimit? どっち?)
はじめに
ここ↓にある通りですが、カーネル 2.6.36 以降なら prlimit(1) で簡単にできます(prlimit が使える環境が手元になかったので試していませんが)。
それ以前のカーネル(あるいは Linux 以外)なら gdb でアタッチして対象プロセスで setrlimit を無理やり呼び出します。
手元に prlimit が使える環境がなかったので gdb を使います。
バッチを作成
gdb でアタッチした瞬間にプロセスが停止してしまうため gdb のコマンドは手打ちせず、停止時間を短くするために gdb のコマンドをバッチにしておく方が良いでしょう。
set pagination off
set width 0
set height 0
set $rlim = &{0ll, 0ll}
print getrlimit(4, $rlim)
print *$rlim
set $rlim = &{-1ll, -1ll}
print setrlimit(4, $rlim)
detach
quit
先頭 3 行は gcore からコピりました、意味は知りません。
6 行目や 10 行目の 4
は RLIMIT_CORE
です。具体的な値は /usr/include/bits/resource.h
で定義されています。
9 行目の -1ll
は long long の -1 で RLIM_INFINITY
です。1つ目がソフトで2つ目がハードです。どちらも無制限に設定します。
gdb でプロセスにアタッチ
バッチを作成した後、対象プロセスの PID を調べて gdb を次のように実行します。
$ gdb -x rlimit.gdb -batch -p 12345
[Thread debugging using libthread_db enabled]
:
$1 = 0
$2 = {0, -1}
$3 = 0
-
$1
は getrlimit の戻り値です(0 なら成功) -
$2
は getrlimit で得られた現在のリミットです(ソフト、ハード) -
$3
は setrlimit の戻り値です(0 なら成功)
コアダンプさせてみる
この後、このプロセスをセグらせるとコアダンプします。
$ kill -SIGSEGV 12345
Segmentation fault (core dumped)
注意点
prlimit は試せる環境が無かったので判りませんが、gdb で setrlimit を呼ぶ方法はあくまでも対象プロセスのプロセス空間内で setrlimit を呼んでいるだけなので、ハードリミットを上げるためには対象プロセスが root で実行されているか CAP_SYS_RESOURCE
ケーパビリティを持っている必要があります。
gdb を root で実行しようがなにしようが対象プロセスに特権が無いならハードリミットは上げられません。
ソフトリミットをハードリミットの範囲内で設定したり、ハードリミットを下げる分には問題ありません。
追記
id:hiboma から次のブコメをもらいました。
getrlimit, setrlimit 実行後にコケてしまうと errno を書きかえるので副作用がでます。errno を一旦退避しておいて元に戻しておくとより安全
実際に確認してみます。
まずは次のように errno が非 0 になるまでループさせます。
もちろんループの中で errno が非 0 になる可能性が無いので無限ループするはずです。
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <errno.h>
pid_t gettid()
{
return syscall(SYS_gettid);
}
int main()
{
pid_t pid = getpid();
pid_t tid = gettid();
printf("%ld %ld\n", pid, tid);
while(!errno);
printf("errno: %d\n", errno);
return 0;
}
ハードリミットを 0 に設定して実行します。
$ gcc a.c
$ ulimit -H -c 0
$ ./a.out
12345 12345
gdb でアタッチします。
$ gdb -p 12345
setrlimit で -1 を設定してみます。が、非特権ユーザーなので失敗します。
set $rlim = &{-1ll, -1ll}
print setrlimit(4, $rlim)
デタッチすると・・・
detach
実行していたプロセスが終了します。
$ ./a.out
12345 12345
errno: 1
確かに setrlimit の失敗によって errno が書き換えられてしまってますね・・・
なので次のように errno を退避しておくほうが良いようです。
set pagination off
set width 0
set height 0
set $errno = errno
set $rlim = &{0ll, 0ll}
print getrlimit(4, $rlim)
print *$rlim
set $rlim = &{-1ll, -1ll}
print setrlimit(4, $rlim)
set errno = $errno
detach
quit