信号
说起信号并不陌生,从常见的事物出发,例如红绿灯就是一个信号,当红灯亮起,表示停止信号,绿灯亮起表示执行信号,在Linux终端可通过kill -l命令查看系统中的信号。
其中最常用到的比如SIGINT,SIGQUIT,SIGABRT,SIGKILL等信号,这些信号的定义包含在头文件<signal.h>中,所以在使用相关的函数时需要包含该头文件。
下面对一些常见信号进行说明。
- SIGABRT 调用abort函数时产生此信号。进程异常终止。
- SIGALRM 在用alarm函数设置的计时器超时时,产生此信号。
- SIGINT 当用户按中断键(一般采用DELETE或Ctrl+C)时,终端驱动程序产生此信号并发送至前台进程组中的每一个进程。当一个进程在运行时失控,比如在终端大量打印一些不需要的输出时,常用此信号终止。
- SIGKILL 杀死任意进程的可靠方法。
- SIGQUIT 当用户在终端按退出键(Ctrl+)时,产生此信号,并发送给前台进程组中的所有进程。
当一个进程调用fork时,其子进程继承父进程的信号处理方式,因为子进程在开始时复制了父进程的存储镜像,所以信号捕捉函数的地址在子进程中也是有意义的。
在此处特别强调在进程和线程相关知识(二)中提及子进程不集成父进程的未决信号,未决信号是指当我们向进程递送了一个信号,在信号产生和递送之间的时间间隔内,称信号是未决的。简单来说,就是信号还未被响应。
其中信号机制最简单的接口是signal函数。
/**********************************************************************************/
//int signal(int signo,void(*func)(int)(int))
//参数1:信号的宏名
//参数2:当该信号发生时,所要执行的操作,
//其中sig_handler()为自己定义的行为,还可以是SIG_IGN(忽略此信号),SIG_DFL(执行默认行为)
/**********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
//信号处理函数
void sig_handler(int signum)
{
printf("This is a sig_handle func!\n");
}
int main(int argc, char *argv[])
{
int i;
//该函数的作用是当用户按下Ctrl+C时,不再发生终止行为,而是执行对应的信号处理函数。
signal(SIGINT,sig_handler);
//当调用alarm函数是,产生SIGALRM信号
alarm(1);
//当SIGALRM信号产生时,忽略此信号,程序继续向下执行。
signal(SIGALRM,SIG_IGN);
for(i = 0;i < 10;i++)
{
write(1,"*",1);
sleep(1);
}
return 0;
}
同样可以使用kill函数向指定的进程发送信号。
/********************************/
//int kill(pid_t pid,int sig)
//参数1:要发送信号的进程号
//参数2:向指定进程发送的信号
/*******************************/
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int num;
pid_t pid;
pid = fork();
if(pid == 0){
while(1){
printf("hello world\n");
sleep(1);
}
}
if(pid > 0){
while(1){
scanf("%d",&num);
if(num == 0)
kill(pid,SIGSTOP); //当用户输入0时向子进程发送暂停信号。
if(num == 1)
kill(pid,SIGCONT); //当用户输入1时向子进程发送继续信号。
if(num == 2)
kill(pid,SIGINT); //当用户输入2时向子进程发送终止信号。
}
}
return 0;
}