Linux —— 进程信号
一、信号的基本概念
1. 什么是信号
信号是Linux系统为了响应某种状况而产生的事件。进程收到信号后应该采取相应的动作。
信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称作软中断。从它的命名可以看出,它的实质和使用很象中断。所以,信号可以说是进程控制的一部分
2. 哪些情况会产生信号?
- 键盘事件
- 非法内存
- 硬件故障
- 环境切换
3. 如何查看信号
- 查看系统中的信号:
kill -l
; - 查看信号的默认处理:
man 7 signal
; - 有的信号有多个值,对应不同系统(中间那个值对应Linux系统)
4. 进程收到信号的三种处理方式:
- 默认:执⾏行该信号的默认处理动作。
- 忽略:信号来了不处理,相当于丢掉信号(
SIGKILL
和SIGSTOP
不能被忽略) - 捕获并处理:提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个函数(
SIGKILL
和SIGSTOP
不能被捕获)。
5. 信号的分类
- 不可靠信号 (1-31号信号不可靠)
Linux的信号继承自早期的UNIX信号,UNIX信号有一定的缺陷:
1. 信号处理函数执行完毕,信号恢复成默认处理方式(Linux已改进)
2. 会出现信号丢失,信号不排队
可靠信号(34-64号信号可靠)
不会出现信号丢失,支持排队,信号处理函数执行完后不会恢复成缺省处理方式。实时信号:就是可靠信号
- 非实时信号:就是不可靠信号
可靠信号和不可靠信号的区别:
这里的不可靠主要是不支持信号队列,就是当多个信号发生在进程中的时候(收到信号的速度超过进程处理的速度的时候),这些没来的及处理的信号就会被丢掉,仅仅留下一个信号。
可靠信号是多个信号发送到进程的时候(收到信号的速度超过进程处理信号的速度的时候),这些没来的及处理的信号就会排入进程的队列。等进程有机会来处理的时候,依次再处理,信号不丢失。
二、操作信号
1. 注册信号 —— signal()函数
注册信号实际是对信号进行三种处理操作,用于告诉当前进程接收信号后该去执行什么动作。
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
函数功能:开始获取信号值为signum的信号,如果获取到该信号,则开始执行handler指向的函数。
第一个参数signum
:指明了所要处理的信号类型,它可以取除了SIGKILL
和SIGSTOP
外的任何一种信号。
第二个参数handler
:描述了与信号关联的动作,它可以取以下三种值:
SIG_IGN
:表示忽略该信号。#define SIG_IGN ((sighandler_t)1)
SIG_DFL
:表示恢复对信号的系统默认处理。不写此处理函数默认也是执行系统默认操作。#define SIG_IGN ((sighandler_t)0)
sighandler_t
类型的函数指针:执行自己写的代码。
返回值:
成功返回原本的信号处理函数指针;
失败返回SIGERR
; SIGERR
的宏为 #define SIG_IGN ((sighandler_t)-1)
代码示例:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void handler(int n)
{
printf("叫你装逼, 这下死了吧!\n");
exit(0);
}
int main()
{
signal(SIGINT, SIG_IGN);
//SIGINT代表 ctrl+c 发起的信号,SIG_IGN表示忽略收到的信号
signal(SIGQUIT, handler);
//SIGQUIT代表 ctrl+\ 发起的信号,收到这个信号后转去执行handler函数。
int i = 0;
for(; ;)
{
printf("哈哈,死不了!\n");
sleep(1);
}
}
2. 给进程发送信号 —— kill()函数
1.使用指令发送:kill -[信号值] [pid]
,
例如使用kill -SIGSEGV [pid]
杀死进程:
[tian@localhost 10-信号]$ ps -ef | grep a.out
tian 10538 9053 0 00:32 pts/7 00:00:00 ./a.out
tian 10541 10307 0 00:33 pts/6 00:00:00 grep a.out
[tian@localhost 10-信号]$ kill -SIGSEGV 10538
[tian@localhost 10-信号]$ ps -ef | grep a.out
tian 10546 10307 0 00:33 pts/6 00:00:00 grep a.out
2.使用函数发送——kill()
#include <sys/types.h>
#include <signal.h>
int kill(int pid, int signum)
函数功能:
用于向任何进程组或进程发送信号。
第一个参数pid
:
pid > 0:将信号发送给进程id为pid的进程;
pid = 0:将信号发送给调用者所在进程组中所有进程;
pid = -1:将信号发送给所有进程,除了1号进程;
pid < -1:将信号发送给进程组id为|pid|的进程组中所有进程
第二个参数signum
:
准备发送的信号的信号值,即信号编号。假如其值为零则没有任何信号送出,但是系统会执行错误检查,通常会利用sig值为零来检验某个进程是否仍在执行。
返回值:
成功执行时,返回0。失败返回-1
代码示例:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void handler(int n)
{
printf("收到,over!\n");
}
int main()
{
signal(SIGUSR1, handler); //收到SIGUSR1后,转去执行handler函数
pid_t pid = fork();
if(pid == 0){ //子进程
kill(getppid(), SIGUSR1);//在子进程中向父进程发送一个SIGUSR1信号
exit(0);
}else{ //父进程
while(1){
printf("收到请回话!\n");
fflush(stdout);
sleep(1);
}
}
}
测试结果:
[tian@localhost 10-信号]$ ./a.out
收到请回话!
收到,over!
收到请回话!
收到请回话!
收到请回话!
收到请回话!
^C
[tian@localhost 10-信号]$
3. 给本进程发信号——raise()函数
#include <signal.h>
int raise(int sig);
返回值:成功返回0,失败返回非0;
这个函数也可以用kill()
函数实现: kill(getpid(), sig)
4. 给进程组发信号 —— killpg()函数
#include <signal.h>
int killpg(int pgrp, int sig);
返回值:成功返回0,失败返回-1;
三、常用的SIGALRM信号
1. 闹钟信号 —— alarm()函数
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
所需头文件 | #include <unistd.h> |
---|---|
函数原型 | unsigned int alarm(unsigned int seconds); |
参数seconds
|
执行秒数,系统经过second 秒后向该进程发送SIGALRM 信号 |
函数返回值 | 成功:如调用此alarm() 函数前,进程中已设置了闹钟,则返回上个闹钟的剩余时间,否则返回0。 失败:-1 |
可以在进程中设置一个定时器,等到时间到达时,就会给进程发送SIGALARM信号,注意的是一个进程只能有一个闹钟时间,如果调用alarm()
之前已经设置了闹钟时间,那么任何以前的闹钟时间都会被新值所代替。如果second
设为0,表示清除闹钟。
代码示例:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void handler(int n)
{
printf("\n超时,退出!\n");
exit(1);
}
int main()
{
char buff[100];
printf("your name: ");
signal(SIGALRM, handler);
alarm(3); //设置闹钟,3s后发送信号
scanf("%s", &buff);
alarm(0); //清除闹钟
printf("get name: %s\n", buff);
for(; ;)
{
fflush(stdout);
printf("……");
sleep(1);
}
}
测试结果:
[tian@localhost 10-信号]$ ./a.out
your name:
超时,退出!
[tian@localhost 10-信号]$ ./a.out
your name: 张三
get name: 张三
…………………………………………^C
[tian@localhost 10-信号]$
附加1(重要信号的含义):
SIGHUP :当用户退出Shell时,由该Shell启的发所有进程都退接收到这个信号,默认动作为终止进程。
SIGINT :用户按下组合键(ctrl + c)时,用户端时向正在运行中的由该终端启动的程序发出此信号。默认动作为终止进程。
SIGQUIT :当用户按下组合键(ctrl + \)时产生该信号,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止进程并产生core文件。
SIGILL :CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件。
SIGTRAP:该信号由断点指令或其他trap指令产生。默认动作为终止进程并产生core文件。
SIGABRT :调用abort函数时产生该信号。默认动作为终止进程并产生core文件。
SIGBUS:非法访问内存地址,包括内存地址对齐(alignment)出错,默认动作为终止进程并产生core文件。
SIGFPE:在发生致命的算术错误时产生。不仅包括浮点运行错误,还包括溢出及除数为0等所有的算术错误。默认动作为终止进程并产生core文件。
SIGKILL :无条件终止进程。本信号不能被忽略、处理和阻塞。默认动作为终止进程。它向系统管理员提供了一种可以杀死任何进程的方法。
SIGUSR1:用户定义的信号,即程序可以在程序中定义并使用该信号。默认动作为终止进程。
SIGSEGV:指示进程进行了无效的内存访问。默认动作为终止进程并使用该信号。默认动作为终止进程。
SIGUSR2:这是另外一个用户定义信号,程序员可以在程序中定义并使用该信号。默认动作为终止进程。
SIGPIPE :Broken pipe:向一个没有读端的管道写数据。默认动作为终止进程。
SIGALRM :定时器超时,超时的时间由系统调用alarm设置。默认动作为终止进程。
SIGTERM :程序结束(terminate)信号,与SIGKILL不同的是,该信号可以被阻塞和处理。通常用来要求程序正常退出。执行Shell命令kill时,缺少产生这个信号。默认动作为终止进程。
SIGCHLD :子程序结束时,父进程会收到这个信号。默认动作为忽略该信号。
SIGCONT:让一个暂停的进程继续执行。
SIGSTOP:停止(stopped)进程的执行。注意它和SIGTERM以及SIGINT的区别:该进程还未结束,只是暂停执行。本信号不能被忽略、处理和阻塞。默认作为暂停进程。
SIGTSTP:停止进程的动作,但该信号可以被处理和忽略。按下组合键时发出该信号。默认动作为暂停进程。
SIGTTIN:当后台进程要从用户终端读数据时,该终端中的所有进程会收到SIGTTIN信号。默认动作为暂停进程。
SIGTTOU:该信号类似于SIGTIN,在后台进程要向终端输出数据时产生。默认动作为暂停进程。
SIGURG :套接字(socket)上有紧急数据时,向当前正在运行的进程发出此信号,报告有紧急数据到达。默认动作为忽略该信号。
SIGXCPU:进程执行时间超过了分配给该进程的CPU时间,系统产生该信号并发送给该进程。默认动作为终止进程。
SIGXFSZ:超过文件最大长度的限制。默认动作为yl终止进程并产生core文件。
SIGVTALRM:虚拟时钟超时时产生该信号。类似于SIGALRM,但是它只计算该进程占有用的CPU时间。默认动作为终止进程。
SIGPROF:类似于SIGVTALRM,它不仅包括该进程占用的CPU时间还抱括执行系统调用的时间。默认动作为终止进程。
SIGWINCH:窗口大小改变时发出。默认动作为忽略该信号。
SIGIO:此信号向进程指示发出一个异步IO事件。默认动作为忽略。
SIGPWR:关机。默认动作为终止进程。
上一篇: nginx03 Nginx信号控制