Libevent 学习五:Libevent 信号事件

Libevent 信号事件

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 创建信号事件。

你可能感兴趣的:(libevent,我的文章,学习,linux,c++)