linux sigsuspend 函數的作用
2010-07-08 19:36:30
分類: LINUX
Unix提供了等待訊號的系統調用,sigsuspend就是其中一個,在CU(www.chinaunix.net)上曾經討論過一個關於該系統調用的問題,這裡也做一下解疑。
CU 網友討論的問題的核心就是到底sigsuspend先返回還是signal handler先返回。這個問題Stevens在《Unix環境進階編程》一書中是如是回答的“If a signal is caught and if the signal handler returns, then sigsuspend returns and the signal mask of the process is set to its value before the call to sigsuspend.”,由於sigsuspend是原子操作,所以這句給人的感覺就是先調用signal
handler先返回,然後sigsuspend再返回。但其第一個例子這麼講又說不通,看下面的代碼:
CU上討論該問題起於中的該例子:
int main(void) {
sigset_t newmask, oldmask, zeromask;
if (signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal(SIGINT) error");
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
/* block SIGINT and save current signal mask */
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
/* critical region of code */
pr_mask("in critical region: ");
/* allow all signals and pause */
if (sigsuspend(&zeromask) != -1)
err_sys("sigsuspend error");
pr_mask("after return from sigsuspend: ");
/* reset signal mask which unblocks SIGINT */
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
/* and continue processing ... */
exit(0);
}
static void sig_int(int signo) {
pr_mask("\nin sig_int: ");
return;
}
結果:
$a.out
in critical region: SIGINT
^C
in sig_int: SIGINT
after return from sigsuspend: SIGINT
如果按照sig_handler先返回,那麼SIGINT是不該被列印出來的,因為那時屏蔽字還沒有恢複,所有訊號都是不阻塞的。那麼是Stevens說錯了嗎?當然沒有,只是Stevens沒有說請在sigsuspend的原子操作中到底做了什嗎?
sigsuspend的整個原子操作過程為:
(1) 設定新的mask阻塞當前進程;
(2) 收到訊號,恢複原先mask;
(3) 調用該進程設定的訊號處理函數;
(4) 待訊號處理函數返回後,sigsuspend返回。
大致就是上面這個過程,噢,原來signal handler是原子操作的一部分,而且是在恢複屏蔽字後執行的,所以上面的例子是沒有問題的,Stevens說的也沒錯。由於Linux和Unix的千絲萬縷的聯絡,所以在兩個平台上絕大部分的系統調用的語義是一致的。上面的sigsuspend的原子操作也是從《深入理解Linux核心》一書中揣度出來的。書中的描述如下:
The sigsuspend( ) system call puts the process in the TASK_INTERRUPTIBLE state, after having blocked the standard signals specified by a bit mask array to which the mask parameter points. The process will wake up only when a nonignored, nonblocked signal is
sent to it. The corresponding sys_sigsuspend( ) service routine executes these statements:
mask &= ~(sigmask(SIGKILL) | sigmask(SIGSTOP));
spin_lock_irq(¤t->sigmask_lock);
saveset = current->blocked;
siginitset(¤t->blocked, mask);
recalc_sigpending(current);
spin_unlock_irq(¤t->sigmask_lock);
regs->eax = -EINTR;
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule( );
if (do_signal(regs, &saveset))
return -EINTR;
}
而最後的do_signal函數調用則是負責調用User Signal Handler的傢伙。我想到這CU上的那個問題該被解疑清楚了吧。