1、信号的种类:
每个信号名都以SIG开头,信号名的定义都在头文件<signal.h>中,使用命令查看所有信号 kill -l
Linux系统中有许多信号,其中前面31个信号都有一个特殊的名字,并且对应着一个特殊的事件,这些信号都是从Unix系统继承下来的,他们还有个名称叫不可靠信号
,特点如下:
- 1、非实时信号不排队,信号的响应会相互嵌套(其他的信号可以打断正在执行的信号)
- 2、如果目标进程没有及时响应非实时信号,那么随后到达的该信号会被丢弃
- 3、每一个非实时信号都对应一个事件,当事件发生时,将产生这个信号
- 4、如果进程的挂起信号中含有实时和非实时信号,那么进程优先响应实时信号并且是从大到小一次响应,而非实时信号没有固定的次序
后面的31个信号(从34到64)是Linux系统新增的实时信号,也被称为可靠信号,特点如下:
- 1、实时信号的响应次序按接受顺序排队,不嵌套
- 2、即使相同的实时信号被同时发送对此,也不会被丢弃,而会依次挨个响应
- 3、实时信号没有特殊的事件与之对应
对以上信号需要注意的是:
- 1、表中罗列出来的
值
,在X86、PowerPC和Arm平台下是有效的。但是其他的平台的值也许和这个表里面的值不一样。 - 2、
备注
中注明的事件发生时会产生相应的信号,但并不是说该信号的产生就一定发生了这个事件。事实上,任何进程都可以使用函数keill()来产生任何信号。 - 3、信号:
SIGKILL
和SIGSTOP
是两个特殊的信号,他们不能被忽略、阻塞或则捕捉,只能按照缺省的动作来响应信号。除了这两个信号的其他信号,接收信号的目标进程按照下面的顺序作出反应:-
A)、如果该信号被阻塞,那么将信号挂起,不对其做任何处理,等到为其做解除阻塞为止。否则进入
B)
-
B)、如果该信号被捕捉,判断捕捉的类型:否则进入
C)
- B1)、如果设置了响应函数,那么执行响应函数
- B2)、如果设置为丢弃,那么忽略该信号
-
C)、执行该信号的缺省动作
-
2、相关函数
kill函数-发送信号
signal函数-捕获信号
练习:父进程发送信号,子进程接收信号去执行响应函数
#include "stdio.h"
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void func(int a)
{
printf("响应函数-》捕捉信号:%d", a);
}
int main()
{
int pid = fork();
int i = 0;
if(pid > 0)
{
while(1)
{
printf("父进程%d\n", i++);
sleep(5);//每隔五秒,父进程向子进程发送4号命令
printf("执行4号命令 \n " );
kill(pid, 4);
}
}
else if(pid == 0)
{
signal(4, func);//子进程接收到4号命令之后开始执行func函数
while(1)
{
printf("子进程\n");
sleep(1);
}
}
else
{
perror("Pid");
}
}
raise函数
pause函数
练习:
尝试设置所有信号阻塞,然后一次性全部解除,留意处理的先后顺序
#include "stdio.h"
#include <signal.h>
#include "stdlib.h"
#include <unistd.h>
void func(int argc)
{
printf("响应函数:%d\n", argc);
}
int main(void)
{
//设置信号的响应函数
signal(3, func);
signal(4, func);
//初始化信号集
sigset_t set;
sigemptyset(&set);//将信号集情况
sigaddset(&set, 3);//将一个指定的信号添加到信号集中
sigaddset(&set, 4);//将一个指定的信号添加到信号集中
//设置阻塞信号集中的信号
sigprocmask(SIG_SETMASK, &set, NULL);
//给自己发送信号
raise(3);
raise(4);
sleep(2);//延时两秒解除阻塞
//解除阻塞
sigprocmask(SIG_UNBLOCK, &set, NULL);
return 0;
}
3、给进程发送信号,并携带额外的数据
额外携带的数必须是一个联合体
union sigval
{
int sigval_int;
void * sigval_prt
};
利用siqqueue()发送信号的同时可以携带一个整形数据或者一个void型指针,目标进程想要想要额外获得这些数据
以上的act参数比较复杂,其类型结构体struct sigaction如下:
stract sigaction
{
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
void (*sa_sigaction)(int, siginfo_t *, void *);函数
-
第一个参数:int类型,就是触发该函数的信号
-
第二个参数:siginfo_t型指针,指向如下结构体:
siginfo_t { int si_signo; // si_signo、si_errno 和 si_code 对所有信号都有效 int si_errno; int si_code; int si_trapno; // 以下成员只对部分情形有效,详情见下面的注解 pid_t si_pid; uid_t si_uid; int si_status; clock_t si_utime; clock_t si_stime; sigval_t si_value; int si_int; // 整型参数 void *si_ptr; // 指针参数 int si_overrun; int si_timerid; void *si_addr; long si_band; int si_fd; short si_addr_lsb; }
示例:
#include "stdio.h"
#include <signal.h>
#include <strings.h>
#include <sys/types.h>
#include <unistd.h>
void func(int sig, siginfo_t *info, void *arg)
{
printf("sig:%d, info:%s", sig, (char *)info->si_ptr);
}
int main(void)
{
//定义ACT结构体并设置其信息
struct sigaction act;
bzero(&act, sizeof(act));//清空结构体
act.sa_sigaction = func;
act.sa_flags |= SA_SIGINFO;
//设置捕获的信号响应函数
if(sigaction(3, &act, NULL))
{
printf("设置捕获失败\n");
return 0;
}
//设置携带参数
union sigval value;
value.sival_int = 1024;
value.sival_ptr = "Hello Even";
//发送信号
pid_t pid = getpid();
if(sigqueue(pid, 3, value))
{
printf("发送信号失败\n");
return 0;
}
printf("发送信号成功\n");
sleep(1);
return 0;
}
这段代码的作用是在当前进程内设置信号捕获函数,并向自己发送信号3(SIGQUIT),同时携带附加信息。当接收到信号后,会调用信号捕获函数 func 来处理,并输出信号编号和信号携带的信息。这样实现了进程内部的通信。