一、发送信号的方式
发送信号的方式有几种:
1、由键盘触发的信号(只能发送一些比较特殊的信号)
SIGINT (2):ctrl+c 中断符
SIGQUIT (3):ctrl+\ 退出符
SIGTSTP (20):ctrl+z 停止符
2、由错误和异常引发的信号
SIGILL (4) 进程试图执行非法指令
SIGBUS (7) 硬件或对齐错误
SIGFPE (8) 算术异常
SIGSEGV (11) 无法内存访问
SIGPIPE (13) 向无读取进程的管道写入
SIGSTKFLT (16) 协处理器栈错误
SIGXFSZ (25) 文件资源超限
SIGPWR (30) 断电
SIGSYS (31) 进程试图执行无效系统调用
3、用专门的系统命令发送信号
kill [-信号] PIDs
若不指明具体的信号,缺省发送 SIGTERM (15) 信号。
该信号允许用户优雅地终止进程。进程可以选择捕获该信号,并在临终之前完成必要的清理和善后工作。但如果捕获了该信号,却死赖着不走,则有流氓进程之嫌。
若要指明具体信号,可以使用信号编号,也可以使用信号名称,而且信号名称中的“SIG”前缀可以省略不写。例如:
kill -9 1234
kill -SIGKILL 1234
kill -KILL 1234
kill -9 -1 (终止所有进程)
接收信号的进程可以是一个、多个或所有的(PIDs 取 -1)。拆机用户可以发送任何进程,而普通用户只能发送自己的进程。
二、发送信号的系统函数
1、函数 kill
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
返回值:成功(至少发出去一个信号)返回 0,失败返回 -1
(1)函数功能
向指定进程或进程组发送信号
(2)参数解析
kill 的 pid 参数有以下 4 种不同的情况。
pid > 0 将该信号发送给进程 ID 为 pid 的进程
pid == 0 将该信号发送给与发送进程属于同一进程组的所有进程(这些进程的进程组 ID 等于发送进程的进程组 ID),而且发送进程具有权限向这些进程发送信号。这里用的术语“所有进程”不包括实现定义的系统进程集。对于大多数 UNIX 系统,系统进程集包括内核进程和 init (pid 为 1)。
pid < 0 将信号发送给其进程组 ID 等于 pid 绝对值,而且发送进程具有权限向其发送信号的所有进程。如前所述,所有进程并不包括系统进程集中的进程。
pid == -1 将该信号发送给发送进程有权限向它们发送信号的所有进程。若茜所述,所有进程不包括系统进程集中的进程。
参数 sig 为信号编号,取 0 可用于检查 pid 进程是否存在,如果不存在 kill 函数返回 -1,且 errno 为 ESRCH。
(3)示例说明
//示例一
//使用kill函数发送信号
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
void fa(int signo)
{
printf("捕获到了信号%d\n",signo);
}
int main(void)
{
//使用fork函数创建子进程
pid_t pid = fork();
if(-1 == pid)
{
perror("fork"),exit(1);
}
if(0 == pid) //子进程
{
printf("pid = %d\n",getpid());
//设置对信号50进行自定义处理
signal(50,fa);
while(1);
}
sleep(1);
//判断子进程是否存在
if(0 == kill(pid,0))
{
printf("父进程发送信号50\n");
kill(pid,50);
}
return 0;
}
输出结果:
pid = 4569
父进程发送信号50
捕获到了信号50
//示例二 杀死进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
int main(void)
{
pid_t pid;
if((pid=fork())<0)
{
perror("fork");
}
else if(pid==0)
{
printf("这是子进程,pid=%d,",getpid());
printf("父进程的pid=%d\n",getppid());
printf("按回车键,终止父进程\n");
getchar();
kill(getppid(), SIGTERM);
}
else
{
printf("这是父进程,pid=%d\n",getpid());
while (1)
pause();
}
return 0;
}
输出结果:
这是父进程,pid=4705
这是子进程,pid=4706,父进程的pid=4705
按回车键,终止父进程
已终止
(4)示例解析
示例一,发送信号 50 给子进程。如果 kill 第二个参数 sig 取 0,可判断子进程是否存在。
示例二,发送 SIGTERM 给指定父进程。杀死特定进程。
2、函数 raise
#include <signal.h>
int raise(int sig);
返回值:成功返回 0,失败返回非 0
(1)函数功能
向调用进程自己发送信号
(2)参数解析
sig 为信号编号
(3)函数解析
raise 函数实际上是给调用进程或者线程发送信号。
对于单线程应用来说,它相当于 kill (getpid (), sig);
对于多线程应用来说,它相当于 pthread_kill (pthread_self (), sig);
若所发信号被捕获,raise 函数会在信号处理函数返回后返回。
(4)示例说明
//示例一
//使用raise函数发送信号
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void fa(int signo)
{
printf("捕获到了信号%d\n",signo);
}
int main(void)
{
//设置信号2进行自定义处理
signal(2,fa);
int res = sleep(10);
if(0 == res)
{
printf("总算美美地睡了个好觉\n");
}
else
{
printf("睡眠被打断,还有%d秒没有来得及睡\n",res);
}
//使用raise函数发送信号2
raise(2);
while(1);
return 0;
}
输出结果:
总算美美地睡了个好觉
捕获到了信号2
//示例二 进程自杀
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
int main (void)
{
printf ("按回车键,终止进程\n");
getchar ();
raise (SIGTERM);
while (1)
pause ();
return 0;
}
输出结果:
按回车键,终止进程
已终止
(5)示例解析
示例一,如果不使用 ctrl+c,而是等待 10 秒,raise 函数会发送信号 2。
示例二,raise 发送信号 SIGTERM,终止进程。