Process.getrlimit と Process.setrlimit が欲しいので実装してみました。 きっかけは、とあるシェルスクリプトを Ruby に移植しようと考えた時に、 ulimit が移植できなかったことです。 ちなみにそのシェルスクリプトというのは、しばらく前から http://www.rubyist.net/~akr/autobuild/ で試している Ruby の自動 build のスクリプトで、Ruby の build やテストが (万が一) 無限ループに陥ったり スタックを食いつぶした場合にプロセスを終了させたり、core がちゃんと残 るように設定するのに ulimit を使っています。 また、[ruby-dev:24405] で指摘されたような、デフォルトのスタックサイズ が小さい OS に対処するために、アプリケーションからスタックを大きくする ことにも使えるかも知れません。まぁ、アプリケーションにそういう対処をい れるのを推奨するのはどうかという気もするので、これは微妙な所ですが。 規格としては、SUSv3 に載っています。 実装としては、BSD と SVR4 の両方にあったようなので、現在の Unix ならた いていのにはあるんじゃないかと思います。 パッチはちょっと長いんですが、ポイントとしては * struct rlimit に対応するクラスは作らず、[cur,max] という配列を使う * rlim_t が定義されていない場合には、NotImplementedError にする * rlim_t が規格上 Unsigned integer type となっていて長さが決まっていな いのに真面目に対応している * ちなみに手元の Debian GNU/Linux では 64bit * NUM2ULL がなかったので足した * 関数定義が ANSI style なのは意図的 というところでしょうか。 とりあえず 1.9 にいれたいんですが、どうでしょう? Index: configure.in =================================================================== RCS file: /src/ruby/configure.in,v retrieving revision 1.249 diff -u -p -r1.249 configure.in --- configure.in 7 Nov 2004 15:16:06 -0000 1.249 +++ configure.in 13 Nov 2004 17:12:08 -0000 @@ -205,6 +205,12 @@ AC_CHECK_SIZEOF(float, 4) AC_CHECK_SIZEOF(double, 8) AC_CHECK_SIZEOF(time_t, 0) +AC_CHECK_TYPE(rlim_t, [AC_DEFINE(HAVE_RLIM_T)], [], [#include <sys/resource.h>]) +AC_CHECK_SIZEOF(rlim_t, 0, [ + #include <stdio.h> + #include <sys/resource.h> +]) + AC_CACHE_CHECK(for prototypes, rb_cv_have_prototypes, [AC_TRY_COMPILE([int foo(int x) { return 0; }], [return foo(10);], rb_cv_have_prototypes=yes, @@ -416,7 +422,8 @@ AC_CHECK_FUNCS(fmod killpg wait4 waitpid setitimer setruid seteuid setreuid setresuid setproctitle\ setrgid setegid setregid setresgid issetugid pause lchown lchmod\ getpgrp setpgrp getpgid setpgid initgroups getgroups setgroups\ - getpriority getrlimit dlopen sigprocmask sigaction _setjmp\ + getpriority getrlimit setrlimit\ + dlopen sigprocmask sigaction _setjmp\ setsid telldir seekdir fchmod mktime timegm cosh sinh tanh\ setuid setgid daemon) AC_ARG_ENABLE(setreuid, Index: process.c =================================================================== RCS file: /src/ruby/process.c,v retrieving revision 1.118 diff -u -p -r1.118 process.c --- process.c 1 Nov 2004 02:49:06 -0000 1.118 +++ process.c 13 Nov 2004 17:12:09 -0000 @@ -45,7 +45,7 @@ struct timeval rb_time_interval _((VALUE #ifdef HAVE_SYS_WAIT_H # include <sys/wait.h> #endif -#ifdef HAVE_GETPRIORITY +#ifdef HAVE_SYS_RESOURCE_H # include <sys/resource.h> #endif #include "st.h" @@ -1931,6 +1931,111 @@ proc_setpriority(obj, which, who, prio) #endif } +#ifdef HAVE_RLIM_T + +#if SIZEOF_RLIM_T == SIZEOF_INT +# define RLIM2NUM(v) UINT2NUM(v) +# define NUM2RLIM(v) NUM2UINT(v) +#elif SIZEOF_RLIM_T == SIZEOF_LONG +# define RLIM2NUM(v) ULONG2NUM(v) +# define NUM2RLIM(v) NUM2ULONG(v) +#elif SIZEOF_RLIM_T == SIZEOF_LONG_LONG +# define RLIM2NUM(v) ULL2NUM(v) +# define NUM2RLIM(v) NUM2ULL(v) +#else +# error cannot find an integer type which size is same as rlim_t. +#endif + +/* + * call-seq: + * Process.getrlimit(resource) => [cur_limit, max_limit] + * + * Gets the resource limit of the process. + * _cur_limit_ means current (soft) limit and + * _max_limit_ means maximum (hard) limit. + * + * _resource_ indicates the kind of resource to limit: + * such as <code>Process::RLIMIT_CORE</code>, + * <code>Process::RLIMIT_CPU</code>, etc. + * See Process.setrlimit for details. + * + * _cur_limit_ and _max_limit_ may be <code>Process::RLIM_INFINITY</code>, + * <code>Process::RLIM_SAVED_MAX</code> or + * <code>Process::RLIM_SAVED_CUR</code>. + * See Process.setrlimit and the system getrlimit(2) manual for details. + */ + +static VALUE +proc_getrlimit( + VALUE obj, + VALUE resource) +{ +#ifdef HAVE_GETRLIMIT + struct rlimit rlim; + + if (getrlimit(NUM2INT(resource), &rlim) < 0) { + rb_sys_fail("getrlimit"); + } + return rb_ary_new3(2, RLIM2NUM(rlim.rlim_cur), RLIM2NUM(rlim.rlim_max)); +#else + rb_notimplement(); +#endif +} + +/* + * call-seq: + * Process.setrlimit(resource, cur_limit, max_limit) => nil + * + * Sets the resource limit of the process. + * _cur_limit_ means current (soft) limit and + * _max_limit_ means maximum (hard) limit. + * + * _resource_ indicates the kind of resource to limit. + * Although the list of resources are OS dependent, + * SUSv3 defines following resources. + * + * [Process::RLIMIT_CORE] core size (bytes) + * [Process::RLIMIT_CPU] CPU time (seconds) + * [Process::RLIMIT_DATA] data segment (bytes) + * [Process::RLIMIT_FSIZE] file size (bytes) + * [Process::RLIMIT_NOFILE] file descriptors (number) + * [Process::RLIMIT_STACK] stack size (bytes) + * [Process::RLIMIT_AS] total available memory (bytes) + * + * Other <code>Process::RLIMIT_???</code> constants may be defined. + * + * _cur_limit_ and _max_limit_ may be <code>Process::RLIM_INFINITY</code>, + * which means that the resource is not limited. + * They may be <code>Process::RLIM_SAVED_MAX</code> or + * <code>Process::RLIM_SAVED_CUR</code> too. + * See system setrlimit(2) manual for details. + * + */ + +static VALUE +proc_setrlimit( + VALUE obj, + VALUE resource, + VALUE rlim_cur, + VALUE rlim_max) +{ +#ifdef HAVE_SETRLIMIT + struct rlimit rlim; + + rlim.rlim_cur = NUM2RLIM(rlim_cur); + rlim.rlim_max = NUM2RLIM(rlim_max); + + if (setrlimit(NUM2INT(resource), &rlim) < 0) { + rb_sys_fail("setrlimit"); + } + return Qnil; +#else + rb_notimplement(); +#endif +} + +#endif + static int under_uid_switch = 0; static void check_uid_switch() @@ -3608,6 +3713,57 @@ Init_process() rb_define_const(rb_mProcess, "PRIO_PROCESS", INT2FIX(PRIO_PROCESS)); rb_define_const(rb_mProcess, "PRIO_PGRP", INT2FIX(PRIO_PGRP)); rb_define_const(rb_mProcess, "PRIO_USER", INT2FIX(PRIO_USER)); +#endif + +#ifdef HAVE_RLIM_T +#ifdef HAVE_GETRLIMIT + rb_define_module_function(rb_mProcess, "getrlimit", proc_getrlimit, 1); +#endif +#ifdef HAVE_SETRLIMIT + rb_define_module_function(rb_mProcess, "setrlimit", proc_setrlimit, 3); +#endif +#ifdef RLIM_INFINITY + rb_define_const(rb_mProcess, "RLIM_INFINITY", RLIM2NUM(RLIM_INFINITY)); +#endif +#ifdef RLIM_SAVED_MAX + rb_define_const(rb_mProcess, "RLIM_SAVED_MAX", RLIM2NUM(RLIM_SAVED_MAX)); +#endif +#ifdef RLIM_SAVED_CUR + rb_define_const(rb_mProcess, "RLIM_SAVED_CUR", RLIM2NUM(RLIM_SAVED_CUR)); +#endif +#ifdef RLIMIT_CORE + rb_define_const(rb_mProcess, "RLIMIT_CORE", INT2FIX(RLIMIT_CORE)); +#endif +#ifdef RLIMIT_CPU + rb_define_const(rb_mProcess, "RLIMIT_CPU", INT2FIX(RLIMIT_CPU)); +#endif +#ifdef RLIMIT_DATA + rb_define_const(rb_mProcess, "RLIMIT_DATA", INT2FIX(RLIMIT_DATA)); +#endif +#ifdef RLIMIT_FSIZE + rb_define_const(rb_mProcess, "RLIMIT_FSIZE", INT2FIX(RLIMIT_FSIZE)); +#endif +#ifdef RLIMIT_NOFILE + rb_define_const(rb_mProcess, "RLIMIT_NOFILE", INT2FIX(RLIMIT_NOFILE)); +#endif +#ifdef RLIMIT_STACK + rb_define_const(rb_mProcess, "RLIMIT_STACK", INT2FIX(RLIMIT_STACK)); +#endif +#ifdef RLIMIT_AS + rb_define_const(rb_mProcess, "RLIMIT_AS", INT2FIX(RLIMIT_AS)); +#endif +#ifdef RLIMIT_LOCKS + rb_define_const(rb_mProcess, "RLIMIT_LOCKS", INT2FIX(RLIMIT_LOCKS)); +#endif +#ifdef RLIMIT_MEMLOCK + rb_define_const(rb_mProcess, "RLIMIT_MEMLOCK", INT2FIX(RLIMIT_MEMLOCK)); +#endif +#ifdef RLIMIT_NPROC + rb_define_const(rb_mProcess, "RLIMIT_NPROC", INT2FIX(RLIMIT_NPROC)); +#endif +#ifdef RLIMIT_RSS + rb_define_const(rb_mProcess, "RLIMIT_RSS", INT2FIX(RLIMIT_RSS)); +#endif #endif rb_define_module_function(rb_mProcess, "uid", proc_getuid, 0); Index: ruby.h =================================================================== RCS file: /src/ruby/ruby.h,v retrieving revision 1.107 diff -u -p -r1.107 ruby.h --- ruby.h 27 Oct 2004 09:29:25 -0000 1.107 +++ ruby.h 13 Nov 2004 17:12:09 -0000 @@ -265,6 +265,7 @@ unsigned long rb_fix2uint _((VALUE)); LONG_LONG rb_num2ll _((VALUE)); unsigned LONG_LONG rb_num2ull _((VALUE)); # define NUM2LL(x) (FIXNUM_P(x)?FIX2LONG(x):rb_num2ll((VALUE)x)) +# define NUM2ULL(x) rb_num2ull((VALUE)x) #endif #if HAVE_LONG_LONG && SIZEOF_OFF_T > SIZEOF_LONG -- [田中 哲][たなか あきら][Tanaka Akira]