Linux c/c++後端程式設計,訊號量,遮蔽和不遮蔽,訊號捕獲;
阿新 • • 發佈:2019-02-03
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;
}