前言
提示:要解决以下问题:
- sigsuspend使用的场景
- sigsuspend的实质
一、sigsuspend的应用场景
- 临时阻塞一个信号,以防止其中断正在执行的关键代码
- 解除对该信号的阻塞,然后暂停执行,直至有信号的传递
二、sigsuspend的实质
int sigsuspend(const sigset_t *mask);
调用sigsuspend()相当于以不可中段的方式执行以下操作:
sigprocmask(SIG_SETMASK,&mask,&prevMask); /*调整新的掩码*/
pause()
/*挂起之后再执行的信号处理函数*/
sigprocmask(SIG_SETMASK,&prevMask,NULL)
那我们写一段程序测试是否相当于执行以上代码。
- 向执行sigsuspend(SIGUSER1)的进程发送siguser1的信号,是否可以执行指定的信号处理器。
#define __GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
static volatile sig_atomic_t gotSigquit =0;
void printSigset(FILE *of, const char *prefix, const sigset_t *sigset)
{
int sig,cnt;
cnt = 0;
for (sig = 1; sig < NSIG; sig++){
if (sigismember(sigset,sig)){
cnt++;
fprintf(of, "%s%d (%s)\n",prefix,sig,strsignal(sig));
}
}
if (cnt == 0)
fprintf(of, "%s < empty signal set >\n",prefix);
}
int printSigMask(FILE *of, const char *msg)
{
sigset_t currMask;
if (msg != NULL){
fprintf(of, "%s",msg);
}
sigprocmask(SIG_BLOCK,NULL,&currMask);
printSigset(of,"\t\t", &currMask);
}
int printPendingSigs(FILE *of, const char *msg)
{
sigset_t pendingSigs;
if (msg != NULL)
fprintf(of,"%s",msg);
if (sigpending(&pendingSigs) == -1)
return -1;
printSigset(of,"\t\t",&pendingSigs);
return 0;
}
static void handler(int sig)
{
printf("caught signl %d,%s\n",sig,strsignal(sig)); /*unsafe*/
if (sig == SIGQUIT)
gotSigquit = 1;
}
int main(int argc, char **argv)
{
int loopNum;
time_t startTime;
sigset_t origMask,blockMask;
struct sigaction sa;
printSigMask(stdout, "Initial signal mask is:\n");
sigemptyset(&blockMask);
sigaddset(&blockMask,SIGQUIT);
sigaddset(&blockMask,SIGINT);
if (sigprocmask(SIG_BLOCK, &blockMask,&origMask) == -1)
exit(1);
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = handler;
if (sigaction(SIGINT,&sa, NULL) == -1){
exit(1);
}
if (sigaction(SIGQUIT,&sa,NULL) == -1)
exit(1);
for (loopNum = 1; !gotSigquit; loopNum++){
printf("===LOOP %d\n",loopNum);
/*假设一段关键代码的执行*/
printSigMask(stdout, "Staring critical section,signal mask is :\n");
for (startTime = time(NULL); time(NULL) < startTime + 4;){
continue; /*运行几秒钟*/
}
printPendingSigs(stdout,"before sigsuspend() - pending signals:\n");
if (sigsuspend(&origMask) == -1 && errno != EINTR)
{
exit(1);
}
}
if (sigprocmask(SIG_SETMASK, &origMask,NULL) == -1)
exit(1);
printSigMask(stdout, "==Exited loop\n Restored signal mask to: \n");
exit(1);
}
>>> a.out
Initial signal mask is:
< empty signal set >
===LOOP 1
Staring critical section,signal mask is :
2 (Interrupt)
3 (Quit)
^C SIGINT 处于阻塞状态
before sigsuspend() - pending signals:
2 (Interrupt)
caught signl 2,Interrupt
/*本身SIGINT处于阻塞状态,不应该显示caught signl 2,Interrupt,但是调用了sigsuspend 解除了SIGINT,*/
/*(执行了信号处理函数,但是因为sigsupend执行是解除阻塞挂起是原子操作,所以不会出现假死现象*/
===LOOP 2
Staring critical section,signal mask is :
2 (Interrupt)
3 (Quit)
^\before sigsuspend() - pending signals:
3 (Quit)
caught signl 3,Quit
==Exited loop
Restored signal mask to:
< empty signal set >
总结
- 这种问题的实质仍然是,信号传递的时机是:
- 进程再调度超时后,再次获得调度时,即一个时间片的开始处
- 系统调用完成时。
- suspend的实质就是确保信号的传递是在系统调用完成时,进入系统调用检测是否有未决信号,防止不能叫醒自己。