我们正在使用 libevent 1.4.14。
在我们的生产环境中,我们发现当主机用完文件描述符时,我们的进程会因来自 libevent 的 SIGABRT 崩溃而崩溃。
考虑以下因素
#include <event.h>
#include <sys/epoll.h>
#include <sys/resource.h>
#include <iostream>
int main(int argc, char* argv[]) {
struct rlimit rl;
rl.rlim_cur = rl.rlim_max = 400; // artificially limit number of fds
int ret = setrlimit(RLIMIT_NOFILE, &rl);
if (ret) {
int errno_ = errno;
std::cerr << "unable to set RLIMIT_NOFILE " << errno_ << std::endl;
}
for (int i = 0; i < 400; i++) {
event_base_new();
}
}
这最终会崩溃
[err] evsignal_init: socketpair: Too many open files
libevent exit code: 1
我不会将其包含在这里,但我们有一个信号处理程序,可以报告来自内部 SIGABRT 的信号处理程序
@ 00000000025ca144 event_err
[...]/log.c:88
@ 00000000025cc0e2 evsignal_init
[...]/signal.c:112
@ 00000000025cb73b epoll_init
[...]/epoll.c:295
@ 00000000025c8f41 event_base_new
[...]/event.c:192
有什么方法可以防止
event_base_new
使进程崩溃,而不是让它冒出错误(例如返回 NULL
)? (即使我们对此无能为力。)
不幸的是答案是否定的 - 在这种情况下没有办法阻止 libevent 调用
exit(1)
。
请注意,在 libevent 1.4.14 中,函数
event_base_new
永远不会返回 NULL
- 即它要么设法创建一个新的事件库,要么终止尝试的进程:
/**
Initialize the event API.
Use event_base_new() to initialize a new event base, but does not set
the current_base global. If using only event_base_new(), each event
added must have an event base set with event_base_set()
@see event_base_set(), event_base_free(), event_init()
*/
struct event_base *event_base_new(void);
(请注意,还没有“如果出现错误,则返回 NULL”子句 - 这是随 libevent 2 添加的)
因此,鉴于 API 限制,确实没有办法优雅地处理这个问题。
如果你检查
event_base_new
的实现,情况确实如此 - 它总是返回一个非空指针或终止。
event_err
和event_errx
都无条件调用exit
。
struct event_base* event_base_new(void) {
int i;
struct event_base *base;
if ((base = calloc(1, sizeof(struct event_base))) == NULL)
event_err(1, "%s: calloc", __func__);
// ...
base->evbase = NULL;
for (i = 0; eventops[i] && !base->evbase; i++) {
base->evsel = eventops[i];
base->evbase = base->evsel->init(base);
}
if (base->evbase == NULL)
event_errx(1, "%s: no event mechanism available", __func__);
// ...
return (base);
}