当“打开的文件太多”时,如何防止 event_base_new() 发出 SIGABRT

问题描述 投票:0回答:1

我们正在使用 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
)? (即使我们对此无能为力。)

c++ file-descriptor libevent
1个回答
0
投票

不幸的是答案是否定的 - 在这种情况下没有办法阻止 libevent 调用

exit(1)


请注意,在 libevent 1.4.14 中,函数

event_base_new
永远不会返回
NULL
- 即它要么设法创建一个新的事件库,要么终止尝试的进程:

event.h 第 273 行:

/**
  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

event.c 第 173 行:

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);
}
© www.soinside.com 2019 - 2024. All rights reserved.