避免在libgmp中中止
我有一些使用libgmp的代码。 在某些时候,用户可以请求非常大数量的阶乘。 不幸的是,这导致libgmp引发中止信号。
例如,以下代码:
#include #include #include int main() { mpz_t result; mpz_init(result); mpz_fac_ui(result, 20922789888000); std::cout << mpz_get_si(result) << std::endl; }
结果是:
$ ./test gmp: overflow in mpz type Aborted
显然,产生的数字非常大。 无论如何,还是比中止更优雅地处理错误。 这是一个基于GUI的应用程序,它中止是处理此类问题的最不可取的方法。
在应用程序中优雅地处理这些错误的最佳方法可能是分离辅助进程以执行GMP计算。 如果辅助进程被SIGABRT
杀死,则您的父进程可以检测到该进程并向用户报告错误。
(以下是我的原始答案,根据GMP文档有“未定义的结果” – 为了完整性而留在这里)。
如果为使用longjmp()
SIGABRT
安装信号处理程序,则可以捕获错误:
jmp_buf abort_jb; void abort_handler(int x) { longjmp(abort_jb, 1); } int dofac(unsigned long n) { signal(SIGABRT, abort_handler); if (setjmp(abort_jb)) goto error; mpz_t result; mpz_init(result); mpz_fac_ui(result, 20922789888000); std::cout << mpz_get_si(result) << std::endl; signal(SIGABRT, SIG_DFL); return 0; error: signal(SIGABRT, SIG_DFL); std::cerr << "Caught SIGABRT from GMP.\n"; return 1; }
根据mpz / realloc.c和mpz / realloc2.c中的代码,看起来你运气不好。 如果请求太多内存,它只是这样做:
if (UNLIKELY (new_alloc > INT_MAX)) { fprintf (stderr, "gmp: overflow in mpz type\n"); abort (); }
用LD_PRELOAD
覆盖abort()
。
什么是LD_PRELOAD技巧?
编辑:为了使答案更加独立,我在这里复制了答案的文字:
如果将LD_PRELOAD设置为共享对象的路径,则将在任何其他库(包括C运行时libc.so)之前加载该文件。 因此,要使用特殊的malloc()实现运行ls,请执行以下操作:
$ LD_PRELOAD=/path/to/my/malloc.so /bin/ls
致JesperE的学分。