一.信号处理常见函数:
1.signal 函数
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
说明: 次函数为信号处理函数 或者 信号扑捉函数
2. kill 和 raise 函数
#include <signal.h>
int kill(pid_t pid, int sig);
说明: 将信号发送给进程或者进程组。
int raise(int sig);
说明: 允许进程向自身发送信号。 等价于调用 kill(getpid(),signo);
3.alarm 和 pause
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
说明:使用这个函数可以设置一个计时器,在某个指定时间产生SIGALRM信号
int pause(void);
说明:使调用进程挂起直至捕捉到一个信号。
4.信号集--- 数据类型
#include <signal.h>
int sigemptyset(sigset_t *set); //将信号集初始化为空
int sigfillset(sigset_t *set); //将信号集初始化为包含所有已定义的信号
int sigaddset(sigset_t *set, int signum); ///添加一个信号到信号集
int sigdelset(sigset_t *set, int signum); // 从信号集中删除一个信号
int sigismember(const sigset_t *set, int signum); // 判断一个给定的信号是否是一个信号集的成员
5.sigprocmask 函数
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
说明: 信号屏蔽的设置函数
SIG_BLOCK --把参数set中的信号添加到信号屏蔽字中
SIG_SETMASK -- 把信号屏蔽字设置为参数set中的信号
SIG_UABLOCK --从信号屏蔽字中删除参数set中的信号
6.sigpending 函数
#include <signal.h>
int sigpending(sigset_t *set);
说明: 返回信号集,
7. sigaction 函数
#include <signal.h>
int sigaction(int signum, /// 被处理的信号
const struct sigaction *act, ///处理函数及其参数
struct sigaction *oldact); //返回原来的处理函数
说明: 检查,修改与指定信号相关联的处理动作。
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
8.sigsuspend 函数
#include <signal.h>
int sigsuspend(const sigset_t *mask);
说明: 将进程的信号屏蔽字设置为由mask 指向的值。在捕捉到一个信号或者发生了一个会终止该进程的信号之前,该进程被挂起。如果捕捉到一个信号而且从该信号处理程序返回,则sigsuspend返回,并且将进程的信号屏蔽字设置为调用sigsuspend 之前的值。
几个经典案例程序:
案例1: 写一个程序,只要有信号触发,程序将退出
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
/// 信号处理函数
void deal(int s)
{
printf("有信号!\n");
}
int main()
{
sigset_t new,old;
struct sigaction act;
act.sa_handler = deal; // 绑定信号处理函数
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT,&act,0);// 准备扑捉SIGINT信号
sigemptyset(&new);
sigaddset(&new,SIGINT);
sigprocmask(SIG_BLOCK,&new,&old); // 将SIGINT信号阻塞,同时保存到当前信号
printf("SIG_BLOCK");
sigprocmask(SIG_UNBLOCK,&old,NULL);// 取消阻塞
//pause();
sleep(5);
return 0;
}
解析:结果发现,程序一直处于挂起状态。在取消阻塞和 pause()函数之间有个临界区域执行码,使程序处理一直挂起状态。
案例2: 使用sigsuspend() 函数解决上述问题
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void deal(int s)
{
if(s== SIGINT)
printf("信号SIGINT\n");
else if(s == SIGQUIT)
printf("信号SIGQUIT\n");
else
printf("信号SIGUSR1\n");
}
int main()
{
sigset_t new,old,wait; // 三个信号集
struct sigaction act;
act.sa_handler = deal;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT,&act,0); // 可以捕捉到3个信号SIGINT/SIGQUIT/SIGUSR1
sigaction(SIGINT,&act,0);
sigaction(SIGINT,&act,0);
sigemptyset(&new);
sigaddset(&new,SIGINT); // 把SIGINT信号加载到new信号集中
sigemptyset(&wait);
sigaddset(&wait,SIGUSR1);
sigprocmask(SIG_BLOCK,&new,&old); // 将SIGINT信号阻塞,保存在当前信号old
// 临界区执行
if( sigsuspend(&wait) != -1)
{
printf("sigsuspend 错误!\n");
}
sigprocmask(SIG_SETMASK,&old,NULL);
return 0;
}
解析:if(sigsuspend(&wait) != -1)
程序在此处挂起;用wait信号集替换new信号集。即:过来SIGUSR1信
sigsuspend的原子操作是:
(1)设置新的mask阻塞当前进程(上面是用wait替换new,即阻塞SIGUSR1信号)
(2)收到SIGUSR1信号,阻塞,程序继续挂起;收到其他信号,恢复原先的mask(即包含SIGINT信号的)。
(3)调用该进程设置的信号处理函数(程序中如果先来SIGUSR1信号,然后过来SIGINT信号,则信号处理函数会调用两次,打印不同的内容。第一次打印SIGINT,第二次打印SIGUSR1,因为SIGUSR1是前面阻塞的)
(4)待信号处理函数返回,sigsuspend返回了。(sigsuspend将捕捉信号和信号处理函数集成到一起了)
案例3:(集中处理外部中断)
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void deal(int s)
{
printf("外部中断处理中..... \n");
sleep(3);
printf("外部中断处理完毕!\n");
}
main()
{
int i;
sigset_t new,old;
sigemptyset(&new);
sigemptyset(&old);
sigaddset(&new,SIGINT);
signal(SIGINT,deal);
sigprocmask(SIG_BLOCK,&new,0);
for(i=1; i<=10; ++i)
{
printf("正在拷贝文件<%d>!\n",i);
sleep(3);
printf("拷贝文件<%d> 完毕!\n",i);
sigpending(&new);//函数返回在送往进程的时候被阻塞挂起的信号集合
if( sigismember(&new,SIGINT))
{
sigsuspend(&old);
}
}
printf("所有文件拷贝完毕\n");
sigprocmask(SIG_UNBLOCK,&new,0);
printf("over!\n");
}
调试结果:
案例4:
1.使用sigaction 处理信号,使用kill 发送信号
2.使用sigacction 处理信号,使用sigqueue 发送信号
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void deal(int s)
{
printf("外部中断处理中..... \n");
sleep(3);
printf("外部中断处理完毕!\n");
}
main()
{
struct sigaction act = {0};
act.sa_handler = deal;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,SIGINT);
act.sa_flags = 0;
sigaction(SIGUSR1,&act,0);
while(1);
}
先运行程序,打开另外一个终端,给这个运行的程序发送信号SIGUSR1,
---------------------------------------
$ kill -USR1 12771
$ kill -INT 12771
------------------------------------------------------------------