Libevent 信号事件 API 都是以 evsignal_ 开头,但并非是函数,而是宏定义,位于 event2/event_compat.h 中,定义如下:
// 将信号事件接加入libevent中,状态变为待决
#define evsignal_add(ev, tv) event_add((ev), (tv))
// 给一个信号事件赋值,需要传递事件指针,不常用
#define evsignal_assign(ev, b, x, cb, arg) \
event_assign((ev), (b), (x), EV_SIGNAL|EV_PERSIST, cb, (arg))
// 新建一个信号事件对象
#define evsignal_new(b, x, cb, arg) \
event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))
// 从libevent中删除信号事件
#define evsignal_del(ev) event_del(ev)
// 判断信号事件是否为待决状态
#define evsignal_pending(ev, tv) event_pending((ev), EV_SIGNAL, (tv))
// 判断信号事件是否为已初始化状态
#define evsignal_initialized(ev) event_initialized(ev)
可以看到,基本上就是对 event_ 等基础函数做了一个简单的封装。注意:在使用 evsignal_new 创建一个信号事件时,事件已经被标记为持久化了,所以不必担心信号只会响应一次。当然,若只需要信号响应一次,就只能用 event_new 函数创建信号事件。
Windows 上也支持信号事件,同样也有头文件 signal.h ,虽然大部分都和 Linux 上一样,但并不保证完全和 Linux 上兼容,所以使用时最好还是用宏定义分开。
下面是一个 Linux Libevent 信号事件实例:中断信号处理,即 Ctrl+C 和 Kill 信号处理。分别使用 evsignal_new 和 event_new 创造信号事件,源码如下:
#include
#include
#ifndef _WIN32
#include
#endif // !_WIN32
#include "event.h"
// SIGING 信号处理函数
// fd: 文件描述符; what: 事件类型; arg: 传递的参数
static void sig_int(evutil_socket_t fd, short what, void *arg) {
std::cout << "ctrl + c" << std::endl;
}
// SIGTERM(kill) 信号处理函数
// fd: 文件描述符; what: 事件类型; arg: 传递的参数
static void sig_term(evutil_socket_t fd, short what, void *arg) {
std::cout << "kill" << std::endl;
// 如果处于非待决,再次添加到libevent中,否则信号只会被响应一次
event *ev = (event*)arg;
if (!evsignal_pending(ev, NULL)) {
event_del(ev);
event_add(ev, NULL);
}
}
int main(int argc, char* argv[])
{
#ifdef _WIN32
// 初始化socket库
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
#else
// 忽略管道信号,因为发送数据给已关闭的socket会生成SIGPIPE信号,导致进程退出
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
return -1;
#endif
std::cout << "libevent signal test" << std::endl;
// 创建libevent上下文
event_base *base = event_base_new();
if (!base) {
std::cout << "event_base_new failed" << std::endl;
return -1;
}
std::cout << "event_base_new success" << std::endl;
// 添加 ctrl + c 信号事件,处于no pending状态
// evsignal_new隐藏的状态 EV_SIGNAL|EV_PERSIST
event *ev_sigint = evsignal_new(base, SIGINT, sig_int, base);
if (!ev_sigint) {
std::cerr << "SIGINT evsignal_new failed" << std::endl;
return -1;
}
// 添加事件到pending
if (event_add(ev_sigint, 0) != 0) {
std::cerr << "SIGINT event_add failed" << std::endl;
return -1;
}
// 添加kill信号,非持久,只进入一次
event *ev_sigterm = event_new(base, SIGTERM, EV_SIGNAL, sig_term, event_self_cbarg());
if (!ev_sigterm) {
std::cerr << "SIGTERM event_new failed" << std::endl;
return -1;
}
// 添加事件到pending
if (event_add(ev_sigterm, 0) != 0) {
std::cerr << "SIGTERM event_add failed" << std::endl;
return -1;
}
// 进入事件主循环
event_base_dispatch(base);
// 释放资源
event_free(ev_sigint);
event_base_free(base);
return 0;
}
注意,上面代码是为了演示信号事件创建的两种方法,本质上是一样的,都是调用 event_add
接口。实际使用过程中不用如此麻烦,可以直接调用 evsignal_new
创建信号事件。