prlimit(2) 資源の制限を取得/設定する

Other Alias

getrlimit, setrlimit

書式

#include <sys/time.h>
#include <sys/resource.h>

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

int prlimit(pid_t pid, int resource, const struct rlimit *new_limit,
struct rlimit *old_limit);

glibc 向けの機能検査マクロの要件 (feature_test_macros(7) 参照):

prlimit(): _GNU_SOURCE && _FILE_OFFSET_BITS == 64

説明

getrlimit() と setrlimit() はそれぞれ資源 (resource) の制限 (limit) の設定と取得を行う。 各リソースには、それに対応するソフトリミッ トとハードリミットがあり、 rlimit 構造体で定義される:

struct rlimit {
    rlim_t rlim_cur;  /* ソフトリミット */
    rlim_t rlim_max;  /* ハードリミット
                         (rlim_cur より小さくない) */
};
ソフトリミットは、カーネルが対応するリソースに対して課す制限値である。 ハードリミットはソフトリミットの上限として働く。 特権を持たないプロセスは、ソフトリミットの値を 0 からハードリミットの範囲に設定することと、 ハードリミットを下げることのみができる (一度下げたハードリミットは上げられない)。 特権プロセス (Linux では CAP_SYS_RESOURCE ケーパビリティ (capability) を持つプロセス) は ソフトリミットとハードリミットを自由に変更できる。

RLIM_INFINITY はリソースに制限がないことを表す (この値は getrlimit() が返す構造体と setrlimit() に渡す構造体の両方で使用される)。

resource 引き数は次のいずれか 1 つである。

RLIMIT_AS
プロセスの仮想メモリ (アドレス空間) の最大サイズ (バイト単位)。 この制限は brk(2), mmap(2), mremap(2) の呼び出しに影響し、この制限を超えた場合は エラー ENOMEM で失敗する。 また自動的なスタック拡張にも失敗する (さらに sigaltstack(2) を使った代替スタックを利用可能にしていなかった場合には、 SIGSEGV を生成してそのプロセスを kill する)。 この値は long 型なので、32 ビットの long 型を持つマシンでは、 この制限は最大で 2 GiB になるか、この資源が無制限になる。
RLIMIT_CORE
core ファイルの最大サイズ。 0 の場合、core ファイルは生成されない。 0 以外の場合、このサイズより大きいダンプは切り詰められる。
RLIMIT_CPU
CPU 時間の上限 (秒数)。プロセスがソフトリミットに達した場合に、 SIGXCPU シグナルを送る。このシグナルに対するデフォルトの動作は、 プロセスの終了である。ただし、シグナルをキャッチして、ハンドラがメイン プログラムに制御を返すこともできる。プロセスが CPU 時間を使い続けた 場合は、ハードリミットに達するまで 1 秒毎にプロセスに SIGXCPU を送り、 ハードリミットに達すると SIGKILL を送る。 (ソフトリミットを超過したときの動作は、 Linux における動作である。ソフ トリミットを超えて CPU 時間を使い続けるプロセスの扱い方についての実装は 変化してきている。 このシグナルをキャッチする必要のある 移植性を考えた アプリケーションでは、 最初に SIGXCPU を受け取った時点で正しく終了 すべきである。)
RLIMIT_DATA
プロセスのデータセグメント (初期化されたデータ・初期化されていないデータ・ヒープ) の最大値。 このリミットは brk(2) と sbrk(2) の呼び出しに影響する。 これらの関数は、このリソースのソフトリミットに達すると、 エラー ENOMEM で失敗する。
RLIMIT_FSIZE
プロセスが作成できるファイルサイズの最大値。 このサイズを超えてファイルを拡張すると、 SIGXFSZ シグナルを送る。 デフォルトでは、このシグナルはプロセスを終了する。 プロセスをキャッチすることもできるが、 関連するシステムコール (write(2), truncate(2) など) はエラー EFBIG で失敗する。
RLIMIT_LOCKS (初期の Linux 2.4 のみ)
このプロセスが実行できる flock(2) ロック数と fcntl(2) リース数の合計値を制限する。
RLIMIT_MEMLOCK
RAM 内にロックできるメモリの最大バイト数。 実際には、この制限はシステムページサイズの最も近い倍数に 切り捨てて丸められる。 この制限は mlock(2), mlockall(2), mmap(2) の MAP_LOCKED 操作に影響する。 Linux 2.6.9 以降では shmctl(2) SHM_LOCK 操作にも影響する。 この操作は呼び出し元プロセスの実 (real) ユーザー ID にロックされる 共有メモリセグメント (shmget(2) を参照) の合計バイト数の最大値を設定する。 shmctl(2) SHM_LOCK によるロックは、 mlock(2), mlockall(2), mmap(2) の MAP_LOCKED によって確立されるプロセス毎のメモリロックとは分けて数える。 1 つのプロセスはこの制限までのバイトをロックできる。 この制限には 2 つの種類がある。 2.6.9 より前の Linux カーネル では、 この制限は特権プロセスによってロックされるメモリの合計を制御していた。 Linux 2.6.9 以降では、特権プロセスがロックするメモリの合計に制限はなく、 代わりにこの制限は非特権プロセスがロックするメモリの合計に 適用されるようになった。
RLIMIT_MSGQUEUE (Linux 2.6.8 以降)
呼び出し元プロセスの実ユーザー ID に対して、 POSIX メッセージキューのために確保できるバイト数の制限を指定する。 この制限は mq_open(3) に対して適用される。 ユーザが作成した各々のメッセージキューのバイト数は 以下の式により計算され、(そのキューが削除されるまでの間) この制限の計算対象に含められる。
    bytes = attr.mq_maxmsg * sizeof(struct msg_msg *) +
            attr.mq_maxmsg * attr.mq_msgsize
