| GETRLIMIT(2) | Linux Programmer's Manual | GETRLIMIT(2) |
getrlimit, setrlimit, prlimit - 資源の制限を取得/設定する
#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);
prlimit(): _GNU_SOURCE
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 つである。
bytes = attr.mq_maxmsg * sizeof(struct msg_msg) +
min(attr.mq_maxmsg, MQ_PRIO_MAX) *
sizeof(struct posix_msg_tree_node)+
/* オーバーヘッド分 */
attr.mq_maxmsg * attr.mq_msgsize;
/* メッセージデータ分 */
bytes = attr.mq_maxmsg * sizeof(struct msg_msg *) +
/* オーバーヘッド分 */
attr.mq_maxmsg * attr.mq_msgsize;
/* メッセージデータ分 */
Linux 固有の prlimit() システムコールは、 setrlimit() と getrlimit の機能を合わせて拡張したものである。 このシステムコールを使って、任意のプロセスのリソース上限の設定と取得を行うことができる。
resource 引数は setrlimit() や getrlimit() と同じ意味である。
new_limit 引数が NULL 以外の場合、 new_limit が指す rlimit 構造体を使って resource のソフトリミットとハードリミットの新しい値が設定される。 old_limit 引数が NULL 以外の場合、 prlimit() の呼び出しが成功すると、 resource の直前のソフトリミットとハードリミットが old_limit が指す rlimit 構造体に格納される。
The pid argument specifies the ID of the process on which the call is to operate. If pid is 0, then the call applies to the calling process. To set or get the resources of a process other than itself, the caller must have the CAP_SYS_RESOURCE capability in the user namespace of the process whose resource limits are being changed, or the real, effective, and saved set user IDs of the target process must match the real user ID of the caller and the real, effective, and saved set group IDs of the target process must match the real group ID of the caller.
成功した場合、これらのシステムコールは 0 を返す。 エラーの場合は -1 が返され、 errno が適切に設定される。
prlimit() システムコールは Linux 2.6.36 以降で利用できる。 ライブラリのサポートは glibc 2.13 以降で利用できる。
この節で使用されている用語の説明については、 attributes(7) を参照。
| インターフェース | 属性 | 値 |
| getrlimit(), setrlimit(), prlimit() | Thread safety | MT-Safe |
getrlimit(), setrlimit(): POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD.
prlimit(): Linux 固有。
RLIMIT_MEMLOCK と RLIMIT_NPROC は BSD から派生し、 POSIX.1 には指定されていない。これらは BSD 系と Linux に存在するが、他の実装は少ない。 RLIMIT_RSS は BSD から派生し、POSIX.1 には指定されていない。それにも関わらず多くの実装で存在する。 RLIMIT_MSGQUEUE, RLIMIT_NICE, RLIMIT_RTPRIO, RLIMIT_RTTIME, RLIMIT_SIGPENDING は Linux 固有のものである。
fork(2) で作成された作成された子プロセスは、 親プロセスのリソース制限を継承する。 execve(2) の前後でリソース制限は保存される。
Resource limits are per-process attributes that are shared by all of the threads in a process.
リソースのソフトリミットをそのプロセスが現在のリソース使用量より小さい値に設定することはできる (但し、そのプロセスはそれ以降そのリソースの使用量を増やすことができなくなる)。
シェルのリソース制限は、シェルの組み込みコマンドである ulimit (csh(1) では limit ) を使って設定することができる。 このシェルのリソース制限は、コマンドを実行してシェルが生成するプロセス に引き継がれる。
Linux 2.6.24 以降では、 プロセスのリソース上限は /proc/[pid]/limits で知ることができる。 proc(5) 参照。
古いシステムでは、 setrlimit() と同様の目的を持つ関数 vlimit() が提供されていた。 後方互換性のため、glibc でも vlimit() を提供している。 全ての新しいアプリケーションでは、 setrlimit() を使用すべきである。
バージョン 2.13 以降では、 glibc の getrlimit() と setrlimit() のラッパー関数はもはや対応するシステムコールを呼び出さず、 代わりに「バグ」の節で説明されている理由から prlimit() を利用している。
The name of the glibc wrapper function is prlimit(); the underlying system call is prlimit64().
以前の Linux カーネルでは、プロセスがソフトまたはハード RLIMIT_CPU リミットに達した場合に送られる SIGXCPU と SIGKILL シグナルが、本来送られるべき時点の 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_cur が rlim->rlim_max より大きかった場合、 setrlimit() での EINVAL エラーを検出できない。
Linux doesn't return an error when an attempt to set RLIMIT_CPU has failed, for compatibility reasons.
glibc の getrlimit() と setrlimit() ラッパー関数は、32 ビットプラットフォームであっても 64 ビットの rlim_t データ型を使用する。 しかし、 getrlimit() と setrlimit() システムコールで使用される rlim_t データ型は (32 ビットの) unsigned long である。 さらに、 Linux では、 カーネルは 32 ビットプラットフォームではリソース上限を unsigned long として表現している。 しかしながら、 32 ビットデータ型は十分な大きさではない。 ここで最も関係がある上限値は RLIMIT_FSIZE である。 この上限はファイルサイズの最大値であり、実用性の面からは、 この上限をファイルオフセットを表現するのに使用されている型、 つまり 64 ビットの off_t (_FILE_OFFSET_BITS=64 でコンパイルしたプログラムの場合)、 と同じ幅を持つ型、を使って表現すべきである。
カーネルのこの制限に対する対策として、 プログラムがリソース上限を 32 ビットの unsigned long で表現できる値よりも大きな値に設定しようとした際には、 glibc の setrlimit() ラッパー関数はこの上限値を黙って RLIM_INFINITY に変換していた。 言い換えると、指定されたリソース上限値は黙って無視されていた。
バージョン 2.13 以降の glibc では、 getrlimit() と setrlimit() システムコールの制限に対する回避手段として、 setrlimit() と getrlimit() を prlimit() を呼び出すラッパー関数として実装している。
以下のプログラムに prlimit() の使用例を示す。
#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
#include <stdint.h>
#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=%jd; hard=%jd\n",
(intmax_t) old.rlim_cur, (intmax_t) 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=%jd; hard=%jd\n",
(intmax_t) old.rlim_cur, (intmax_t) old.rlim_max);
exit(EXIT_SUCCESS);
}
prlimit(1), dup(2), fcntl(2), fork(2), getrusage(2), mlock(2), mmap(2), open(2), quotactl(2), sbrk(2), shmctl(2), malloc(3), sigqueue(3), ulimit(3), core(5), capabilities(7), cgroups(7), credentials(7), signal(7)
この man ページは Linux man-pages プロジェクトのリリース 5.10 の一部である。プロジェクトの説明とバグ報告に関する情報は https://www.kernel.org/doc/man-pages/ に書かれている。
| 2020-11-01 | Linux |