信号在内核中的三种状态:
阻塞、未决、递达(这里也可以用handler表示处理),如下图:
递达:执行信号处理动作称为信号的递达(delivery)
未决:从信号的产生到信号的递达之间的状态称为未决(peeding)
阻塞:进程可以阻塞(block)信号,这时信号处在未决状态,直到进程接触对信号的阻塞,才执行递达动作
修改block的值:
sigprocmask(int how,const sigset_t *set,sigset_t *old);
how: 如何将信号集设置入内核
SIG_BLOCK:在内核原本mask的基础上将set的mask加入
SIG_UNBLOCK:在内核原本mask的基础上去掉set中的mask
SIG_SETMASK:直接等于set,不考了原本内核中的值
sigset_t :block 对外提供的接口
int sigemptyset():清空信号集
int sigfillset():把信号集置1
int sigaddset():将指定某个比特位置为1,也就是将某一个信号加入信号集
int sigdelset():将指定某个比特位清零,也就时将这个信号量从信号集中删除
int sigismember():判断一个信号量在不在这个信号集中
将先前信号量集的值通过old返回
这几个函数都是在用户空间倒腾,只有sigprocmask()是映射到内核中的
未决状态的获取:
peeding状态只能获取不能设置!
获取内核的未决状态集:sigpending(sigset_t *set)
阻塞与未决的具体使用:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<string.h>
4 #include<stdlib.h>
5 #include<signal.h>
6 void handler(int s)
7 {
8 printf("%d recv\n",s);
9 }
10 void handler_quit(int s)
11 {
12 sigset_t set;
13 sigemptyset(&set);
14 sigaddset(&set,SIGINT);
15 sigprocmask(SIG_UNBLOCK,&set,NULL);
16 }
17 int main()
18 {
19 signal(SIGINT,handler);
20 signal(SIGQUIT,handler_quit);
21 sigset_t set;
22 sigset_t pset;
23 sigemptyset(&set);
24 sigaddset(&set,SIGINT);
25 sigprocmask(SIG_BLOCK,&set,NULL);
26 for(;;)
27 {
28 sleep(1);
29 sigemptyset(&pset);
30 sigpending(&pset);
31 int i=1;
32 for( i=1;i<=_NSIG;i++)
33 {
34 if(sigismember(&pset,i))
35 {
36 printf("1");
37 }
38 else
39 {
40 printf("0");
41 }
42 }
43 }
44 }
信号忽略是信号递达后处理的一个动作,信号屏蔽是信号没有递达
信号处理函数中的设置,只在信号处理函数中有效
竞态 (pause):
int pause(void)
实现暂停功能,将当前进程挂起,转存储调度,直到有信号递达才能唤醒该进程
让操作系统调度起别的进程运行
用pause模拟实现sleep函数:
首先我们要知道:sleep是可中断的,返回值为距离设定设定时间还差多少秒,如果我们设定的是sleep(5),我们在2秒时发送一个信号,那么返回值就为3
使用alarm(0)是避免出现其他信号打断时alarm再次出现又将其打断。
这里为了避免由于时间片的切换导致信号无法正常唤醒pause,我们可以引入带信号屏蔽版的pause
int sigsuspend(const sigset_t *mask);
1.sigsuspend=pause+信号屏蔽
2.sigsuspend等信号处理函数执行完后返回
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<signal.h>
4 void handler(int s)
5 {
6 printf("signal arrive");
7 }
8 void handler2(int s)
9 {
10
11 }
12 int mysleep(int sec)
13 {
14 signal(SIGALRM,handler2);
15 sigset_t set,old,zset;
16 sigemptyset(&set);
17 sigemptyset(&old);
18 sigemptyset(&zset);
19 sigaddset(&set,SIGALRM);
20 sigprocmask(SIG_BLOCK,&set,&old);
21 alarm(sec);
22 sigsuspend(&zset);
23 sigprocmask(SIG_UNBLOCK,&set,&old);
24 int n=alarm(0);
25 return n;
26 }
27 int main()
28 {
29 signal(SIGINT,handler);
30 for(;;)
31 {
32 printf(".");
33 fflush(stdout);
34 int n=mysleep(3);
35 printf("%d\n",n);
36 }
37
38 }
注册信号升级版:
int sigaction(int signo,const struct sigaction*set,struct sigaction *old);
signo:捕获的信号
set:设置信号处理函数
返回值为old
struct sigaction{
void (*sa_handler)(int);//信号的处理函数
sigset_t sa_mask;//在信号处理函数执行期间的信号屏蔽,是一个集合,使用时要用sigemptyset清空
int sa_flags; //永远填0
}
在本信号执行期间会屏蔽掉再一次到来的本信号,别的信号不受影响,若要屏蔽其他信号可以加载mask中。
具体用例:
1 #include<stdio.h>
2 #include<signal.h>
3 #include<unistd.h>
4 void handler(int s)
5 {
6 printf("recv %d\n",s);
7 }
8 int main()
9 {
10 struct sigaction act;
11 act.sa_handler=handler;
12 sigemptyset(&act.sa_mask);
13 act.sa_flags=0;
14 sigaction(SIGINT,&act,NULL);
15 for(;;)
16 {
17 pause();
18 }
19 }