未决:在信号产生和递送之间的时间间隔内,我们称信号是未决的。
阻塞信号递送:如果为进程产生了一个阻塞信号,而且对该信号的动作是默认动作或捕捉该信号,则为该进程将此信号保持为未决的,直到进程对此信号解除阻塞,或者对此信号的动作更改为忽略。
信号屏蔽字:每个进程都有一个信号屏蔽字,它规定了当前要阻塞递送到该进程的信号集。
信号集:可以表示多个信号的数据类型,定义数据类型为sigset_t
#include
int sigemptyset(sigset_t *set); //初始化信号集为空
int sigfillset(sigset_t *set); //初始化信号集包括所有信号
int sigaddset(sigset_t *set,int signo); // 将信号signo加入到set信号集
int sigdelset(sigset_t *set,int signo); //将信号signo从信号集中删除
1.函数kill和raise
#include
int kill(pid_t pid,int signo);
int raise(int signo);
返回值: 成功返回0,失败返回-1
kill的pid参数
pid>0 将该信号发送进程ID为pid的进程
pid==0 将信号发送给与发送进程同一进程组的所有进程,但不包 括系统进程集,既内核进程和init。
pid<0 将该进程发送给其他进程组ID为pid绝对值的所有进程,不包括系统进程集
pid==-1将该信号发送给进程有权限向它们发送信号的所有进程,不包括系统进程集
如上所述,进程发送信号是要权限的,超级用户可以发送给所有的进程。非超级的,发送者的实际用户ID或者有效用户ID必须等于接收者的实际用户ID或者有效用户ID。
2.函数alarm和pause
alarm函数可以设置一个闹钟时间,超时产生SIGALRM信号。如忽略或者不捕捉该信号,系统默认动作是终止调用alarm函数的进程。
#include
unsigned int alarm(unsigned int seconds);
返回值:0,或者是以前设置的闹钟时间的剩余时间
每个进程只能有一个闹钟时间。如果在调用alarm之前已经为该进程注册的闹钟时间还未超时,则该闹钟的剩余时间作为本次alarm函数调用的值返回。以前注册的闹钟时间则被新值代替。如果本次调用的seconds值是0,则取消以前的闹钟,其剩余值,作为alarm函数的返回值。
pause函数调用进程挂起,直到捕捉到一个信号
#include
int paush(void);
返回值:-1;errno设置为EINTR
给出一个用alarm和pause实现sleep的实例
#include
#include
#include
#include
static jmp_buf env_alrm;
static void myalrm(int signo) //信号处理函数
{
longjmp(env_alrm,1); //局部跳转
}
int mysleep(int sec)
{
int minsec,ret,tmp=0;
if(signal(SIGALRM,myalrm)==SIG_EER)
return sec;
minsec=alrm(0); //得到上一次未超时定时器所剩的时间
ret=sec>minsec?(sec-minsec):0;
if(setjmp(env_alrm)==0) //局部跳转
{
if(minsec) //考虑之前设置了定时器
alarm(minsecsec){ //若前面登记的定时器比睡眠时间长,退出后需要复位之前定时器剩下的时间
alarm(minsec-sec);
}
if(tmp)
ret+=tmp;
return ret;
}
int main()
{
int othersec;
alarm(6);
othersec=mysleep(3);
printf("return %d\n",othersec);
return 0;
}
3.函数sigprocmask
调用函数sigprocmask可以检测和更改当前阻塞而不能递送给进程的信号集。
#include
int sigprocmask(int how,const sigset_t *restrict set,sigset_t *restrict oset);
返回值:成功返回0,失败返回-1;
若oset为非空指针,进程当前的信号屏蔽子通过oset返回。
若set为非空指针,参数how指示进行何种操作。若为空,则不改变当前信号屏蔽字,how参数也无用。
how | 说明 |
---|---|
SIG_BLOCK | 新set中的信号加到信号屏蔽字中 |
SIG_UNBLOCK | 将set中信号从当前信号屏蔽子中取出 |
SIG_SETMASK | 该进程新的信号屏蔽是set指向的值 |
4.函数sigismember
函数sigismember用来测试参数signum 代表的信号是否已加入至参数set信号集里。
int sigismember(const sigset_t *set,int signum)
返回值:有该信号,返回1,无则返回0;出错返回-1
5.函数sigpending
sigpending函数返回在送往进程的时候被阻塞挂起的信号集合。这个信号集合通过参数set返回。
#include
int sigpengding(sigset_t *set);
返回值:成功返回0,出错返回-1
下面这个例程,用到了上面几个函数
static void sig_quit(int signo)
{
printf("caught sigquit\n");
}
int main()
{
sigset_t newmak,oldmask,pendmsk;
if(signal(SIGQUIT,sig_quit)==SIG_ERR)
printf("can not catch quit\n");
sigemptyset(&newmask); //信号集清空函数
sigaddmask(&newmask,SIGQUIT); //将信号添加到信号集中
if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0) //将信号SIGQUIT添加到信号屏蔽字中
printf("err\n");
sleep(5);
if(sigpending(&pendmask)<0) //检测当前进程中阻塞的信号,通过pendmask返回
printf("sigpending err\n");
if(sigismember(&pendmask,SIGQUIT)) //检测SIGQUIT是否在pendmask信号集中
printf("sigquit pending\n");
if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0) //将SIGQUIT信号取消屏蔽
printf("err\n");
sleep(5);
exit(0);
}
6.函数sigaction
sigaction函数的功能是检查或者修改与指定信号相关联的动作。此函数取代了早期使用的signal函数。
#include
int sigaction(int signo,conststruct sigaction*restrict act,
struct sigaction*restrict oact);
返回值:成功返回0,失败返回-1
给信号signo设置新的信号处理函数act, 同时保留该信号原有的信号处理函数oldact。
此函数用到了一个数据结构
struct sigaction{
void (*sa_handler)(int);
sigset_t sa_mask;
int sa_flag;
void (*sa_sigaction)(int,siginfo_t*,void*);
};
sa_handler字段:包含一个信号捕捉函数的地址。
sa_mask字段:说明了一个信号集,在调用该信号捕捉函数之前,这一信号集要加进进程的信号屏蔽字中。仅当从信号捕捉函数返回时再将进程的信号屏蔽字复位为原先值。
sa_flags字段:指定对信号进行处理的 各个选项。有张表格,我就不列了,自己百度吧。简单说明下面程序用到的两个标志
SA_NODEFER: 当信号处理函数正在进行时,不堵塞对于信号处理函数自身信号功能。
SA_RESETHAND:当用户注册的信号处理函数被执行过一次后,该信号的处理函数被设为系统默认的处理函数。
sa_sigaction字段:是一个替代的信号处理程序,当sa_flags 为SA_SIGINFO时,使用该信号处理程序。sa_sigaction和sa_handler,应用只能一次调用其中一个。
static void sig_quit(int signo)
{
printf("catch quit\n");
}
int main()
{
struct sigaction act,oldact;
act.sa_handler = sig_quit;
act.sa_flags = SA_NODEFER | SA_RESETHAND;
sigaction(SIGINT,&act,&oldact);
sleep(5);
printf("hello \n");
return 0;
}
7.函数sigsuspend
sigsuspend函数和pause函数一样,可以使进程挂起(进入睡眠状态),直至有信号发生。
sigsuspend函数的参数是一个信号集,这个信号集是用来屏蔽信号的,信号集中存放了要屏蔽的信号。
如果该信号集为空的话,sigsuspend就不屏蔽任何信号,任何信号都可以使进程从挂起状态唤醒,这就与pause函数一样了。
#include
int sigsuspend(const sigset_t *sigmask);
返回值:-1,并将errno设置为EINTR
可以用于保护代码临界区,使其不被特定的信号中断
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static void sig_init(int);
int main()
{
sigset_t newmask,oldmask,waitmask;
printf("hello");
if(signal(SIGINT,sig_init)==SIG_ERR)
perror("signal");
sigemptyset(&waitmask);
sigaddset(&waitmask,SIGUSR1);
sigemptyset(&newmask);
sigaddset(&newmask,SIGINT);
if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0) //将SIGINT加入到信号屏蔽字中,以oldmask返回
perror("sigprocmask");
printf("ok");
if(sigsuspend(&waitmask)!=-1) //将sigusr1屏蔽
perror("sigsuspend");
if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0) //将阻塞信号SIGINT恢复
perror("sigprocmask");
exit(0);
}
static void sig_init(int signo)
{
printf("how are you\n");
}