信号通讯
1. 信号概述
信号是在软件层次上对中断机制的一种模拟。在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的:一个进程不必通过任何操作来等待信号的到达。信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。它可以在任何时候发送给某一进程,而无需知道该进程的状态。如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它为止;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。
信号是进程间通讯机制中唯一的异步通讯机制。可以把这种机制看作异步通知,通知接受信号的进程有哪些事情发生了。信号事件的产生有硬件来源(比如按键或硬件故障)和软件来源(kill()、raise()、alarm()、setitimer()、sigqueue()等,还包括一些非法运算操作)。
进程可以通过3种方式来响应一个信号:
- 忽略信号:对信号不做任何处理。SIGKILL及SIGSTOP信号不能被忽略
- 捕捉信号:定义信号处理函数,当信号发生时执行相应的处理函数
- 缺省方式:Linux对每种信号都规定了默认操作,如下表所示
信号名 | 含义 | 默认操作 |
---|---|---|
SIGHUP | 用户终端连接结束时发出,通知同一会话内的各个进程与控制端不再关联 | 终止进程 |
SIGINT | 输入INTR字符时(Ctrl+C)时发出,并送到前台进程的每个进程中 | 终止进程 |
SIGQUIT | 和SIGINT类似,但有QUIT字符(Ctrl+ \ )来控制 | 终止进程 |
SIGILL | 在一个进程企图执行一条非法指令时发出 | 终止进程 |
SIGFPE | 在发生致命的算术运算时发出 | 终止进程 |
SIGKILL | 用来立即结束程序的运行,且不能被阻塞、处理和忽略 | 终止进程 |
SIGALARM | 当一个定时器到时的时候发出 | 终止进程 |
SIGSTOP | 用于暂停一个进程,且不能被阻塞、处理和忽略 | 暂停进程 |
SIGTSTP | 用于交互停止进程,用户在输入SUSP字符时(Ctrl+Z)发出 | 终止进程 |
SIGCHLG | 子进程改变状态时,父进程会收到这个信号 | 忽略 |
2. 信号相关函数
2.1 信号发送:kill() 和 raise()
kill() 函数与 kill 系统命令一样,可以发送信号给进程或进程组(kill系统命令就是用kill()函数实现的)。该函数不仅可以终止进程,也可以向进程发送其他信号。
raise() 函数只允许进程向自身发送信号。
/*****kill()函数*****/
函数原型:int kill(pid_t pid, int sig)
传 入 值:pid -->正数:发送信号给进程号为pid的进程
--> 0:信号被发送到所有和当前进程在同一个进程组的进程
-->-1:信号发给所有的进程表中的进程(除了进程号最大的进程外)
-->小于-1:信号发送给进程组号为-pid的每一个进程
sig 信号类型
返 回 值:成功返回0;失败返回-1
/*****raise()函数*****/
函数原型:int raise(int sig)
传 入 值:sig 信号类型
返 回 值:成功返回0;失败返回-1
实例程序
/*****kill_raise.c*****/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(){
pid_t pid;
int ret;
if((pid == fork()) < 0){
//创建子进程
printf("Fork error\n");
exit(-1);
}
if(pid == 0){
/*在子进程中使用raise()函数发出SIGSTOP信号,使子进程暂停*/
printf("child(pid: %d) is waiting for any signal!\n",getpid());
raise(SIGSTOP);
exit(0);
}else{
/*在父进程中收集子进程的状态,并调用kill()函数发送信号*/
if((waitpid(pid,NULL,WNOHANG)) == 0){
kill(pid,SIGKILL);
printf("parent kill child process %d\n",pid);
}
waitpid(pid,NULL,0);
exit(0);
}
}
程序运行结果如下
linux@linux-virtual-machine:~/andy/proc$ ./kill_raise
child(pid:4877) is waiting for any signal!
parent kill child process 4877
2.2 定时器信号:alarm() 和 pause()
alarm() 也称为闹钟函数,它可以在进程中设置一个定时器。当定时器指定的时间到了时,它就向进程发送SIGALARM信号。要注意的是,一个进程只能有一个闹钟时间,如果在调用 alarm()之前已经设置过闹钟时间,则任何以前的闹钟时间都被新值所代替。
pause() 用于将调用进程挂起直至接收到信号为止。
/*****alarm()函数*****/
函数原型:unsigned int alarm(unsigned int seconds)
传 入 值:seconds 指定秒数,系统经过seconds秒后向该进程发送SIGALARM信号
返 回 值:成功:如果调用此函数前,进程中已经设置了闹钟时间,则返回上一个闹钟剩余的时间,否则返回0
失败:返回-1
/*****pause()函数*****/
函数原型:int pause(void)
返 回 值:-1 并且把errno值设为EINTR
实例程序
/*****alarm_pause.c*****/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
alarm(5);
pause();
printf("I have been waken up\n"); //此语句不会被执行
}
程序运行结果如下
linux@linux-virtual-machine:~/andy/proc$ ./alarm_pause
Alarm clock //SIGALARM信号默认处理函数打印
2.3 信号设置:signal() 和 sigaction()
信号处理主要有两种方法:一种是使用简单的signal() 函数,另一种是使用 sigaction()。
signal() 函数只需要指定信号类型和信号处理函数即可。主要用于前32种非实时信号的处理,不支持信号传递信息。
sigaction() 函数相对 signal() 函数更加健壮、功能更全。
/*****signal()函数*****/
函数原型:typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
传 入 值:signum 指定信号代码
handler -->SIG_IGN 忽略该信号
-->SIG_DFL 采用系统默认方式处理信号
-->自定义的信号处理函数
返 回 值:成功:返回以前的信号处理函数
失败:返回-1
/*****sigaction()函数*****/
函数原型:int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
传 入 值:signum 信号类型,除SIGKILL和SIGSTOP之外的任何一个信号
act 指向sigaction结构体的指针,包含对特点信号的处理
oldact 保存信号原先的处理方式
返 回 值:成功返回0;失败返回-1
//sigaction结构体的定义
struct sigaction{
void (*sa_handler)(int signo); //函数指针,指向信号处理函数
sigset_t sa_mask; //信号集合,用来指定在信号处理函数执行过程中哪些信号被屏蔽
int sa_flags; //信号处理相关的各种标志位
void (*sa_restore)(void);
}
signal() 实例程序
/*****signal.c*****/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void my_func(int sign_no){
//自定义信号处理函数
if(sign_no == SIGINT)
printf("I have got SIGINT\n");
else if(sign_no == SIGQUIT)
printf("I have got SIGQUIT\n");
}
int main(){
printf("Waiting for signal SIGINT or SIGQUIT...\n");
signal(SIGINT,my_func);
signal(SIGQUIT,my_func);
pause();
exit(0);
}
程序运行结果如下
linux@linux-virtual-machine:~/andy/proc$ ./signal
Waiting for signal SIGINT or SIGQUIT...
I have got SIGINT (按 ctrl + c 组合键)
linux@linux-virtual-machine:~/andy/proc$ ./signal
Waiting for signal SIGINT or SIGQUIT...
I have got SIGQUIT (按 ctrl + \ 组合键)
sigaction() 实例程序,实现同样的功能
/*****sigaction.c*****/
//头文件省略
//自定义信号处理函数同以上实例
int main(){
struct sigaction action;
/*sigaction结构初始化*/
sigaction(SIGINT,0,&action);
action.sa_handler = my_func;
sigaction(SIGINT,&action,0);
sigaction(SIGQUIT,0,&action);
action.sa_handler = my_func;
sigaction(SIGQUIT,&action,0);
printf("Waiting for signal SIGINT or SIGQUIT...\n");
pause();
exit(0);
}