ここで attrmq_attr 構造体であり、 mq_open(3) の第 4 引き数として指定される。

sizeof(struct msg_msg *) (Linux/i386 では 4 バイト) を含む最初の加数は、 ユーザーが長さ 0 のメッセージを無制限に作れないこと保証している (このようなメッセージであっても、 記録のためのオーバーヘッドでシステムメモリを消費する)。

RLIMIT_NICE (Linux 2.6.12 以降, 下記の「バグ」の節も参照)
setpriority(2) や nice(2) を使って引き上げられるプロセスの nice 値の上限を指定する。 nice 値の実際の上限は 20 - rlim_cur で計算される (このような変な状況は、リソース制限値として負の数を指定できないため 発生する。通常、負の値は特別な意味を持っているからである。 例えば、通常は RLIM_INFINITY の値は -1 である)。
RLIMIT_NOFILE
このプロセスがオープンできるファイルディスクリプタ数の最大値より 1 大きい値を指定する。 (open(2), pipe(2), dup(2) などにより) この上限を超えようとした場合、エラー EMFILE が発生する (歴史的に、BSD ではこの上限は RLIMIT_OFILE という名前となっている)。
RLIMIT_NPROC
呼び出したプロセスの実ユーザー ID で作成できる最大プロセス数 (より正確には Linux ではスレッド数)。 この上限に達すると、 fork(2) はエラー EAGAIN で失敗する。
RLIMIT_RSS
プロセスの resident set (RAM 上に存在する仮想ページの数) の 上限を (ページ数で) 指定する。 この制限は 2.4.30 より前でしか影響がなく、 madvise(2) に MADV_WILLNEED を指定した関数コールにしか影響しない。
RLIMIT_RTPRIO (Linux 2.6.12 以降, バグの節も参照)
sched_setscheduler(2) や sched_setparam(2) を使って設定できる、そのプロセスのリアルタイム優先度の上限を指定する。
RLIMIT_RTTIME (Linux 2.6.25 以降)
リアルタイムスケジューリング方針でスケジューリングされるプロセスが ブロッキング型のシステムコールを呼び出さずに消費することのできる CPU 時間の合計についての上限を (マイクロ秒単位で) 指定する。 この上限の目的のため、プロセスがブロッキング型のシステムコールを 呼び出す度に、消費された CPU 時間のカウントは 0 にリセットされる。 プロセスが CPU を使い続けようとしたが他のプロセスに置き換えられた (preempted) 場合や、そのプロセスのタイムスライスが満了した場合、 そのプロセスが sched_yield(2) を呼び出した場合は、CPU 時間のカウントはリセットされない。

ソフトリミットに達すると、そのプロセスに SIGXCPU シグナルが送られる。そのプロセスがこのシグナルを捕捉するか 無視して、CPU 時間を消費し続けた場合には、 ハードリミットに達するまで 1 秒に 1 回 SIGXCPU が生成され続けることになる。 ハードリミットに達した時点で、そのプロセスには SIGKILL シグナルが送られる。

