1、信号在内核中的表示
执行信号的处理动作称为信号递达。信号从产生到递达状态之间的状态成为未决(pending)。
进程可以选择阻塞某个信号。被阻塞的信号产生时将保持在未决状态。直到进程解除对此信号的阻塞,才可以执行递达动作。
如下图,进程任务结构(PCB)中包含:信号屏蔽字64位,linux中用sigset_t类型表示(block 1,信号掩码);
未决状态字(pending 1)...阻塞信号的话,到达就会处于未决。
如图,SIGINT信号,发送给进程,进程信号屏蔽字屏蔽信号,那么其被阻塞,也处于未决状态。当将信号屏蔽改为0,信号就会递达,进而执行信号处理函数。
信号集操作函数,下面这5个函数只是会改变信号集中的信号屏蔽字,但是并不会改变进程的信号屏蔽字。
1、int sigemptyset(sigset_t *set);//清空
2、int sigfillset(sigset_t *set);//置1
3、int sigaddset(sigset_t *set,int signo);
4、int sigdelset(sigset_t *set,int signo);
5、int sigismember(const sigset_t *set,int signo);
sigprocmask函数可以获取或者改变进程中的信号屏蔽字。
int sigprocmask(int how,const sigset_t *set,sigset_t *oset);
功能:读取或者更改进程的信号屏蔽字。成功返回0,出错返回-1.
如果oset是非空指针,则读取进程的当前的信号屏蔽字,通过oset参数传出。如果set是非空指针,则将进程信号屏蔽字改为set.参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前信号屏蔽字为mask,
那么how参数含义:
SIG_BLOCK:set包含了我们希望添加到当前信号屏蔽字的信号,相当于mask=mask|set;
SIG_UNBLOCK:set包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask=mask&~set;
SIG_SETMASK:设置当前信号屏蔽字为set所指向的值,相当于mask=set.
下面的例子说明信号从产生到递达的过程:
1 #include<unistd.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<fcntl.h> 5 #include<stdlib.h> 6 #include<stdio.h> 7 #include<errno.h> 8 #include<string.h> 9 10 #include<signal.h> 11 #define ERR_EXIT(m) 12 do 13 { 14 perror(m); 15 exit(EXIT_FAILURE); 16 }while(0) //宏要求一条语句 17 void handler(int sig); 18 void printsigset(sigset_t *set) 19 { 20 int i;//NSIG表示信号最大值,64 21 for(i=1;i<NSIG;i++){ 22 if(sigismember(set,i)) 23 putchar('1'); 24 else putchar('0'); 25 } 26 printf(" "); 27 } 28 int main(int argc,char*argv[]) 29 { 30 sigset_t pset; 31 sigset_t bset; 32 sigemptyset(&bset);//sigset_t 用之前必须置0或置1 33 if(signal(SIGINT,handler)==SIG_ERR) 34 ERR_EXIT("signal error"); 35 // if(signal(SIGQUIT,handler)==SIG_ERR) 36 // ERR_EXIT("signal error");//再安装一个信号,按下ctrl+解除SIGINT阻塞,信号递达(信号处理函数) 37 sigaddset(&bset,SIGINT);//SIGINT加入信号集。 38 sigprocmask(SIG_BLOCK,&bset,NULL);//对bset操作,改变信号屏蔽字,阻塞SIGIN信号。按下ctrl+c也会被阻塞。这个是不可靠信号,只会阻塞一个信号,不会排队。 39 for(;;) 40 { 41 sigpending(&pset);//获取未决信号,没有阻塞则没有未决
/*
sigpending函数返回信号集,其中的各个信号对于调用进程是阻塞的而不能递送,因而也一定是当前未决的。该信号集通过set参数返回。(这些信号是已经产生的信号,
但因为信号屏蔽字中对其设置了屏蔽位,从而被阻塞,不能递送给进程的那些信号。注意sigpending返回的信号集与信号屏蔽字的区别。从集合角度来讲,此信号集是当前信号屏蔽字的子集。)
*/
42 sleep(1); 43 printsigset(&pset); 44 sleep(1); 45 } 46 sleep(1); 47 return 0; 48 } 49 50 void handler(int sig)//sig是signum 51 { 52 if(sig==SIGINT) 53 printf("receive a sig %d ",sig); 54 /* else if(sig==SIGQUIT) 55 { 56 sigset_t uset; 57 sigemptyset(&uset); 58 sigaddset(&uset,SIGINT); 59 sigprocmask(SIG_UNBLOCK,&uset,NULL);//解除uset中信号的阻塞 60 } 61 */ 62 }