您现在的位置是:首页 >技术教程 >信号发送与处理网站首页技术教程
信号发送与处理
问题
按下 CTRL + C之后,命令行中的前台进程会被终止。为什么???
什么是信号?
信号是一种“软件中断”,用来处理异步事件
- 内核发送信号到某个进程,通知进程事件的发生
- 事件可能来自硬件,可能来自用户输入,可能来自除零错误
信号是一种类型的进程间通信方式(一个进程向另外一个进程发送信号)
- A进程发送事件T,向B进程发送信号,B进程执行动作响应事件
- 进程可以对接收的不同信号进行不同动作响应(信号 -> 处理)
信号的分类
硬件异常
内核检测到硬件错误,发送相应信号给相关进程
终端信号(用户交互信号,最典型:ctrl+c)
在终端输入 “特殊字符” 等价于向前台进程组发送信号
软件信号
在软件层面(进程代码中)触发的信号(发送给自身或其他进程)
硬件异常信号
信号 | 值 | 说明 |
SIGBUS | 7 | 总线错误,进程发生了内存访问错误 |
SIGFPE | 8 | 算术错误,FPE表示浮点异常 |
SIGILL | 9 | 指令错误,进程尝试执行非法指令 |
SIGSEGV | 11 | 段错误,进程访问了非法内存区域 |
终端相关信号
- SIGINT(Ctrl + c) :程序终止信号,用于通知前台进程组终止进程
- SIGQUIT(Ctrl + ) :与SIGINT类似,进程收到该信号退出时可产生 coredump 文件
- SIGTSTP(Ctrl + Z) :停止进程的运行,进程收到该信号后可以选择处理或忽略。Ctrl + Z :进程收到该信号后停止运行(状态发生转换),后续可以恢复运行状态。
软件相关信号
- 子进程退出:父进程收到 SIGCHLD 信号
- 父进程退出:子进程可能收到信号(什么信号?)
- 定时器到期:alarm(),ualarm(),timer_create(),...
- 主动发信号:kill(),raise(),...
内核与信号
1.内核检测到相关的错误,将信号发送给对应的进程。
2.进程可以通过系统调用,告诉内核,自己需要信号。比如:时钟信号。比如进程A告诉内核,我需要时钟信号,那么内核就定期给进程A发送时钟信号。
3.进程间通信:进程A通过内核 将信号 发送给进程B
信号的发送与处理,无非就是以上三种情况。
信号的默认处理
默认处理方式 | 说明 | 示例 |
ignore | 进程丢弃信号不会产生任何影响 | SIGCHLD SIGURG |
terminate | 终止进程 | SIGKILL SIGHUP |
coredump | 终止进程并产生转储文件 | SIGQUIT SIGILL |
stop/continue | 停止进程执行/恢复进程执行 | SIGSTOP,SIGCONT |
知识加油站:System V vs BSD
System V :也被称为 AT&T SystemV,是Unix操作系统众多版本中的一支
BSD :加州大学巴克利分校开创,Unix衍生系统,代表由此派生出的各种套件集合
Linux之所以被称为类Unix操作系统(Unix Like),部分原因就是Linux的操作风格是介于上述二者之间,且不同的厂商为了照顾不同的用户,其发行版本的操作风格有存在差异。
自定义信号发送
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid,int sig);
//pid > 0 ==> 发送信号给进程 ,pid == 0 ==> 发送信号给进程组 ,pid < -1 比如-1000,将信号发送给PGID为-1000的进程组
int raise(int sig); //信号处理完毕才返回
notes:
标准信号是unix系统中的信号,编号范围从1到31。
实时信号是Linux独有的信号,编号范围从32到64。
下面来看几个demo示例。
signal
可以看到在终端按下 CTRL + C 之后,信号处理函数 signal_handler就不断地调用。
sysv_signal
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int sig)
{
printf("handler : sig = %d
",sig);
}
int main(void)
{
//signal(SIGINT,signal_handler);
sysv_signal(SIGINT,signal_handler);
while(1)
{
sleep(1);
}
return 0;
}
zhaixue@ubuntu:~/DTLinux/0-11$ gcc main.c -o main.out
zhaixue@ubuntu:~/DTLinux/0-11$ ./main.out
^Chandler : sig = 2
^C
zhaixue@ubuntu:~/DTLinux/0-11$
bsd_signal
//#define _GNU_SOURCE
#define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200800L
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int sig)
{
printf("handler : sig = %d
",sig);
}
int main(void)
{
//signal(SIGINT,signal_handler);
//sysv_signal(SIGINT,signal_handler);
bsd_signal(SIGINT,signal_handler);
while(1)
{
sleep(1);
}
return 0;
}
zhaixue@ubuntu:~/DTLinux/0-11$ gcc main.c -o main.out
zhaixue@ubuntu:~/DTLinux/0-11$ ./main.out
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Quit (core dumped)
zhaixue@ubuntu:~/DTLinux/0-11$
发送自定义信号
//未完待续...
思考
以上signal、sysv_signal、bsd_signal 三种自定义信号处理函数有什么区别?