この上限を意図的に使用するのは、暴走したリアルタイムプロセスを 停止して、システムが動かなくなるのを避ける場合である。

RLIMIT_SIGPENDING (Linux 2.6.8 以降)
呼び出し元プロセスの実ユーザー ID に対して キューに入れられるシグナルの 数の制限を指定する。この制限をチェックするため、標準シグナルとリアルタ イムシグナルの両方がカウントされる。しかし、この制限は sigqueue(3) に対してのみ適用され、 kill(2) 使うことで、そのプロセスに対してま だキューに入れられていない シグナルのインスタンスをキューに入れることが できる。
RLIMIT_STACK
プロセススタックの最大サイズをバイト単位で指定する。 この上限に達すると、 SIGSEGV シグナルが生成される。 このシグナルを扱うためには、 プロセスは代りのシグナルスタック (sigaltstack(2)) を使用しなければならない。

Linux 2.6.23 以降では、この制限はプロセスのコマンドライン引き数と環境変数 に使用される空間の合計サイズの上限の決定にも使用される。詳細については execve(2) を参照。

prlimit()

Linux 固有の prlimit() システムコールは、 setrlimit() と getrlimit の機能を合わせて拡張したものである。 このシステムコールを使って、任意のプロセスのリソース上限の設定と取得を行うことができる。

resource 引き数は setrlimit() や getrlimit() と同じ意味である。

new_limit 引き数が NULL 以外の場合、 new_limit が指す rlimit 構造体を使って resource のソフトリミットとハードリミットの新しい値が設定される。 old_limit 引き数が NULL 以外の場合、 prlimit() の呼び出しが成功すると、 resource の直前のソフトリミットとハードリミットが old_limit が指す rlimit 構造体に格納される。

pid 引き数は呼び出しの操作対象となるプロセス ID を指定する。 pid が 0 の場合、呼び出しは呼び出し元プロセスに対して適用される。 自分以外のプロセスのリソースの設定と取得を行うためには、 呼び出し元プロセスが CAP_SYS_RESOURCE ケーパビリティを持っているか、 対象となるプロセスの実ユーザ ID、 実効ユーザ ID、 保存 set-user-ID が呼び出し元プロセスの実ユーザ ID と一致し、 かつ、 対象となるプロセスの実グループ ID、 実効グループ ID、 保存 set-group-ID が呼び出し元プロセスの実グループ ID と一致していなければならない。

返り値

成功した場合、これらのシステムコールは 0 を返す。 エラーの場合は -1 が返され、 errno が適切に設定される。

エラー

EFAULT
場所を指すポインタ引き数がアクセス可能なアドレス空間外を指している。
EINVAL
resource で指定された値が有効でない。 または、 setrlimit() や prlimit() で、 rlim->rlim_currlim->rlim_max よりも大きかった。
EPERM
特権のないプロセスがハードリミットを上げようとした。 これを行うためには CAP_SYS_RESOURCE ケーパビリティが必要である。 または、特権のないプロセスが RLIMIT_NOFILE ハードリミットを 現在のカーネルの最大値 (NR_OPEN) 以上に増やそうとした。 または、呼び出したプロセスが pid で指定されたプロセスのリミットを 設定する許可を持っていなかった。
ESRCH
pid で指定された ID のプロセスが見つからなかった。

バージョン

prlimit() システムコールは Linux 2.6.36 以降で利用できる。 ライブラリのサポートは glibc 2.13 以降で利用できる。

準拠

getrlimit(), setrlimit(): SVr4, 4.3BSD, POSIX.1-2001.
prlimit(): Linux 固有。

RLIMIT_MEMLOCKRLIMIT_NPROC は BSD から派生し、 POSIX.1-2001 には指定されていない。 これらは BSD 系と Linux に存在するが、他の実装は少ない。 RLIMIT_RSS は BSD から派生し、POSIX.1-2001 には指定されていない。 それにも関わらず多くの実装で存在する。 RLIMIT_MSGQUEUE, RLIMIT_NICE, RLIMIT_RTPRIO, RLIMIT_RTTIME, RLIMIT_SIGPENDING は Linux 固有のものである。

注意

