一个sigaction的C++ wrap

本文介绍了一种在C++中封装信号处理的方法,通过自定义类CSignal实现对信号的捕获和处理,允许将信号回调至C++成员函数,避免了传统sigaction函数只能回调C函数的限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  在上一篇文章(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文件里去了。

转载于:https://www.cnblogs.com/coding-my-life/p/4231066.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值