一个sigaction的C++ wrap

  在上一篇文章(http://www.cnblogs.com/coding-my-life/p/4220128.html)中,提到了libev提供了处理信号的C++ wrap。但我显然接受不了需要进入libev的事件循环(event loop)后才能捕捉到消息的缺点。于是决定依照libev的思路自己写一个C++的wrap。

  分析了一下libev的源代码,主要有以下几个要点:

  • sigaction函数只能设置一个C的函数指针作为回调函数,不能回调成员函数。因此需要通过C函数回调成员函数。
  • 回调一个成员函数,必须要有对象(或它的指针)和成员函数指针。因此要设计一个结构来存储对象指针及成员函数指针。
  • 要存储数据,主要涉及内存的分配、释放及管理。因此设计一个全局的对象来管理这些数据结构。

根据上面的要点,可以设计出下面的代码:

#include <cstddef>    /* for NULL */
#include <signal.h>
#include <cassert>

static inline void sighandler( int signum );

/**
 * @brief The sig_action struct
 * 存储回调对象指针及回调函数
 */
struct sig_action
{
    void *object;
    void (*cb)( void *object,int signum );
};

/**
 * @brief The CSignal class
 * 对sigaction封装的C++类
 */
class CSignal
{
public:
    explicit CSignal();
    ~ CSignal();

    int reset( int signum );
    int ignore( int signum );
    void feed_signal( int signum );
    template<class K,void (K::*pf)(int)>
        int set( K *object,int signum,int sa_flag );
private:

    /* NSIG The value of this symbolic constant is the total number of signals defined.
       Since the signal numbers are allocated consecutively, NSIG is also one greater
       than the largest defined signal number.
       define in sys/signal.h
    */
    struct sig_action* m_actions[NSIG]; 

    template<class K, void (K::*method)(int)>
    static void method_thunk ( void *object,int signum )
    {
      (static_cast<K *>(object)->*method)(signum);
    }
};

/**
 * @brief CSignal::CSignal
 * 初始化信号回调结构体
 */
CSignal::CSignal()
{
    for ( int i = 0;i < NSIG;i ++ )
    {
        m_actions[i] = NULL;
    }
}

/**
 * @brief CSignal::~CSignal
 * 销毁信号回调结构体
 */
CSignal::~CSignal()
{
    for ( int i = 0;i < NSIG;i ++ )
    {
        if ( m_actions[i] )
        {
            delete m_actions[i];
            m_actions[i] = NULL;
        }
    }
}

/**
 * @brief CSignal::set
 * 设置信号对象回调
 */
template<class K,void (K::*pf)(int)>
int CSignal::set( K *object,int signum,int sa_flag )
{
    assert( 0 < signum && NSIG > signum ); /* valid signals range from 1 to NSIG-1 */

    struct sigaction sa;

    sa.sa_handler = sighandler;
    sigemptyset( &sa.sa_mask );     /* will not block any other signal */
    sa.sa_flags = sa_flag;          /* usually SA_RESTART or SA_RESETHAND,man sigaction for more */

    if ( NULL == m_actions[signum] ) /* first init */
        m_actions[signum] = new struct sig_action;

    m_actions[signum]->object = (void*)object;
    m_actions[signum]->cb = method_thunk<K,pf>;

    return sigaction( signum,&sa,0 );
}

/**
 * @brief CSignal::feed_signal
 * @param signum
 * 触发回调
 */
void CSignal::feed_signal( int signum )
{
    struct sig_action *sac = m_actions[signum];
    assert( NULL != sac );

    sac->cb( sac->object,signum );
}

/**
 * @brief CSignal::ignore
 * @param signum
 * @return  返回值同sigaction
 * 忽略信号
 */
int CSignal::ignore( int signum )
{
    struct sigaction sa;
    sa.sa_handler = SIG_IGN;
    sigemptyset( &sa.sa_mask );

    return sigaction( signum,&sa,0 );
}

/**
 * @brief CSignal::reset
 * @param signum
 * @return 返回值同sigaction
 * 重置信号为默认
 */
int CSignal::reset( int signum )
{
    struct sigaction sa;
    sa.sa_handler = SIG_DFL;
    sigemptyset( &sa.sa_mask );

    return sigaction( signum,&sa,0 );
}

/*                           global funtion and object                             */

static class CSignal signal_watcher;

static inline class CSignal *default_signal_watcher()
{
    return ((class CSignal*)&signal_watcher);
}

#define CSIG_DEFAULT default_signal_watcher()

/**
 * @brief sighandler
 * @param signum
 * 信号回调函数主入口
 */
static inline void sighandler( int signum )
{
    CSIG_DEFAULT->feed_signal( signum );
}

为了测试上面的代码,还写了一些辅助的测试类:

#ifndef CBACKEND_H
#define CBACKEND_H

/**
 * @brief The CBackend class
 * 后台工作类
 */
class CBackend
{
public:
    CBackend();

    void sighandler( int signum );
    void loop();
};

#endif // CBACKEND_H
#include "CBackend.h"

#include <signal.h>    /* for strsignal */
#include <unistd.h>    /* for sleep in ubuntu 14.04 */
#include <iostream>    /* for std::cout */
#include <cstring>     /* for strsignal */

CBackend::CBackend()
{
}

/**
 * @brief CBackend::sighandler
 * @param signum
 * 处理信号
 */
void CBackend::sighandler( int signum )
{
    std::cout << "catch signal " << signum << " -- " << strsignal( signum ) << std::endl;
}

/**
 * @brief CBackend::loop
 * 后台工作循环
 */
void CBackend::loop()
{
    while ( true )
    {
        std::cout << "I‘m working ..." << std::endl;
        sleep( 1 );
    }
}
#include "signal++.h"
#include "CBackend.h"

/**
 * @brief main
 * @return
 * CSignal example
 */
int main()
{
    CBackend backend;

    CSIG_DEFAULT->set<CBackend,&CBackend::sighandler>( &backend,SIGINT,SA_RESTART );

    backend.loop();
}

编译并测试:

g++ -o example example.cpp CBackend.cpp
./example

按下ctrl+c就可以看到catch signal 2 -- Interrupt这样的消息。

这样,基本完成了信号回调C++成员函数的初衷。不过,上面的代码也还有些问题:使用了全局变量。不过目前暂时没有更好的方案。另一方面,由于使用了模板,不劫持分离编译,干脆把所有函数都写到.h文件里去了。

时间: 2024-10-10 05:25:42

一个sigaction的C++ wrap的相关文章

WPF 依赖属性概念

理解依赖属性 在 WPF 中变成相比较于 传统 Windows Forms 变成发生了较大的改变. 属性现在以一组服务的形式提供给开发人员. 这组服务就叫做属性系统. 由 WPF 属性系统所支持的属性成为依赖属性. 依赖属性的概念 WPF 在依赖属性中提供了标准属性无法提供的功能, 特性如下: 决定属性值: 依赖属性的属性值可以在运行时有其他元素或者是其他信息所决定, 决定的过程具有一个优先次序. 自动验证或变更通知: 依赖属性哟一个自定的回调方法, 当属性值变更时被执行, 这个回调能验证新的值

jQuery基础(DOM篇,append(),after(),prepend(),insertAfter(),节点删除,遍历方法each())

1.DOM创建节点及节点属性   创建流程比较简单,大体如下: - 创建节点(常见的:元素.属性和文本) - 添加节点的一些属性 - 加入到文档中   流程中涉及的一点方法: - 创建元素:document.createElement - 设置属性:setAttribute - 添加文本:innerHTML - 加入文档:appendChild   2.jQuery节点创建与属性的处理 创建元素节点: 可以有几种方式,后面会慢慢接触.常见的就是直接把这个节点的结构给通过HTML标记字符串描述出来

Android中直播视频技术探究之---基础核心类ByteBuffer解析

一.前言 前一篇文章我们介绍了Android中直播视频技术的基础大纲知识,这里就开始一一讲解各个知识点,首先主要来看一下视频直播中的一个重要的基础核心类:ByteBuffer,这个类看上去都知道了,是字节缓冲区处理字节的,这个类的功能非常强大,也在各个场景都有用到,比如网络数据底层处理,特别是结合网络通道信息处理的时候,还有就是后面要说到的OpenGL技术也要用到,当然在视频处理中也是很重要的,因为要处理视频流信息,比如在使用MediaCodec进行底层的视频流编码的时候,处理的就是字节,我们如

jQuery DOM 节点操作

DOM 中有一个非常重要的功能,就是节点模型,也就是 DOM 中的“M”.页面中的元素结构就是通过这种节点模型来互相对应着的,我们只需要通过这些节点关系,可以创建.插入.替换.克隆.删除等等一些列的元素操作. 一.创建节点 为了使页面更加智能化,有时我们想动态的在 html 结构页面添加一个元素标签,那么 在插入之前首先要做的动作就是:创建节点. $('div').append('<strong>节点</strong>'); //向 div 内部插入 strong 节点 $('di

jQuery概述

目录 jQuery的加载 jQuery基础 jQuery对象 jQuery构造函数 jQuery构造函数返回的结果集 链式操作 $(document).ready() $.noConflict方法 jQuery实例对象的方法 结果集的过滤方法 DOM相关方法 添加.复制和移动网页元素的方法 动画效果方法 其他方法 事件处理 事件绑定的简便方法 on方法,trigger方法,off方法 event对象 一次性事件 参考链接 jQuery是目前使用最广泛的JavaScript函数库.据统计,全世界5

freemarker程序开发

1.程序开发入门 1.1 创建配置实例 首先,你应该创建一个freemarker.template.Configuration的实例,然后调整它的设置.Configuration实例是存储FreeMarker应用级设置的核心部分.同时,它也处理创建和缓存预解析模板的工作.也许你只在应用(可能是servlet)生命周期的开始执行它一次: Configuration cfg = new Configuration();// 指定模板文件从何处加载的数据源,这里设置成一个文件目录.cfg.setDir

Jquery(二)——DOM篇(转)

DOM创建节点及节点属性 通过JavaScript可以很方便的获取DOM节点,从而进行一系列的DOM操作.但实际上一般开发者都习惯性的先定义好HTML结构,但这样就非常不灵活了. 试想下这样的情况:如果我们通过AJAX获取到数据之后然后才能确定结构的话,这种情况就需要动态的处理节点了 本文向大家介绍一下如何使用JavaScript创建div节点元素,主要包括创建div节点元素的属性和创建div节点元素的样式两大部分内容,相信本文介绍一定会让你有所收获. 先介绍下需要用到的浏览器提供的一些原生的方

DOM节点的复制和替换(jQuery)

1DOM拷贝clone() 克隆节点是DOM的常见操作,jQuery提供一个clone方法,专门用于处理dom的克隆 .clone()方法深度 复制所有匹配的元素集合,包括所有匹配元素.匹配元素的下级元素.文字节点. clone方法比较简单就是克隆节点,但是需要注意,如果节点有事件或者数据之类的其他处理,我们需要通过clone(ture)传递一个布尔值ture用来指定,这样不仅仅只是克隆单纯的节点结构,还要把附带的事件与数据给一并克隆了 例如: HTML部分 <div></div>

CUDA系列学习(二)CUDA memory &amp; variables

本文来介绍CUDA的memory和变量存放,分为以下章节: (一).CPU Memory 结构 (二).GPU Memory结构 (三).CUDA Context (四).kernel设计 (五).变量 & Memory 5.1 global arrays 5.2 global variables 5.3 Constant variables 5.4 Register 5.5 Local Array 5.6 Shared Memory 5.7 Texture Memory 5.8 总结 (一).