Linux信号
- 00.什么是信号?
1.信号顾名思义,就是平时用来传递信息的一种手段,如小明的妈妈:“小明回家吃饭了!”,其实这就是一种信号,当小明听到了这句话的时候,就知道要回家吃饭了(要不然估计又是一顿)。
2.正如上面所说Linux信号也是一样的,发送信号给某个进程,当这个进程收到了信号,那么收到这个信号的进程就会按照该信号的处理方式去做出相应的处理动作。
3.结合上篇博客,可以知道,信号也是进程间通信的一种手段。
- 01.Linux下有哪些信号?
1.用kill -l 来查看linux下所有信号
![]()
2.通过man -7 signal 来查看对这些信号的描述和处理动作
- 02.Linux信号产生方式和信号处理方式
-1.如图
![]()
1.键入信号:
ctrl -\
![]()
解释说明:其中core dump文件是记录执行该信号处理函数前一个程序的状态,通常们可以通过ulimit -a 来查看当前对core dump定义,一般情况下,core dump文件大小为0,也就是不生成core dump文件,当时可以通过参数进行修改。如图
![]()
此时运行一个死循环程序就会产生一个core dump文件,
1.1.上述涉及到信号在PCB(内核)中的表示,所以请看下图
![]()
其实就是上图所示的一样,每个进程对信号都一个自己独立的处理方式,而对信号的处理方式都在PCB中的某个字段上,其中有二个”位图”(sigset_t结构信号集),记录着该进程对信号的阻塞,未决,还有一个函数指针用来指向信号处理函数。其中特别值得注意的是,忽略和阻塞是不一样的,阻塞是不会递答的,而忽略则是一种处理信号的方式,即在抵达之后,在handler中找到处理方式。
2.调用系统函数发送型号给进程
2.1系统API是kill 命令
![]()
2.2看一下实例:通过用kill 发送SIGINT指令使其死循环终止进程。
![]()
3.由软件产生的信号
(1)前面博客讲到了管道,当一个管道的读端关闭在尝试去写,那么会产生一个SIGPIPE信号。
(2)而在一个子进程结束后,会向父进程发送SIGCHLD信号
(3)alarm函数,即闹钟函数,在其设置时间到达时,会尝试发送以SIGALRM信号,默认终止程序。
![]()
示例代码:
int main()
{
std::cout<<"设置一个3s的闹钟!"<<std::endl;
alarm(3);
for(;;)
{
std::cout<<"程序在运行中 !"<<std::endl;
sleep(1);
}
}
//程序执行结果
[skin@bogon Git]$ ./a.out
设置一个3s的闹钟!
程序在运行中 !
程序在运行中 !
程序在运行中 !
闹钟
[skin@bogon Git]$
- 03.信号的设置
- -
1.信号集设置的API接口及其描述
![]()
2.纸上得来终觉浅,写个代码就全明白了
5 void PrintSigSet(sigset_t set)
6 {
7 //打印1-31号信号
8 std::cout<<"阻塞集 :\n";
9 for(size_t i=1;i<32;++i)
10 {
11 if(sigismember(&set,i))
12 std::cout<<"1 ";
13 else
14 std::cout<<"0 ";
15 }
16 std::cout<<std::endl;
17
18
19 std::cout<<"未决集 :\n";
20 sigpending(&set);
21 for(size_t i=1;i<32;++i)
22 {
23 if(sigismember(&set,i))
24 std::cout<<"1 ";
25 else
26 std::cout<<"0 ";
27 }
28 std::cout<<std::endl;
29 std::cout<<std::endl;
30
31 }
32
33 int main()
34 {
35 //0.定义
36 sigset_t newset,oldset;
37 //1.初始话
38 sigemptyset(&newset);
39 //2.将SIGINT信号添加到set中
40 sigaddset(&newset,SIGINT);
41 //3.设置SIGINT到屏蔽字中
42 sigprocmask(SIG_BLOCK,&newset,&oldset);
43 //4.测试ctrl-c是否被阻塞
44 while(1)
45 {
46 PrintSigSet(newset);
47 sleep(1);
48 }
49
50 return 0;
51 }
//程序执行结果
- 04.信号的捕捉
1.如图
![]()
2.信号捕捉API
1.sigaction函数是用来处理实时信号的,可以修改信号的处理函数,从而达到捕捉的效果。
2.其中signum表示处理信号的编号,act表示你想对信号怎样处理,即其信号处理函数的定义,oldact则表示的是原有的信号处理动作,可以用它来进行原来信号处理的行为,如果为NULL则表示不关心其动作。
3.sigaction结构中的sa_handler字段用来表示处理动作,sa_mask用来说明在处理此信号是应该还要屏蔽那些信号,sa_flag是一些其他选项。
4.信号处理过程中,或者说在信号处理函数中,该信号会自动加入信号屏蔽中,等处理完成后在回复原来方式。否则如果不加入屏蔽字,那么这个处理函数如果不是可重入的,就会导致一些问题。
5.示例代码:
void sighandler(int signo)
37 {
38 (void)signo;
39 std::cout<<"子进程: "<<waitpid(-1,NULL,0)<<" 退出时发送的SIGCHLD信号 !"<<std::endl;
40 exit(-1);
41 }
42
43 int main()
44 {
45 //0.定义
46 struct sigaction act,oldact ;
47 //1.初始化act
48 act.sa_handler = sighandler;
49 act.sa_flags =0;
50 sigaction(SIGCHLD,&act,&oldact);
51 //2.fork创建子进程
52
53 pid_t pid =vfork();
54 if(pid<0)
55 return -1;
56 if(pid==0)
57 {
58 std::cout<<"子进程: "<<getpid()<<" 执行完毕!"<<std::endl;
59 exit(0);
60 }
61 else
62 {
63 }
64
65
66 return 0;
67 }
//程序执行结果
[skin@bogon Git]$ ./a.out
子进程: 12912 执行完毕!
子进程: 12912 退出时发送的SIGCHLD信号 !
- 05.其他相关问题
- signal 函数也是进行捕捉
![]()
signum:信号编号
handler:处理信号函数
2.上面提到可重入函数:一段代码同一时刻有多个执行流进行执行,而不会影响其结果的正确性,称为可重入的,否则就是不可重入的。