信号
信号
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。
它可以在任何时候发给某一进程,而无需知道该进程的状态。
用户进程对信号的响应可以有 3 种方式:
-
忽略信号,即对信号不做任何处理,SIGKILL 及 SIGSTOP信号不能忽略。
-
捕捉信号,定义信号处理函数,当信号发生时,执行相应的处理函数。
-
执行缺省操作,Linux 对每种信号都规定了默认操作。
系统支持的信号列表。
xxx@xxx-pc:~/Documents$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
32号之前的为UNIX 系统中继承下来的信号,为不可靠信号。32之后的为扩充的可靠信号。
所有可靠信号都支持排队, 而不可靠信号则都不支持排队(若前一个信号还未注销又产生了相同的信号就会产生信号丢失)。
- 信号的发送和捕获
signal可以发送信号给进程或进程组。
SYNOPSIS
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
raise 函数允许进程向自身发送信号。
SYNOPSIS
#include <signal.h>
int raise(int sig);
实例:
SIGSTOP - 暂停进程信号
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
int ret;
if((pid = fork()) < 0){
perror("fork");
exit(1);
}
if(pid == 0){
raise(SIGSTOP);
exit(0);
}else{
printf("pid = %d\n",pid);
if((waitpid(pid,NULL,WNOHANG)) == 0){
if((ret = kill(pid,SIGKILL)) == 0){
printf("kill %d\n",pid);
}else{
perror("kill");
}
}
}
}
结果如下:
xxx@xxx-pc:~/Documents$ ./a.out
pid = 3288
kill 3288
- alarm()和pause()函数
alarm在进程中设置一个定时器,当定时器指定的时间到时,它就向进程发送 SIGALARM 信号。
要注意的是,一个进程只能有一个闹钟时间,如果在调用 alarm 之前已设置过闹钟时间,则任何以前的闹钟时间都被新值所代替。
SYNOPSIS
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
pause 函数是用于将调用进程挂起直至捕捉到信号为止。这个函数很常用,通常可以用于判断信号是否已到。
SYNOPSIS
#include <unistd.h>
int pause(void);
实例:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int ret;
alarm(10);
sleep(2);
ret = alarm(1);
printf("alarm value %d\n", ret);
pause();
printf("I have been waken up %d\n",ret);
return 0;
}
结果如下:
如果调用此alarm()前,进程已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。
alarm信号的默认处理是终止进程。
xxx@xxx-pc:~/Documents$ ./a.out
alarm value 8
Alarm clock
信号的处理
建立信号与进程之间的对应关系,这就是对信号的处理。
signal()函数
SYNOPSIS
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
handler: SIG_IGN - 忽略该信号; SIG_DFL - 采用系统默认方式处理信号; 自定义的信号处理函数指针。
实例:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
void my_func(int sign_no)
{
if(sign_no==SIGINT)
printf("I have get SIGINT\n");
else if(sign_no==SIGQUIT)
printf("I have get SIGQUIT\n");
}
int main()
{
printf("Waiting for signal SIGINT or SIGQUIT \n ");
signal(SIGINT, my_func);
signal(SIGQUIT, my_func);
pause();
exit(0);
}
结果如下:
xxx@xxx-pc:~/Documents$ ./a.out
Waiting for signal SIGINT or SIGQUIT
^C I have get SIGINT
xxx@xxx-pc:~/Documents$ ./a.out
Waiting for signal SIGINT or SIGQUIT
^\ I have get SIGQUIT
信号集函数组
- 创建信号集合
SYNOPSIS
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
- 登记信号处理器
主要用于决定进程如何处理信号。
首先使用 sigprocmask 函数判断检测或更改信号屏蔽字,
然后使用 sigaction 函数用于改变进程接收到特定信号之后的行为。
SYNOPSIS
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
SYNOPSIS
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
实例:
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
/* 自定义的信号处理函数 */
void my_func(int signum)
{
printf("If you want to quit,please try SIGQUIT\n");
}
int main()
{
sigset_t set,pendset;
struct sigaction action1,action2;
/* 初始化信号集为空 */
if(sigemptyset(&set) < 0)
perror("sigemptyset");
/* 将相应的信号加入信号集 */
if(sigaddset(&set,SIGQUIT) < 0)
perror("sigaddset");
if(sigaddset(&set,SIGINT) < 0)
perror("sigaddset");
/* 设置信号集屏蔽字为阻塞,信号被阻塞 */
if(sigprocmask(SIG_BLOCK,&set,NULL) < 0)
perror("sigprocmask");
else{
printf("blocked\n");
sleep(5);
}
/* 设置信号集屏蔽字为非阻塞 */
if(sigprocmask(SIG_UNBLOCK,&set,NULL) < 0)
perror("sigprocmask");
else
printf("unblock\n");
/* 对相应的信号进行循环处理 */
while(1){
if(sigismember(&set,SIGINT)){
sigemptyset(&action1.sa_mask);
/* 信号处理函数 */
action1.sa_handler = my_func;
sigaction(SIGINT,&action1,NULL);
}else if(sigismember(&set,SIGQUIT)){
sigemptyset(&action2.sa_mask);
/* 对信号默认操作 */
action2.sa_handler = SIG_DFL;
sigaction(SIGTERM,&action2,NULL);
}
}
}
结果如下:
xxx@xxx-pc:~/Documents$ ./a.out
blocked
unblock
^CIf you want to quit,please try SIGQUIT
^\Quit (core dumped)