对Linux来说,信号时软中断,许多重要的程序都需要处理信号。
信号为Linux提供了一种处理异步事件的方法。
比如:终端用户输入ctrl+c来中断程序,会通过信号机制停止一个程序。
信号概述:
1.信号的名字和编号
每个信号都有一个名字和编号,这些名字都以"SIG"开头,例如"SIGIO","SIGCHLD"等
等。
信号定义在signal.h头文件中,信号名都定义为正整数。
具体的信号名称可以使用kill -l来查看信号的名字以及序号,信号时从1开始编号的,
不存在0号信号。kill对于信号0有特殊意义。
2.信号的处理
信号的处理有三种方法: 忽略,捕捉和默认动作。
忽略信号:大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别
是SIGKILL和SIGSTOP)。因为他们向内核和超级用户提供了进程终止和停止的可靠方
法。如果忽略,那么这个进程就变成了没人能管理的进程,显然是内核设计者不希望
看到的场景。
捕捉信号:需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处
理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函
数,以此来实现某种信号的处理。
系统默认动作:对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,
系统会自动执行。不过,对系统来说,大部分的吃力方式都比较粗暴,就是直接杀死
该进程。
具体的信号默认动作可以使用 man 7 signal 来查看系统的具体定义。
3.信号处理函数的注册------入门版:signal 高级版:sigaction
信号处理发送函数------入门版:kill 高级版:sigqueue
$:ps -aux|grep a.out-----------------查看a.out进程
$:kill -9 (进程ID) ------------------杀死进程
或$:kill -SIGKILL (进程ID)-----------杀死进程
对于信号而言,最大意义不是杀死信号,而是实现一些异步通讯手段。
入门: 重在动作,没有携带信息
signal原型:
typedef void(*sighandler_t)(int)---------函数指针
sighandler_t signal(int signum,sighandler_t handler);
int signum:信号编码
sighandler_t(结构体) handler:函数指针
kill原型:
int kill(pid_t pid,int sig);
pid:进程ID号
sig:信号编号
例:按Ctrl+c不退出
#include <signal.h>
#include <stdio.h>
void handler(int signum)
{
printf("get signum:%d\n",signum); // 打印信号的值
printf("never quit\n");
}
int main()
{
signal(SIGINT,handler); //信号注册,捕捉信号
//signal(SIGINT,SIG_IGN); //忽略信号,按Ctrl+c后没有反应,但仍可以用指令杀死
while(1);
return 0;
}
运行结果: while(1):一直执行
按Ctrl+c出现:
^Cget signum:2
never quit
^Cget signum:2
never quit
^Cget signum:2
never quit
^Cget signum:2
never quit
^Cget signum:2
never quit
按Ctrl+x退出
通过软件编程来实现signal信号捕捉后对信号进行专配
信号处理dome
#include <signal.h>
#include <stdio.h>
void handler(int signum)
{
printf("get signum:%d\n",signum);
printf("never quit\n");
}
int main()
{
signal(SIGINT,handler);
while(1);
return 0;
}
信号发送dome
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
int signum;
int pid;
signum = atoi(argv[1]); //因为是char **argv,所以要用atoi转成int
pid = atoi(argv[2]);
printf("signum:%d,pid:%d\n",signum,pid);
kill(pid,signum); //信号发送
return 0;
}
也可以用shell脚本的方式去做:
char cmd[128] = {0};
sprintf(cmd,"kill -%d %d",signum,pid);
system(cmd); 来显示打印信息
运行结果:
信号处理运行,信号发送要先得知信号处理的进程ID,通过(ps -aux|grep 运行程序名)
查看。然后发送信号,杀死进程:./a.out 9 4325
高级: 发送信号的同时带有信息
接收信号原型:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
struct sigaction {
void (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽
略,SIG_DFL 为默认动作
void (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额
外数据和sigqueue配合使用
sigset_t sa_mask;//阻塞关键字的信号集,可以再调用捕捉函数之前,把信号添加到信
号阻塞字,信号捕捉函数返回之前恢复为原先的值。
int sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据
};
//回调函数句柄sa_handler、sa_sigaction只能任选其一
信号发送函数:
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
union sigval {
int sival_int;
void *sival_ptr;
};
处理信号信息dome
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void handler(int signum, siginfo_t *info, void *context)
{
printf("get signum:%d\n",signum);
if(context != NULL){
printf("get date:%d\n",info->si_int);
printf("get date:%d\n",info->si_value.sival_int);
printf("from:%d\n",info->si_pid);
}
}
int main()
{
struct sigaction act;
printf("pid = %d\n",getpid());
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1,&act,NULL);
while(1);
return 0;
}
首先用sigaction来注册信号,注册的时候第一个参数是我要收哪一个信号,
第二个参数是我收到这个信号后想要干什么,第三个参数是用来备份。
第二个参数是这么回事,我要接收信号就必须指定里面的flags这个参数,收
到信号以后去调用handler来处理信号,handler收到信号后吧信号的值打印
出来(signum),接着把内容(info)打印出来,内容是否有,取决于context,
context非空的话,info就有内容,把info里面的si_int数据打印出来。
发送信号dome
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
int signum;
int pid;
signum = atoi(argv[1]);
pid = atoi(argv[2]);
union sigval value;
value.sival_int = 999;
sigqueue(pid,signum,value);-------value:联合体
printf("done\n");
return 0;
}
运行结果:
详细教程,在此网站:Linux 信号.