Linux信号学习笔记(1)
程序运行
当如下代码在运行时如需中止这份程序有几种办法。
#include<unistd.h>
#include<stdio.h>
using namespace std;
int main()
{
for(int i=1;i<=100;i++)
{
printf("time of programe running:%ds\n",i);
sleep(1);
}
}
1. 在运行程序的终端中使用"ctrl+c"组合键的方法可以终止:
~$ ./demo_1
time of programe running:1s
time of programe running:2s
time of programe running:3s
^C
2. 在程序运行时,使用终端(另开一个)中ps -ef|grep 进程关键字的方式得到进程号,再使用"kill 进程号"的方式终止:
~$ ps -ef|grep demo_1
mrlop 13554 13146 0 18:47 pts/0 00:00:00 ./demo_1
mrlop 13602 13524 0 18:47 pts/1 00:00:00 grep --color=auto demo_1
~$ kill 13554
此时运行程序的终端中会有如下内容:
~$ ./demo_1
time of programe running:1s
time of programe running:2s
time of programe running:3s
已终止
3. 在程序运行时,使用"killall 进程关键字"的方式终止:
~$ killall demo_1
此时运行程序的终端中会有如下内容:
~$ ./demo_1
time of programe running:1s
time of programe running:2s
time of programe running:3s
已终止
Linux信号
由上述内容中,均是向"demo_1"程序发送信号将其终止,其中"ctrl+c"为发送SIGINT信号,kill和killall均是发送SIGTERM信号,其余信号及功能可以查询Linux信号表,使用"kill -l"指令可以得到Linux信号列表。
~$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
所以SIGINT代码是2,SIGTERM代码是15,kill还可以使用"kill -信号代码 进程号"或"kill -信号名称 进程号"来向对应进程发送其他的信号,中间的参数不加则是发送SIGTERM信号。
signal函数
使用signal.h库中声明的函数signal:
__sighandler_t signal (int __sig, __sighandler_t __handler)
signal函数中第一个参数为信号名称或信号代码,第二个参数为收到信号时运行的处理函数名。signal函数的第二个参数还可以填写"SIGIGN"忽略该信号,"SIG_DFL"回复该信号的处理方式为默认。
修改程序如下:
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
using namespace std;
void handle(int sig)
{
printf("receive signal:%d, program exit\n",sig);
exit(0);
}
int main()
{
signal(15,handle);
for(int i=1;i<=100;i++)
{
printf("time of programe running:%ds\n",i);
sleep(1);
}
}
运行时使用"killall demo_2"的方式结束进程,进程将收到15信号,对于15信号会交由handle函数进行处理。得到以下结果:
~$ ./demo_2
time of programe running:1s
time of programe running:2s
time of programe running:3s
receive signal:15, program exit
但是值得注意的是,即使代码中添加了"signal(9,handle)",使用"kill -9 demo_2"时程序依旧会直接被kill,因为其中SIGKILL(9)和SIGSTOP(19)是不能被屏蔽的。
~$ ./demo_2
time of programe running:1s
time of programe running:2s
已杀死
kill函数
c语言中提供了kill函数用于发送信号:
int kill (__pid_t __pid, int __sig)
第一个参数为所要发送信号的目的进程号,第二个参数为所要发送信号的信号代码。如下代码父进程向子进程发送15信号。
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
using namespace std;
void handle(int sig)
{
printf("receive signal:%d, my pid=%d, my parent pid=%d, program exit.\n",sig,getpid(),getppid());
exit(0);
}
int main()
{
int pid;
pid=fork();
if(pid>0)
{
printf("parent pid:%d.\n",getpid());
sleep(1);
kill(pid,15);
sleep(2);
}
else
{
printf("child pid:%d.\n",getpid());
signal(15,handle);
sleep(2);
}
}
为了避免信号发送的时机不对,以及父进程过早结束导致子进程getppid()获得的父进程号不一致使用了sleep()函数。可以得到如下运行结果:
~$ ./demo_3
parent pid:15694.
child pid:15695.
receive signal:15, my pid=15695, my parent pid=15694, program exit.