1. 程式人生 > >Linux c/c++後端程式設計,訊號量,遮蔽和不遮蔽,訊號捕獲;

Linux c/c++後端程式設計,訊號量,遮蔽和不遮蔽,訊號捕獲;

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;
}