一、什么是信号
1、信号是内容受限的一种异步通信机制
- (1)目的:用来通信
- (2)本质: int型数字编号(事先定义好的)。
- (3)特点:信号是异步的。
2、信号由谁发出
- (1)用户中终端按下按键(如 Ctrl + C)。
- (2)用户使用 kill 命令向其他进程发出信号。
- (3)硬件异常后由操作系统发出信号。
- (4)某种软件条件满足后也会发出信号,如 alarm 闹钟时间到会产生 SIGALARM 信号、向 一个读端已经关闭的管道 write 时会产生 SIGPIPE 信号。
3、信号由谁处理、如何处理
- (1)信号由进程处理。
- (2)信号处理方式:
- ①忽略信号。
- ②捕获信号(信号绑定了一个函数)。
- ③默认处理:当前进程没有明显地理会这个信号,默认忽略或终止进程。
二、常见信号介绍
-
- SIGINT 2 Ctrl + C 时 OS 发送给前台进程中每个进程
-
- SIGABRT 6 调用 abort 函数,进程异常终止
-
- SIGPOLL SIGIO 8 指示一个异步 IO 事件,在高级 IO 中提及
-
- SIGKILL 9 杀死进程的终极办法
-
- SIGSEGV 11 无效存储访问时 OS 发出该信号
-
- SIGALARM 14 涉及 alarm 函数的实现
-
- SIGPIPE 13 涉及管道和 socket
-
- SIGTERM 15 kill 命令发送的默认终止信号
-
- SIGCHLD 17 子进程终止或停止时 OS 向其父进程发出此信号
-
- SIGUSR1 10 用户自定义信号,作用和意义由应用自己定义
-
- SIGUSR2 12 用户自定义信号,作用和意义由应用自己定义
三、进程对信号的处理
1、signal 函数介绍
- (1)
原型:sighandler_t signal(int signum,sighandler_t handler)
,
同时需要配合捕获函数
typedef void (*sighandler_t)(int)
- (2)作用:用来对某一特定信号绑定一个捕获函数,signum是信号编号;handler是捕获函数指针(函数名),handler也可以设置为SIG_IGN(忽略)或SIG_DFL(默认);函数返回值是上一次对该信号设置的处理方式(出错时则返回SIG_ERR信号。)信号发生时该函数将signum传递给捕获函数。
- (3)缺点:
- ①只能处理出现一次的信号,若信号出现第二次则恢复为默认处理。
- ②要获取上一次对该信号的处理方式必须新建一个处理方式.
2、用 siganl 函数处理 SIGINT 信号
- (1)默认处理。
- (2)忽略处理。
- (3)捕获处理。==搭配一个捕获函数
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
typedef void (*sighandler_t)(int);//宏函数指针
void func(int sig)//信号捕获函数
{
if(SIGINT!=sig){
return;printf("func for signal:%d.\n",sig);
}
}
int main(void)
{
sighandler_t ret=(sighandler_t)-2;
//===选择信号处理的方式
//ret=signal(SIGINT,SIG_DFL);//指定信号SIGINT为默认处理方式
//ret=signal(SIGINT,SIG_IGN);//指定信号SIGINT为忽略处理
ret=signal(SIGINT,func);
if (SIG_ERR == ret) {
perror("signal error"); exit(-1);
}
printf("before while(1)\n");
while(1);
printf("after while(1)\n");
return 0;
}
3、sigaction 函数介绍
- 原型
int sigaction(int signum,const struct sigaction* act,struct sigaction* oldact);
- 两个均是API,但:
- (1)sigaction比signal更具有移植性,因为signal在不同的内核版本中作用可能会不同
- (2)sigaction 可处理多次出现的信号。
- (3)sigaction可以通过两个struct sigaction指针不设置新处理方式而单独获取旧处理方式
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
static void sig_handler(int sig)
{
printf("Received signal: %d\n", sig);
}
int main(int argc, char *argv[])
{
struct sigaction sig = {0};
int ret;
sig.sa_handler = sig_handler;
sig.sa_flags = 0;
ret = sigaction(SIGINT, &sig, NULL);
if (-1 == ret) {
perror("sigaction error");
exit(-1);
}
/* 死循环 */
for ( ; ; ) { }
exit(0);
}
四、alarm 函数和 pause 函数
1.alarm 函数
- 内核以API形式提供闹钟:
unsigned int alarm(unsigned int seconds)
- seconds是闹钟的时间(秒);
- 返回值是上个闹钟所剩余的时间,若之前没设置则为0.
- 一个进程只允许设置一个闹钟,新的闹钟会替代旧的闹钟。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void func(int sig)
{
if(sig==SIGALRM)
printf("alarm happened.\n");
}
int main(void)
{
unsigned int ret=-1;
struct sigaction act={0};
act.sa_handler=func;
sigaction(SIGALRM,&act,NULL);
//signal(SIGALRM,func);
ret=alarm(5);printf("ret=%d.\n",ret);
while(1);return 0;
}
2.pause 函数
- 内核挂起:
int pause(void)
- 返回值始终为-1;
- 要退出pause状态需要被信号唤醒。
3.使用 alarm 和 pause 来模拟 sleep
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void func(int sig)
{
printf("Received signal: %d\n", sig);
}
void mysleep(unsigned int seconds);
int main(void)
{
printf("before mysleep.\n");
mysleep(3);
printf("after mysleep.\n");
return 0;
}
void mysleep(unsigned int seconds)
{
struct sigaction act={0};
act.sa_handler=func;//绑定信号捕获函数
sigaction(SIGALRM,&act,NULL);
alarm(seconds);
pause();
}
若要详细了解linux信号,看我另一篇博客linux信号详解