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);
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 より小さくない) */ };
値 RLIM_INFINITY はリソースに制限がないことを表す (この値は getrlimit() が返す構造体と setrlimit() に渡す構造体の両方で使用される)。
resource 引き数は次のいずれか 1 つである。
ここで attr は mq_attr 構造体であり、 mq_open(3) の第 4 引き数として指定される。 また、構造体 msg_msg と posix_msg_tree_node はカーネル内部の構造体である。
Linux 3.5 以降:
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;
/* メッセージデータ分 */
Linux 3.4 以前:
bytes = attr.mq_maxmsg * sizeof(struct msg_msg *) +
/* オーバーヘッド分 */
attr.mq_maxmsg * attr.mq_msgsize;
/* メッセージデータ分 */
上記の式での「オーバーヘッド」加算分は、実装において必要となるオーバーヘッドを考慮したものである。 また、これにより、ユーザーが長さ 0 のメッセージを無制限に作れないことが保証される (このようなメッセージであっても、 記録のためのオーバーヘッドでシステムメモリーを消費する)。
ソフトリミットに達すると、そのプロセスに SIGXCPU シグナルが送られる。そのプロセスがこのシグナルを捕捉するか 無視して、CPU 時間を消費し続けた場合には、 ハードリミットに達するまで 1 秒に 1 回 SIGXCPU が生成され続けることになる。 ハードリミットに達した時点で、そのプロセスには SIGKILL シグナルが送られる。
この上限を意図的に使用するのは、暴走したリアルタイムプロセスを 停止して、システムが動かなくなるのを避ける場合である。
Linux 2.6.23 以降では、この制限はプロセスのコマンドライン引き数と環境変数 に使用される空間の合計サイズの上限の決定にも使用される。詳細については execve(2) を参照。
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 が適切に設定される。
prlimit() システムコールは Linux 2.6.36 以降で利用できる。 ライブラリのサポートは glibc 2.13 以降で利用できる。
getrlimit(), setrlimit(): SVr4, 4.3BSD,
POSIX.1-2001.
prlimit(): Linux 固有。
RLIMIT_MEMLOCK と RLIMIT_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() を使用すべきである。
バージョン 2.13 以降では、 glibc の getrlimit() と setrlimit() のラッパー関数はもはや対応するシステムコールを呼び出さず、 代わりに「バグ」の節で説明されている理由から prlimit() を利用している。
以前の 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 エラーを検出できない。
glibc の getrlimit() と setrlimit() ラッパー関数は、32 ビットプラットフォームであっても 64 ビットの rlim_t データ型を使用する。 しかし、 getrlimit() と setrlimit() システムコールで使用される rlim_t データ型は (32 ビットの) unsigned long である。 さらに、 2.6.36 より前の Linux では、 カーネルは 32 ビットプラットフォームではリソース上限を unsigned long として表現している。 しかしながら、 32 ビットデータ型は十分な大きさではない。 ここで最も関係がある上限値は RLIMIT_FSIZE である。 この上限はファイルサイズの最大値であり、実用性の面からは、 この上限をファイルオフセットを表現するのに使用されている型、 つまり 64 ビットの off_t (_FILE_OFFSET_BITS=64 でコンパイルしたプログラムの場合)、 と同じ幅を持つ型、を使って表現すべきである。
カーネルのこの制限に対する対策として、 プログラムがリソース上限を 32 ビットの unsigned long で表現できる値よりも大きな値に設定しようとした際には、 glibc の setrlimit() ラッパー関数はこの上限値を黙って RLIM_INFINITY に変換していた。 言い換えると、指定されたリソース上限値は黙って無視されていた。
この問題は Linux 2.6.36 での以下の主な変更により解決された。
バージョン 2.13 以降の glibc では、 getrlimit() と setrlimit() システムコールの制限に対する回避手段として、 setrlimit() と getrlimit() を prlimit() を呼び出すラッパー関数として実装している。
以下のプログラムに 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); }
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), signal(7)
この man ページは Linux man-pages プロジェクトのリリース 3.79 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。
2015-01-22 | Linux |