一个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 = ;i < NSIG;i ++ )
{
m_actions[i] = NULL;
}
} /**
* @brief CSignal::~CSignal
* 销毁信号回调结构体
*/
CSignal::~CSignal()
{
for ( int i = ;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( < 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, );
} /**
* @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, );
} /**
* @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, );
} /* 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( );
}
}
#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文件里去了。
一个sigaction的C++ wrap的更多相关文章
- Glide源码导读
最近比较无聊,为了找点事干,就花了两天时间把Glide的源码大概看了一下.刚开始看Glide的源码头脑还是比较乱的,因为作者引入了几个概念,又大量用了泛型,如果不了解这些概念读起代码来就比较痛苦,我也 ...
- c 进程间的通信
在上篇讲解了如何创建和调用进程 c 进程和系统调用 这篇文章就专门讲讲进程通信的问题 先来看一段下边的代码,这段代码的作用是根据关键字调用一个Python程序来检索RSS源,然后打开那个URL #in ...
- 玩转Jquery,告别前端知道思路忘记知识点的痛苦
本节内容: 本章主要讲解一下jquery,主要是工作中用的前端框架是datetables框架,然后datetables框架又是基于jqeury研发的,所以要想学一个东西,就必须要了解其底层,不然走路都 ...
- 【JQ基础】
$(document).ready(function(){});//用来在DOM加载完成之后执行一系列预先定义好的函数. $(function(){});//$(document).ready(fun ...
- CSS3中的弹性布局——"em"的用法
使用CSS也好久了,但一直都是在使用“px”来设置Web元素的相关属性,未敢使用“em”.主要原因是,对其并不什么了解,只知道一点概念性的东西,前段时间在项目中要求使用“em”作为单位设置元素,所以从 ...
- Query节点操作,jQuery插入节点,jQuery删除节点,jQuery Dom操作
一.创建节点 var box = $('<div>节点</div>'); //创建一个节点,或者var box = "<div>节点</div> ...
- 第6章 DOM节点操作
一.创建节点 为了使页面更加智能化,有时我们想动态的在 html 结构页面添加一个元素标签,那么 在插入之前首先要做的动作就是:创建节点. varbox=$('<div id="box ...
- [译] jQuery 3 有哪些新东西
转自:https://github.com/cssmagic/blog/issues/59 jQuery 的横空出世,至今已有十个年头了,而它的长盛不衰显然不是没有理由的.jQuery 提供了极为友好 ...
- python知识点总结
此知识要点,是根据学习廖雪峰phthon3.0教程总结的,所以结构基本和这个教程的结构相同. 背景知识 python是什么?(1)python是一门编程语言,意味着可以用python编写程序,完成一定 ...
随机推荐
- 优化移动体验的HTML5技巧
简介 连轴转的刷新,不断变向的页面转换,以及tap事件的周期性的延迟仅仅是现在移动web环境令人头疼事情的一小部分.开发者正试图尽可能的靠近原生应用,但却经常被各种兼容问题,系统复位,和僵化的框架打乱 ...
- Android AdapterView View的复用机制 分析
对于ListView.GridView相信大家都不陌生,重写个BaseView,实现对于的几个方法,然后就完毕了我们的界面展示.而且在大部分情况下,我们载入特别多的Item也不会发生OOM,大家也都明 ...
- ssh命令
使用ssh命令登陆远程系统 ssh [ip/address] -l [登陆用户名] 如: ssh www.xyz.cn -l root
- DOM事件处理程序-事件对象-键盘事件
事件流: 事件流--描述的是从页面中接受事件的顺序 IE ---事件冒泡流:即事件最开始由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播至最不具体的那个节点(文档). Netsc ...
- TFS 用户与组管理(转)
作者:frank.liu kaka.zhou 安装 Team Foundation Server 后,会创建以下全局组.可以使用这些全局组来控制 Team Foundation 用户的权限. 组 权限 ...
- 开源项目Material Calendar View 学习记录 (一)
开源项目Material Calendar View 学习记录 Github: https://github.com/prolificinteractive/material-calendarview ...
- Gradle 编译时选择不同的 google-services.json
在做的安卓应用需要在 debug 和 release build中使用不同的谷歌服务账号,要用到不同的google-serivces.json ,手动替换的话太费时费力,好在万能的gradle可以完成 ...
- LinkedList : 双向链表与实现
所谓双向链表: (由此图可见老夫深厚的画功) 链表,就是由一个一个的节点连接组成. 在这里,每一个节点都是由三部分组成:上一个节点.当前节点的元素.下一个节点 当链表中只有一个节点的时候,这个节点指向 ...
- 刷新 tableview
UITableView对于iOS开发者来说一定不会陌生,很有可能你的APP很多界面都用到它.关于UITableView的文章,想必已经不计其数,没事可以多看看.特别是UITableView优化的文章, ...
- OpenCV——ANN神经网络
ANN-- Artificial Neural Networks 人工神经网络 //定义人工神经网络 CvANN_MLP bp; // Set up BPNetwork's parameters Cv ...