通过本文你会了解到:
1. 信号机制简介
2. 信号处理函数( signal/sigaction/kill)
信号机制简介
信号机制就是进程间相互传递消息的机制,又称做软中断信号。就像电话一样,你不能确定什么时候有人打电话,随时随地发生。linux系统中处理信号有一下几种方式:
- Term - 终止进程
- Ign - 忽略该信号
- Core - 终止该进程并保存内存信息
- Stop - 停止进程
- Cont - 恢复已停止的进程
除了上述的5种处理,用户还可以自定义处理函数。
信号处理函数
那么在linux中应用什么函数来实现对信号进行处理呢,接下来我们一起来学习几个常用函数:
signal函数 – 为信号设置处理方法 – 早期linux版本使用,知道怎么用就可以了,在编程时还是不要用这么老的函数了。
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signum
- 信号值
handler
- 可以为自定义的信号处理函数,也可以是SIG_IGN
(忽略该信号)和SIG_DEL
(信号默认处理)。
下面以大家最熟悉的ctrl-c(终止进程)为例,根据handler取值不同给出实例(终于可以上代码了,窃喜):
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int signo)
{
switch(signo) {
case SIGINT:
printf("handle SIGINT by user\n");
break;
default:
break;
}
}
int main(int argc, char **argv)
{
printf("ctr-c will be handled by user, enter to continue..\n");
signal(SIGINT, signal_handler);
getchar();
printf("ctr-c will be ignore, enter to continue..\n");
signal(SIGINT, SIG_IGN);
getchar();
printf("ctr-c will handle by default, enter to continue..\n");
signal(SIGINT, SIG_DFL);
getchar();
printf("thanks, bye!\n");
return 0;
}
运行测试:
$ ./signal
ctr-c will be handled by user, enter to continue..
^Chandle SIGINT by user // 信号被自定义函数获取
^Chandle SIGINT by user // 信号被自定义函数获取
ctr-c will be ignore, enter to continue..
^C^C // 信号被忽略
ctr-c will handle by default, enter to continue..
^C // 默认操作,终止进程
sigaction函数 - 为信号设置处理方法
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
signum
- 信号值
act
- sigaction结构体,指定当前信号的处理信息
oldact
- sigaction结构体,返回信号之前的处理信息
要了解后两个参数的作用必须要了解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
函数中的处理函数作用相同,可以为用户自定义的处理函数,也可以为SIG_IGN
和SIG_DFL
sa_sigaction
- 信号的另一个处理函数,当sa_flags
含有SA_SIGINFO时,应用此处理函数。此函数可以提供信号的更过信息。
sa_mask
- 指定在信号处理函数运行期间需要屏蔽的信号。屏蔽的信号会被阻塞直到处理函数结束。
sa_flags
- 指定信号的处理行为,可以为0
或SA_NOCLDSTOP
/A_NOCLDSTOP
等,具体参考man 7 signal
sa_restorer
- 暂为使用,保留。
sigaction的使用也时比较复杂的,后续会结合具体实例做分析,先给一个简单的例子,完成与signal实例同样的功能:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int signo)
{
switch(signo) {
case SIGINT:
printf("handle SIGINT by user\n");
break;
default:
break;
}
}
int main(int argc, char **argv)
{
struct sigaction act;
printf("ctr-c will be handled by user, enter to continue..\n");
act.sa_flags = 0;
act.sa_handler = signal_handler;
sigaction(SIGINT, &act, NULL);
getchar();
printf("ctr-c will be ignore, enter to continue..\n");
act.sa_flags = 0;
act.sa_handler = SIG_IGN;
sigaction(SIGINT, &act, NULL);
getchar();
printf("ctr-c will handle by default, enter to continue..\n");
act.sa_flags = 0;
act.sa_handler = SIG_DFL;
signal(SIGINT, SIG_DFL);
getchar();
printf("thanks, bye!\n");
return 0;
}
运行测试:
$ ./sigaction
ctr-c will be handled by user, enter to continue..
^Chandle SIGINT by user // 信号被自定义函数获取,此处与signal行为不同,getchar函数被中断
ctr-c will be ignore, enter to continue..
^C^C^C // 信号被忽略
ctr-c will handle by default, enter to continue..
^C // 默认处理,终端程序
信号发送函数
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
pid
:可能选择有以下四种
1. pid大于零时,将信号发送给进程ID为pid的进程。
2. pid等于零时,信号将送往所有与调用kill()的那个进程属同一个使用组的进程。
3. pid等于-1时,信号将送往所有调用进程有权给其发送信号的进程,除了进程1(init)。
4. pid小于-1时,信号将送往以-pid为组标识的进程。
sig
:信号值,若为0则不发送任何信号。
kill
函数的使用实例:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int signo)
{
switch(signo) {
case SIGUSR1:
printf("get signal -- SIGUSR1\n");
break;
default:
break;
}
}
int main(int argc, char **argv)
{
pid_t pid;
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = signal_handler;
sigaction(SIGUSR1, &act, NULL);
pid = getpid();
printf("send SIGUSR1 to curent process\n");
kill(pid, SIGUSR1);
getchar();
printf("thanks, bye!\n");
return 0;
}
运行测试:
$ ./kill
send SIGUSR1 to curent process
get signal -- SIGUSR1
thanks, bye!
本文代码实例github地址:
https://github.com/zsirkg/myWorks/tree/master/signal