fork(2) で作成された作成された子プロセスは、 親プロセスのリソース制限を継承する。 execve(2) の前後でリソース制限は保存される。

リソースのソフトリミットをそのプロセスが現在のリソース使用量より小さい値に設定することはできる (但し、そのプロセスはそれ以降そのリソースの使用量を増やすことができなくなる)。

シェルのリソース制限は、シェルの組み込みコマンドである ulimit (csh(1) では limit ) を使って設定することができる。 このシェルのリソース制限は、コマンドを実行してシェルが生成するプロセス に引き継がれる。

Linux 2.6.24 以降では、 プロセスのリソース上限は /proc/[pid]/limits で知ることができる。 proc(5) 参照。

古いシステムでは、 setrlimit() と同様の目的を持つ関数 vlimit() が提供されていた。 後方互換性のため、glibc でも vlimit() を提供している。 全ての新しいアプリケーションでは、 setrlimit() を使用すべきである。

バグ

以前の Linux カーネルでは、プロセスがソフトまたはハード RLIMIT_CPU リミットに達した場合に送られる SIGXCPUSIGKILL シグナルが、本来送られるべき時点の 1 (CPU) 秒後に送られてしまう。 これはカーネル 2.6.8 で修正された。

2.6.17 より前の 2.6.x カーネルでは、 RLIMIT_CPU リミットが 0 の場合、 (RLIM_INFINITY と同じように) 「制限なし」と間違って解釈されていた。 Linux 2.6.17 以降では、リミットを 0 に設定した場合にも 効果を持つようになっているが、実際にはリミットの値は 1 秒となる。

カーネル 2.6.12 には、 RLIMIT_RTPRIO が動作しないというバグがある。この問題はカーネル 2.6.13 で修正されている。

カーネル 2.6.12 では、 getpriority(2) と RLIMIT_NICE が返す優先度の範囲が一つずれていた。このため、nice 値の実際の上限が 19 - rlim_cur になってしまうという影響があった。これはカーネル 2.6.13 で修正された。

Linux 2.6.12 以降では、 プロセスがその RLIMIT_CPU ソフトリミットに達し、 SIGXCPU に対してシグナルハンドラが設定されている場合、 シグナルハンドラを起動するだけでなく、 カーネルは 1 秒間ソフトリミットを増やす。 そのプロセスが CPU 時間を消費し続けている限り、 ハードリミットに達するまで、この動作が繰り返される。 ハードリミットに達すると、その時点でプロセスは kill される。 他の実装では、上記のような RLIMIT_CPU ソフトリミットの変更は行われず、 おそらく Linux の動作は標準に準拠していない。 移植性が必要なアプリケーションではこの Linux 固有の動作を前提にするのは避けるべきである。 Linux 固有の上限 RLIMIT_RTTIME でも、 ソフトリミットに達した場合に同じ動作となる。

2.4.22 より前のカーネルでは、 rlim->rlim_currlim->rlim_max より大きかった場合、 setrlimit() での EINVAL エラーを検出できない。

以下のプログラムに prlimit() の使用例を示す。

#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)
int
main(int argc, char *argv[])
{
    struct rlimit old, new;
    struct rlimit *newp;
    pid_t pid;
    if (!(argc == 2 || argc == 4)) {
        fprintf(stderr, "Usage: %s <pid> [<new-soft-limit> "
                "<new-hard-limit>]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    pid = atoi(argv[1]);        /* PID of target process */
    newp = NULL;
    if (argc == 4) {
        new.rlim_cur = atoi(argv[2]);
        new.rlim_max = atoi(argv[3]);
        newp = &new;
    }
    /* Set CPU time limit of target process; retrieve and display
       previous limit */
    if (prlimit(pid, RLIMIT_CPU, newp, &old) == -1)
        errExit("prlimit-1");
    printf("Previous limits: soft=%lld; hard=%lld\n",
            (long long) old.rlim_cur, (long long) old.rlim_max);
    /* Retrieve and display new CPU time limit */
    if (prlimit(pid, RLIMIT_CPU, NULL, &old) == -1)
        errExit("prlimit-2");
    printf("New limits: soft=%lld; hard=%lld\n",
            (long long) old.rlim_cur, (long long) old.rlim_max);
    exit(EXIT_FAILURE);
}

この文書について

この man ページは Linux man-pages プロジェクトのリリース 3.65 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。