一 信号概念
在计算机科学中,信号(英语:Signals)是 Unix、类 Unix 以及其他 POSIX 兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程 正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。
每个信号都有一个名字, 这些名字都以三个字符 SIG 开头。 例如,SIGABRT 是夭折信号, 当进程调用abort 函数时产生这种信号。SIGALRM 是闹钟信号,当由 alarm 函数设置的计时器超时后产生此信号。在UNIX 系统中,这些信号都定义在头文件 <signal.h> 中,并且都是以一个正整数来表示(信号编号)。通过在shell 中运行命令 kill -l 可以查看当前系统所执行的所有信号。
UNIX 系统规定了内核可以对信号执行以下三种处理行为,
- 忽略此信号。有两个信号 SIGKILL 和 SIGSTOP 不可忽略,这两个信号提供给超级用户终止或停止
进程的可靠方法。 - 执行系统默认动作。大多数信号的默认动作是终止进程。
- 捕获信号,执行用户自定义的处理函数。
二 信号处理
2.1 signal 定义
#include <signal.h>
void (*signal(int num, void (*handler)(int))) (int)
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
2.2 signal的简单应用:
#include <signal.h>
#include <stdio.h>
/*
* void (*signal(int num, void (*handler)(int))) (int)
* typedef void(handler_t)(int)
* handler_t signal(int, handler_t)
*/
void handler(int num)
{
printf("handler: num = %d\n", num);
}
int main(int argc, char const *argv[])
{
//capture signal and binding signal handle function
signal(SIGINT, handler);
while (1);
return 0;
}
Ubuntu下测试输出:
xxx@ubuntu:~/Documents/linux环境高级编程/signal$ ./a.out
^Chandler: num = 2
^Chandler: num = 2
^Chandler: num = 2
^\退出
2.3 raise and kill
UNIX 系统提供了两个函数 kill 和 raise 来产生信号。kill 函数将信号发送给指定的进程或进程组。
raise 函数则允许进程向自身发送信号。
#include <signal.h>
int kill(pid_t pid, int signo);
pid : 需要发送信号的进程的进程号
signo : 信号值
int raise(int signo); 给自己发送信号 等价于 kill(getpid(),signo);
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handler(int num)
{
printf("raise a signal!\n");
}
int main(int argc, char const *argv[])
{
//ignore SIGINT signal
//signal(SIGINT, SIG_IGN);
//default
//signal(SIGINT,SIG_DFL);
signal(SIGINT, handler);
sleep (3);
raise(SIGINT); //3秒后给自己发送一个SIGINT信号 Default action is to terminate the process. 终止进程
return 0;
}
Ubuntu下测试输出:
xxx@ubuntu:~/Documents/linux环境高级编程/signal$ ./a.out
raise a signal! //等待三秒之后的输出
2.4 signal在父子进程中使用
signal提供关于用于自定义的信号值如下:
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
关于signal更多信息使用 man 7 signal 命令查看
使用signal在父子进程中使用代码如下所示:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
//wait : will chock and always ask.
//SIGHOLD: is similar to interrupt.
void my_handler(int num)
{
printf("the subprocess has ended!%d\n",num);
printf("ID- %d, this is my_handler : num = %d\n", getpid(),num);
int status;
pid_t pid = wait(&status);
// xxxxxx See (man wait) for further specification for the status resolution
}
void my_siguser1(int num)
{
printf("ID- %d, this is my_siguser1 : num = %d\n", getpid(),num);
}
int main(int argc, char const *argv[])
{
pid_t pid = fork();
if (pid < 0){
perror("fork");
return 0;
}if (pid > 0){ // parent process
printf("parent process : pid = %d, ppid = %d\n", getpid(), getppid());
sleep(1);
signal(SIGCHLD,my_handler);
kill(pid, SIGUSR1);
while(1);
}if (0 == pid){ //subprocess
printf("subprocess : pif = %d, ppid = %d\n", getpid(),getppid());
//Both SIGUSR1 and SIGUSR2 are for user define
signal(SIGUSR1, my_siguser1); //make SIGUSR1 bind my_siguser1 fuction
sleep(3);
printf("the subprocess will end\n");
//SIGCHLD before the subprocess end, it will send a SIGCHLD signal to parent process
}
return 0;
}
Ubuntu下测试输出:
xxx@ubuntu:~/Documents/linux环境高级编程/signal$ ./a.out
father process : pid = 3395, ppid = 2845
subprocess : pif = 3396, ppid = 3395
ID- 3396, this is my_siguser1 : num = 10
the subprocess end
the subprocess has ended!17
ID- 3395, this is my_handler : num = 17
^C
2.5 signal在父子进程中应用
使用fork创建两个子进程,父进接收stdin的输入,如果输入中含有“terminate_all”,则终止所有进程(包括父进程),否则,如果输入中含有“terminate_01”,则终止第一个子进程,否则,如果输入中含有“terminate_02”,则终止第二个子进程。
具体代码实现如下:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
pid_t pid1 = fork();
if (pid1 < 0){
perror("fork1");
return -1;
}else if (pid1 > 0){
pid_t pid2 = fork();
if (pid2 < 0){
perror("fork2");
return -1;
}else if (pid2 > 0){ //parent process
char * ch = (char *)malloc(100);
while (1){
scanf("%s", ch);
if (strstr(ch, "terminate_all")){
kill(pid1,SIGINT);
kill(pid2,SIGINT);
raise(SIGINT);
}else if (strstr(ch, "terminate_01")){
kill(pid1,SIGINT);
}else if(strstr(ch, "terminate_02")){
kill(pid2,SIGINT);
}
}
}else if(0 == pid2){ // subprocess2
while (1)
{
sleep(5);
printf ("this is subprocess2\n");
}
}
}else if(0 == pid1){ // subprocess1
while (1)
{
sleep(5);
printf ("this is subprocess1\n");
}
}
return 0;
}
Ubuntu下测试输出:
xxx@ubuntu:~/Documents/linux环境高级编程/signal$ ./a.out
this is subprocess2
this is subprocess1
terminate_01
this is subprocess2
this is subprocess2
terminate_02
terminate_all
xxx@ubuntu:~/Documents/linux环境高级编程/signal$