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

来源:互联网 发布:it项目经理成长手记 编辑:程序博客网 时间:2024/05/29 18:25

0x01 缘起

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

如下场景:

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

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

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

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

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

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

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

信号设置。

0x02 ISE代码

      

///////////////////////////////////////////////////////////////////////////////// class SignalMasker - 信号屏蔽类/* * 屏蔽信号的目的是让程序善后了再进行退出; * * */#ifdef ISE_LINUXclass 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;}

原创粉丝点击