信号的概念及类型
信号是Linux系统中用于进程之间通信或操作的一种机制,信号可以在任何时候发送给某一进程,而无须知道该进程的状态。如果该进程并未处于执行状态,则该信号就由内核保存起来,直到该进程恢复执行并传递给他为止。如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。
收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:
- 第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。
- 第二种方法是,忽略某个信号,对该信号不做任何处理,就象未发生过一样。
- 第三种方法是,对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得进程终止。进程通过系统调用signal来指定进程对某个信号的处理行为。
在linux下我们可以使用 kill -l 命令查看当前系统支持的信号,需要注意的是不同的系统支持的信号是不一样的:
在上面的信号中,我们常见或用到的是:
信号编号 | 信号名 | 信号说明 | 默认动作 |
---|---|---|---|
2 | SIGINT | Ctrl+c按键终止程序运行的信号 | 程序终止 |
4 | SIGILL | 非法的指令 | 程序终止 |
7 | SIGBUS | 运行非本CPU相关编译器编译的程序 | 程序终止 |
9 | SIGKILL | 强制杀死程序信号,任何程序都不可以捕捉该信号 | 程序终止,不可被捕捉 |
10 | SIGUSR1 | 用户自定义信号1 | 程序终止 |
11 | SIGSEGV | 段错误系统给程序发送的信号 | 程序终止 |
12 | SIGUSR2 | 用户自定义信号2 | 程序终止 |
13 | SIGPIPE | 管道破裂信号 | 程序终止 |
14 | SIGALRM | alarm()系统调用发送的信号 | 程序终止 |
15 | SIGTERM | kill命令默认发送的信号,默认动作是终止信号 | 程序终止 |
17 | SIGCHLD | 子进程退出信号 | 忽略该信号 |
信号的安装
linux下主要有两个函数实现信号的安装:signal()、sigaction()。
1、signal()
#include <signal.h> //头文件
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
函数说明:注册一个信号捕捉函数
参数说明:① 第一个参数signum指定信号的值。②第二个参数handler制定针对前面函数的处理,可忽略该信号(参数设为SIG_IGN),可以采用系统默认方式处理信号(参数设为SIG_DFL),也可以自己实现处理方式(参数指定一个函数地址),即当指定信号到底时,就会跳转到handler指定的函数执行。
返回值:signal()调用成功时,返回最后一次为安装信号signum而调用signal()时的handler值,失败返回SIG_ERR。
2、sigaction()
#include <signal.h>
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact);
函数说明:函数可对发来的信号做排队处理(通常在Linux用其来注册一个信号的捕捉函数)
参数说明:①第一个参数signum为要操作的信号,可以为SIGKILL及SIGSTOP外的任何一个特定有效的信号(这两个信号定义自己的处理函数,导致安装错误)。②第二个参数act为指向sigaction结构体的一个指针,在结构体中指定了对信号新的处理方式,当为NULL时,进程会按缺省的方式对信号处理。③第三个参数oldact参数输出先前信号的处理方式,也可指为NULL。若第二个参数和第三个参数都为NULL则该函数用来检查信号的有效性。
struct sigaction结构体如下:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数。
sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置
sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。
①SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
②SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
③SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。若SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号。
signal()代码示例
/*********************************************************************************
* Copyright: (C) 2020 makun<1394987689@qq.com>
* All rights reserved.
*
* Filename: signal.c
* Description: This file
*
* Version: 1.0.0(2020年03月20日)
* Author: makun <1394987689@qq.com>
* ChangeLog: 1, Release initial version on "2020年03月20日 14时19分06秒"
*
********************************************************************************/
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
int g_sigstop = 0;
void signal_stop(int signum)
{
if( SIGTERM == signum )
{
printf("SIGTERM signal detected\n");
}
else if( SIGALRM == signum )
{
printf("SIGALRM signal detected\n");
g_sigstop = 1;
}
}
void signal_code(int signum)
{
if(SIGBUS == signum)
{
printf("SIGBUS signal detected\n");
}
else if(SIGILL == signum)
{
printf("SIGILL signal detected\n");
}
else if(SIGSEGV == signum)
{
printf("SIGSEGV signal detected\n");
}
exit(-1);
}
int main (int argc, char **argv)
{
char *ptr=NULL;
signal(SIGTERM,signal_stop);
/*安装SIGTERM信号,SIGTERM这是由kill(1)命令发送的系统默认终止信号,由于该信号是由应用程序捕获的使用SIGTERM也让程序有机会在退出之前做好清理工作,从而优雅的终止*/
signal(SIGALRM,signal_stop);
/*安装SIGALRM,SIGALRM,当用alarm函数设置的定时器超时时,产生此信号,若由setitime(2)函数设置的间隔时间已经超时时,也产生此信号。*/
signal(SIGBUS,signal_code);
/*指示一个实现定义的硬件故障,当出现某些类型的的内存故障时,实现常常产生此种信号*/
signal(SIGILL,signal_code);
/* 此信号表示进程已执行一条非法硬件指令 */
signal(SIGSEGV,signal_code);
/*指示进程进行了一次无效的内存引用(通常说明程序有错,比如访问了一个未初始的指针)*/
printf("program start running for 20 seconds...\n");
alarm(20);//程序执行到这会打印SIGALRM signal detected
while(!g_sigstop)
{
;
}
printf("program start stop running...\n");
printf("invalid pointer operator will raise SIGSEGV signal\n");
*ptr = 'h';//程序执行到这会打印SIGSEGV signal detected
return 0;
}
代码运行结果:
sigaction()代码示例
/*********************************************************************************
* Copyright: (C) 2020 makun<1394987689@qq.com>
* All rights reserved.
*
* Filename: sigaction.c
* Description: This file
*
* Version: 1.0.0(2020年03月20日)
* Author: makun <1394987689@qq.com>
* ChangeLog: 1, Release initial version on "2020年03月20日 14时19分06秒"
*
********************************************************************************/
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
void sig_usr(int signum)
{
if( SIGUSR1 == signum )
{
printf("SIGUSR1 signal detected\n");
}
else if( SIGUSR2 == signum )
{
printf("SIGUSR2 signal detected\n");
}
}
int main (int argc, char **argv)
{
char buf[512];
int n;
struct sigaction sa_usr;
sa_usr.sa_flags =0;
sa_usr.sa_handler = sig_usr; //信号处理函数
sigaction(SIGUSR1, &sa_usr, 0);
sigaction(SIGUSR2, &sa_usr, 0);
printf("My PID is %d\n", getpid());
while(1)
{
if((n = read(STDIN_FILENO, buf, 511)) == -1)
{
if(errno == EINTR)
{
printf("read is interrupted by signal\n");
}
}
else
{
buf[n] = '\0';
printf("%d bytes read: %s\n", n, buf);
}
}
return 0;
}
- 第一次运行结果:进程在这里阻塞,等待另一个进程发送信号
- 进程2给进程1-9709发送信号
3.进程1收到信号后