1.概念
1.信号是软件终端机制一种模拟,只是一种异步通讯的方式。
2.信号时可以直接用户空间进程跟内核进程进行交互,内核进程可以通过信号通知用户空间发生那些系统事件。
3.如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被 取消时才被传递给进程。
2.用户进程对信号的相应方式
1)忽略信号:对信号不做任何处理,但是有两个信号不能忽略(SIGKILL 9和SIGSTOP 17)
2)捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数。
3)执行取胜操作:Linux对每种信号都规定了默认操作
3.信号类型
SIGHUP 1 A 终端挂起或者控制进程终止
SIGINT 2 A 键盘中断(如break键被按下)
SIGQU99v 3 C 键盘的退出键被按下
SIGILL 4 C 非法指令
SIGABRT 6 C 由abort(3)发出的退出指令
SIGFPE 8 C 浮点异常
SIGKILL 9 AEF Kill信号
SIGSEGV 11 C 无效的内存引用
SIGPIPE 13 A 管道破裂: 写一个没有读端口的管道
SIGALRM 14 A 由alarm(2)发出的信号
SIGTERM 15 A 终止信号
SIGUSR1 30,10,16 A 用户自定义信号1
SIGUSR2 31,12,17 A 用户自定义信号2
SIGCHLD 20,17,18 B 子进程结束信号
SIGCONT 19,18,25 进程继续(曾被停止的进程)
SIGSTOP 17,19,23 DEF 终止进程
SIGTSTP 18,20,24 D 控制终端(tty)上按下停止键
SIGTTIN 21,21,26 D 后台进程企图从控制终端读
SIGTTOU 22,22,27 D 后台进程企图从控制终端写
4.相关函数
int kill(pid_t pid,int sig) | |||
功能 | 信号发送 | ||
参数 | 输入参数 | pid | 指定进程 |
sig | 要发送的信号 | ||
返回值 | int | 成功:0 失败:-1 |
int raise(int sig) | |||
功能 | 进程向自己发送信号 | ||
参数 | 输入参数 | sig | 信号 |
返回值 | int | 成功:0 失败:-1 |
unsigned int alarm(unsigned int seconds) | |||
功能 | 在进程中设置一个定时器 | ||
参数 | 输入参数 | seconds | 定时时间,单位为秒 |
返回值 | unsigned int | 如果调用此函数,进程中已经设置了闹钟时间,则返回上个闹钟时间的剩余时间,否则返回为0 | |
注意: | 一个进程只能有一个闹钟时间。如果再调用alarm时已设置过闹钟时间,则之前的闹钟时间被新值所替代 |
int pause(void) | |||
功能 | 用于将调用进程挂起直到收到信号为止 |
void(*signal(int signum,void (*handler)(int)))(int) | |||
功能 | 信号处理函数 | ||
参数 | 输入参数 | signum | 要处理的信号,不能处理SIGKILL和SIGSTOP |
handler | 自定义的信号处理函数指针 | ||
返回值 |
void abort(void) | |
功能 | 给自己发送异常终止信号(SIGABRT ) |
Demo
下面用demo举例
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
int main(void){
printf("%d\r\n",alarm(2));
sleep(2);
return 0;
}
运行结果是:
yates@yates-virtual-machine:~/test/code$ ./signal
0
Alarm clock
过两秒后,出现Alarm Clock 这句话。说明定时器设置成功
如果在第二次设置定时器呢,会打印什么呢?
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
int main(void){
printf("%d\r\n",alarm(7));
sleep(2);
while(1){
printf("%d\r\n",alarm(3));
sleep(7);
}
return 0;
}
yates@yates-virtual-machine:~/test/code$ ./signal
0
5
Alarm clock
第一次alarm返回值为0,说明第一次设置成功。第二次alarm返回值为5,说明剩余时间为5s
下面我们举个demo说明信号捕捉
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
void handler(int arg){
printf("handler :%d\r\n",arg);
// kill(getpid(),9);
}
int main(){
signal(SIGINT,handler);
signal(SIGQUIT,handler);
while(1);
return 0;
}
执行以上程序
yates@yates-virtual-machine:~/test/code$ ./signal
^\handler :3
^Chandler :2
^\handler :3
^Chandler :2
^\handler :3
^Chandler :2
^\handler :3
^Chandler :2
^\handler :3
^Chandler :2
^\handler :3
^Chandler :2
^\handler :3
^Chandler :2
^\handler :3
^Chandler :2
^\handler :3
^Chandler :2
当我们键盘输入 ctrl+c 时,返回值为3,如果输入ctrl+\,返回值为2,并且输入 ctrl+c 时,进程不会主动退出。
假如我们要忽略系统信号量呢,怎么办?请看以下demo
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
void handler(int arg){
printf("handler :%d\r\n",arg);
// kill(getpid(),9);
}
int main(){
signal(SIGINT,SIG_IGN);
signal(SIGQUIT,SIG_IGN);
while(1);
return 0;
}
执行以上程序,如果键盘输入 ctrl+c或者 ctrl+\ 时,进程不做处理
yates@yates-virtual-machine:~/test/code$ ./signal
^\^C^\^C^\^C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
void catch_signal(int sig)
{
switch(sig)
{
case SIGALRM:
printf("sigalrm have recv !\n");
alarm(2);
}
}
int main(int arg,char *args[])
{
//注册信号
if(signal(SIGALRM,catch_signal)==SIG_ERR)
{
perror("signal error");
return -1;
}
//一个alarm()函数只会发送一次信号
alarm(1);
while(1)
{
pause();
printf("accept !\n");
}
printf("game over!\n");
return 0;
}
以上函数执行结果是
yates@yates-virtual-machine:~/test/code$ ./signal
sigalrm have recv !
accept !
sigalrm have recv !
accept !
sigalrm have recv !
accept !
sigalrm have recv !
accept !
说明SIGALRM可以唤醒函数进程。验证了观点3:如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被 取消时才被传递给进程。