1.信号集和信号集处理函数
信号集是一个位向量,其中每一位对应着linux系统的一个信号。可使用如下函数对信号集进行处理:
1 |
#include <signal.h> |
2 |
3 |
int sigemptyset(sigset_t * set); |
4 |
5 |
int sigfillset(sigset_t * set); |
6 |
7 |
int sigaddset(sigset_t * set); |
8 |
9 |
int sigdelset(sigset_t * set); |
sigemptyset将一个信号集清空;sigfillset将信号集的所有位置位;sigaddset函数将参数signo指定的信号所对应的位设置为1;sigdelset将signo的对应位设置为0。
使用如下函数检测信号集的相应位是否被设置:
1 |
#include <signal.h> |
2 |
3 |
int sigismember(sigset_t * set, int aigno); |
返回值为1时,代表该位被设置,返回值为0时,代表该位未被设置,失败则返回-1。
//sigset.c 使用信号集处理函数测试并设置相应信号的位
01 |
#include <stdio.h> |
02 |
03 |
#include <signal.h> |
04 |
05 |
int main() |
06 |
07 |
{ |
08 |
09 |
sigset_t sig_set; |
10 |
11 |
sigemptyset(&sig_set); //清空信号集 |
12 |
13 |
sigaddset(&sig_set,SIGKILL-1); //设置SIGKILL的相应位 |
14 |
15 |
if (sigismember(&sig_set,SIGKILL))==1){ |
16 |
17 |
printf ( "SIGKILL has been set/n" ); |
18 |
19 |
else |
20 |
21 |
printf ( "can't set signal set/n" ); |
22 |
23 |
return 0; |
24 |
25 |
} |
2.屏蔽信号
阻塞一些信号,使进程即使接收到信号,也不做处理。
使用如下函数:
1 |
#include <signal.h> |
2 |
3 |
int sigprocmask( int how, const sigset_t *set,sigset_t *oldset); |
函数说明 sigprocmask()可以用来改变目前的信号屏蔽,其操作依参数how来决定
SIG_BLOCK 新的信号屏蔽由目前的信号屏蔽和参数set 指定的信号屏蔽作联集
SIG_UNBLOCK 将目前的信号屏蔽删除掉参数set指定的信号屏蔽
SIG_SETMASK 将目前的信号屏蔽设成参数set指定的信号屏蔽。
如果参数oldset不是NULL指针,那么目前的信号屏蔽会由此指针返回。
返回值 执行成功则返回0,如果有错误则返回-1。
错误代码 EFAULT 参数set,oldset指针地址无法存取。
EINTR 此调用被中断
如果set的值为NULL,则无论how是何值都不会更改信号屏蔽字。这种方法用于得到当前进程的信号屏蔽字。
sigprocmask(0,NULL,&oset);
01 |
#include <stdio.h> |
02 |
#include <stdlib.h> |
03 |
#include <signal.h> |
04 |
05 |
void sigusr1_handler( int signo) |
06 |
{ |
07 |
printf ( "catch SIGUSR1/n" ); |
08 |
} |
09 |
10 |
int main( void ) |
11 |
{ |
12 |
sigset_t set; |
13 |
14 |
if ( signal (SIGUSR1, sigusr1_handler) == SIG_ERR){ |
15 |
perror ( "can¡¯t set handler for SIGUSR1" ); |
16 |
exit (1); |
17 |
} |
18 |
19 |
sigemptyset(&set); //清空信号集 |
20 |
sigaddset(&set, SIGUSR1 - 1); //设置SIGUSR1 |
21 |
22 |
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1){ //屏蔽该信号 |
23 |
perror ( "fail to set signal-mask" ); |
24 |
exit (1); |
25 |
} |
26 |
27 |
printf ( "SIGUSR1 is not available/n" ); |
28 |
29 |
sleep(10); //休眠,等待用户发送SIGUSR1信号 |
30 |
31 |
if (sigprocmask(SIG_UNBLOCK, &set, NULL) == -1){ //恢复屏蔽的信号 |
32 |
perror ( "fail to set signal-mask" ); |
33 |
exit (1); |
34 |
} |
35 |
36 |
printf ( "SIGUSR1 is available now/n" ); |
37 |
38 |
sleep(10); //休眠,等待用户发送SIGUSR1信号 |
39 |
40 |
return 0; |
41 |
} |
3.处理未决信号
如果屏蔽了一个信号,但是进程还是从某处接收到了此信号,这种信号叫做未决的。这种信号是悬而未决的。
如果调用sigprocmask后有任何未决的但是已经不再阻塞的信号时,在该函数返回之前,至少会将这些解放了的未决信号中的一个发送给该进程。linux中使用sigpending函数检查未决信号,其函数的原型如下;
1 |
#include <signal.h> |
2 |
3 |
int sigpending(sigset_t * set); |
set表示当前进程中所有未决的信号。如果成功得到未决信号集,返回0,否则返回-1。
下例先阻塞SIGUSR1信号,之后向该进程发送SIGUSR1信号,此信号为未决的,使用sigpending函数测试是否有一个未决的SIGUSR1信号,最后取消对该信号的阻塞,处理这个信号。
01 |
#include <stdio.h> |
02 |
#include <stdlib.h> |
03 |
#include <signal.h> |
04 |
05 |
void sigusr1_handler( int signo) |
06 |
{ |
07 |
printf ( "catch SIGUSR1/n" ); |
08 |
} |
09 |
10 |
int main( void ) |
11 |
{ |
12 |
sigset_t set; |
13 |
sigset_t sig_pend; |
14 |
15 |
sigemptyset(&set); |
16 |
sigemptyset(&sig_pend); |
17 |
18 |
if ( signal (SIGUSR1, sigusr1_handler) == SIG_ERR){ |
19 |
perror ( "can¡¯t set handler for SIGUSR1" ); |
20 |
exit (1); |
21 |
} |
22 |
23 |
sigaddset(&set, SIGUSR1 - 1); //添加SIGUSR1信号 |
24 |
25 |
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1){ //阻塞SIGUSR1 |
26 |
perror ( "fail to set signal-mask" ); |
27 |
exit (1); |
28 |
} |
29 |
30 |
sleep(10); //休眠10,期间接收信号,注意进程休眠后不会被未决的信号唤醒 |
31 |
32 |
if (sigpending(&sig_pend) == -1){ //得到所有的未决信号集 |
33 |
perror ( "fail to get pending signal" ); |
34 |
exit (1); |
35 |
} |
36 |
37 |
if (sigismember(&sig_pend, SIGUSR1 - 1) == 1) //测试是否有SIGUSR1信号是未决的 |
38 |
printf ( "there is a signal, SIGUSR1, is pending/n" ); |
39 |
else { |
40 |
perror ( "fail to test signal-set" ); |
41 |
exit (1); |
42 |
} |
43 |
44 |
if (sigprocmask(SIG_UNBLOCK, &set, NULL) == -1){ //取消对SIGUSR1的阻塞 |
45 |
perror ( "fail to set signal-mask" ); |
46 |
exit (1); |
47 |
} |
48 |
49 |
printf ( "SIGUSR1 is available again/n" ); |
50 |
51 |
return 0; |
52 |
} |
4.高级信号处理函数
原型:
#include
定义函数 int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);
函数说明 sigaction()会依参数signum指定的信号编号来设置该信号的处理函数。参数signum可以指定SIGKILL和SIGSTOP以外的所有信号。
如参数结构sigaction定义如下
1 |
struct sigaction |
2 |
{ |
3 |
void (*sa_handler) ( int ); |
4 |
sigset_t sa_mask; |
5 |
int sa_flags; |
6 |
void (*sa_restorer) ( void ); |
7 |
} |
sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数,其他意义请参考signal()。
sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号搁置。
sa_restorer 此参数没有使用。
sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。
OR 运算(|)组合A_NOCLDSTOP : 如果参数signum为SIGCHLD,则当子进程暂停时并不会通知父进程SA_ONESHOT/SA_RESETHAND:当调用新的信号处理函数前,将此信号处理方式改为系统预设的方式。
SA_RESTART:被信号中断的系统调用会自行重启
SA_NOMASK/SA_NODEFER:在处理此信号未结束前不理会此信号的再次到来。如果参数oldact不是NULL指针,则原来的信号处理方式会由此结构sigaction 返回。
返回值 执行成功则返回0,如果有错误则返回-1。
错误代码 EINVAL 参数signum 不合法, 或是企图拦截SIGKILL/SIGSTOPSIGKILL信号
EFAULT 参数act,oldact指针地址无法存取。
EINTR 此调用被中断
5.SA_NOCLDWAIT选项
设置SA_NOCLDWAIT选项后,当信号为SIGCHILD时,则调用进程的子进程终止,立即释放系统资源。如果调用进程调用wait函数,则会导致该进程阻塞,知道其所有的子进程全部释放后wait函数返回-1,并将errno错误号设置为ECHILD,该选项可以用来避免僵尸进程的产生。
01 |
//nowait.c |
02 |
03 |
#include <stdio.h> |
04 |
#include <stdlib.h> |
05 |
#include <signal.h> |
06 |
#include <unistd.h> |
07 |
#include <errno.h> |
08 |
09 |
int main( void ) |
10 |
{ |
11 |
struct sigaction act; |
12 |
pid_t pid; |
13 |
14 |
act.sa_handler = SIG_DFL; |
15 |
act.sa_flags = SA_NOCLDWAIT; |
16 |
act.sa_sigaction = NULL; |
17 |
sigemptyset(&act.sa_mask); |
18 |
19 |
if (sigaction(SIGCHLD, &act, NULL) == -1){ |
20 |
perror ( "fail to set handler for SIGCHILD" ); |
21 |
exit (1); |
22 |
} |
23 |
24 |
pid = fork(); |
25 |
26 |
if (pid < 0){ |
27 |
perror ( "fail to fork" ); |
28 |
exit (1); |
29 |
} else if (pid == 0){ |
30 |
printf ( "the 1st child/n" ); |
31 |
exit (0); //第一个进程立即退出 |
32 |
} else { |
33 |
pid = fork(); |
34 |
35 |
if (pid < 0){ |
36 |
perror ( "fail to fork" ); |
37 |
exit (1); |
38 |
} else if (pid == 0){ |
39 |
printf ( "the 2nd child/n" ); |
40 |
sleep(5); //休眠5秒退出 |
41 |
exit (0); |
42 |
} else { |
43 |
if (wait(NULL) == -1) //调用wait函数,该函数必定出错返回 |
44 |
if ( errno == ECHILD) |
45 |
printf ( "all child quit, no child is zome/n" ); |
46 |
47 |
printf ( "the parent/n" ); |
48 |
} |
49 |
} |
50 |
51 |
return 0; |
52 |
} |
执行:
./nowait
the 1st child
the 2nd child
all child quit, no child is zome
the parent
6.SA_NODEFER选项
如果设置SA_NODEFER选项,当捕捉到该信号时该信号正在执行处理函数时,不阻塞该信号,除非sa_mask中指定阻塞该信号。
01 |
//nodefer.c |
02 |
03 |
#include <stdio.h> |
04 |
#include <stdlib.h> |
05 |
#include <signal.h> |
06 |
07 |
void sigusr1_handler( int signo) |
08 |
{ |
09 |
printf ( "catch SIGUSR1/n" ); |
10 |
11 |
sleep(5); //等待下一个SIGUSR1信号 |
12 |
13 |
printf ( "back to main/n" ); |
14 |
} |
15 |
16 |
int main( void ) |
17 |
{ |
18 |
struct sigaction act; |
19 |
20 |
act.sa_handler = sigusr1_handler; |
21 |
act.sa_flags = SA_NODEFER; |
22 |
act.sa_sigaction = NULL; |
23 |
sigemptyset(&act.sa_mask); |
24 |
25 |
if (sigaction(SIGUSR1,&act, NULL) == -1){ |
26 |
perror ( "fail to set handler for SIGCHILD" ); |
27 |
exit (1); |
28 |
} |
29 |
30 |
printf ( "process begin/n" ); |
31 |
32 |
sleep(10); //等待SIGUSR1信号 |
33 |
34 |
printf ( "done/n" ); |
35 |
36 |
return 0; |
37 |
} |
7.SA_RESETHAND选项
如果设置SA_RESETHAND选项,当信号处理返回后,该信号的处理函数乎恢复为默认的信号处理函数。
01 |
#include <stdio.h> |
02 |
#include <stdlib.h> |
03 |
#include <signal.h> |
04 |
05 |
void sigusr1_handler( int signo) |
06 |
{ |
07 |
printf ( "catch SIGUSR1/n" ); |
08 |
} |
09 |
10 |
int main( void ) |
11 |
{ |
12 |
struct sigaction act; |
13 |
14 |
act.sa_handler = sigusr1_handler; |
15 |
act.sa_flags = SA_RESETHAND; |
16 |
act.sa_sigaction = NULL; |
17 |
sigemptyset(&act.sa_mask); |
18 |
19 |
if (sigaction(SIGUSR1, &act, NULL) == -1){ |
20 |
perror ( "fail to set handler for SIGCHILD" ); |
21 |
exit (1); |
22 |
} |
23 |
24 |
printf ( "process begin/n" ); |
25 |
26 |
sleep(5); //等待第一个SIGUSR1 |
27 |
28 |
sleep(5); //等待第二个SIGUSR1 |
29 |
30 |
printf ( "done/n" ); |
31 |
32 |
return 0; |
33 |
from:http://www.cqihang.com/index.php/archives/398 |