您的位置:首页 > 运维架构 > Linux

Linux c/c++后端编程,信号量,屏蔽和不屏蔽,信号捕获;

2017-06-20 18:05 274 查看

0x01 缘起

在 linux c/c++后端编程的过程中,我们经常对捕获和捕获一些信号的处理。主要是在程序收到相关信号时能进行安全的退出,做一些善后处理。

如下场景:

Linux下的线程实质上是轻量级进程(light weighted process),线程生成时会生成对应的进程控制结构,只是该结构与

父线程的进程控制结构共享了同一个进程内存空间。 同时新线程的进程控制结构将从父线程(进程),处复制得到同样

的进程信息,如打开文件列表和信号阻塞掩码等。由于我们是在子线程生成之后修改了信号阻塞掩码,此刻子线程使用

的是主线程原有的进程信息,因此子线程仍然会对SIGINT和SIGTERM信号进行反应,因此当我们用Ctrl+C发出了

SIGINT信号的时候,主进程不处理该信号,而子进程(线程)会进行默认处理,即退出。子进程退出的同时会向父

进程(线程)发送SIGCHLD信号,表示子进程退出,由于该信号没有被阻塞,因此会导致主进程(线程)也立

刻退出,出现了前述的运行情况。因而该问题的一个解决方法是在子线程生成前进行信号设置,或在子线程内部进行

信号设置。

0x02 ISE代码

      ///////////////////////////////////////////////////////////////////////////////
// class SignalMasker - 信号屏蔽类
/*
* 屏蔽信号的目的是让程序善后了再进行退出;
*
* */

#ifdef ISE_LINUX
class SignalMasker : boost::noncopyable
{
public:
//定义时必须显示的定义,如sigMasker(true),而不能sigMasker = true;
explicit SignalMasker(bool isAutoRestore = false);
//继承时要调用析构函数
virtual ~SignalMasker();

// 设置 Block/UnBlock 操作所需的信号集合
void setSignals(int sigCount, ...);
void setSignals(int sigCount, va_list argList);

// 在进程当前阻塞信号集中添加 setSignals 设置的信号
void block();
// 在进程当前阻塞信号集中解除 setSignals 设置的信号
void unBlock();

// 将进程阻塞信号集恢复为 Block/UnBlock 之前的状态
void restore();

private:
int sigProcMask(int how, const sigset_t *newSet, sigset_t *oldSet);

private:
sigset_t oldSet_;
sigset_t newSet_;
bool isBlock_; //屏蔽信号
bool isAutoRestore_; //自动恢复
};
#endif
SignalMasker::SignalMasker(bool isAutoRestore) :
isBlock_(false),
isAutoRestore_(isAutoRestore)
{
sigemptyset(&oldSet_);//清空信号集
sigemptyset(&newSet_);
}

//-----------------------------------------------------------------------------

SignalMasker::~SignalMasker()
{
if (isAutoRestore_) restore(); //析构时还原设置参数前的场景
}

//-----------------------------------------------------------------------------
// 描述: 设置 Block/UnBlock 操作所需的信号集合
// 参数: sigCount信号数,后面为参数;
//-----------------------------------------------------------------------------
void SignalMasker::setSignals(int sigCount, va_list argList)
{
sigemptyset(&newSet_); //新的信号集清空
for (int i = 0; i < sigCount; i++)
sigaddset(&newSet_, va_arg(argList, int));//添加信号到信号集
}

//-----------------------------------------------------------------------------

void SignalMasker::setSignals(int sigCount, ...)
{
va_list argList;
va_start(argList, sigCount);
setSignals(sigCount, argList);
va_end(argList);
}

//-----------------------------------------------------------------------------
// 描述: 在进程当前阻塞信号集中添加 setSignals 设置的信号
//-----------------------------------------------------------------------------
void SignalMasker::block()
{
sigProcMask(SIG_BLOCK, &newSet_, &oldSet_);
isBlock_ = true;
}

//-----------------------------------------------------------------------------
// 描述: 在进程当前阻塞信号集中解除 setSignals 设置的信号
//-----------------------------------------------------------------------------
void SignalMasker::unBlock()
{
sigProcMask(SIG_UNBLOCK, &newSet_, &oldSet_);
isBlock_ = true;
}

//-----------------------------------------------------------------------------
// 描述: 将进程阻塞信号集恢复为 Block/UnBlock 之前的状态
//-----------------------------------------------------------------------------
void SignalMasker::restore()
{
if (isBlock_)
{
//恢复到之前的状态
sigProcMask(SIG_SETMASK, &oldSet_, NULL);
isBlock_ = false;
}
}

//-----------------------------------------------------------------------------

int SignalMasker::sigProcMask(int how, const sigset_t *newSet, sigset_t *oldSet)
{
int result;
//增加信号集 how 告诉是屏蔽还是解除,oldSet用户还原;
if ((result = sigprocmask(how, newSet, oldSet)) < 0)
iseThrowException(strerror(errno));

return result;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: