小笨LOVE小熊

来源:百度文库 编辑:神马文学网 时间:2024/04/29 08:36:15
LINUX 信号处理
发表时间:2006-8-30 15:50:20
一 入门信号是有相同的或不同的进程向一个进程传递的事件。信号通常用来向一个进程通知异常事件。
术语:
产生(generate):当导致信号发生的事件出现时,比如硬件异常,就产生一个针对某个进程的信号
递送(deliver): 当进程对发给它的信号进程处理时,称为改信号被递送。
未决(pending):产生信号和递送信号之间的时间间隔称为信号未决。
部署(disposition):进程如何响应信号,进程可以忽略(ignore),执行默认操作或者使用自定义代码处理该信号。
linux 按照POSIX.1实现了可靠信号语义  (关于可靠信号与不可靠信号请参看AUPE)。linux 还实现了POSIX.4定义的real-time signals. 命令(kill –l) 可以显示Linux下所有信号。标准信号和实时信号的区别如下:
标准信号
实时信号
所有信号已经定义
未定义(前3个已被linuxpthreads-使用)
不能排队(递送前无论产生多少个信号,接受进程只收到一个)。
可以排队 (/proc/sys/kernel/rtsig-max 显示队列长度)
不能携带其他数据
可以携带其他数据
不能保证时序
保证时序
如果同时有标准信号和实时信号,Linux先处理标准信号
信号处理编程
最简单的信号处理
#include
#include
static void sig_usr(int);
int main()
{
if(signal(SIGUSR1,sig_usr)==SIG_ERR)
perror("cant catch sigusr1");
for(;;)
pause();
return 0;
}
static void sig_usr(int signo)
{
if(signo==SIGUSR1)
printf("recv SIGUSR1\n");
return ;
}
程序运行后,在shell里用命令(kill –USR1 pid ) 向程序发送信号,程序就会输出“recv SIGUSR1”。
这个程序显示了信号处理的基本过程,首先我们使用signal函数部署了SIGUSR1信号的处理函数,用我们定义的sig_usr代替了默认函数。然后调用pause使该进程挂起,等待该进程捕获信号。当进程收到了我们发送的USR1信号是sig_usr就被调用,pause返回,由于是死循环该进程又挂起。
这个程序有一点要说明,signal函数是不推荐使用的,因为signal是ANSI C定义的它对信号的处理的定义非常含糊,完全依赖于操作系统。有的系统(SVR4)实现的不可靠语义,而BSD实现的是可靠语义。Linux内核和libc4,5和SVR4相同,而glibc2和BSD相同,在我的机器上是可靠语义的,鬼才知道你机器上什么样子。但是因为它简单,所以都拿来入门。
要写程序还是要使用sigset_t相关函数和sigaction,sigprocmask,sigsuspend,sigpending.
sigset_t 有5个相关函数分别是:
sigemptyset(sigset_t *set); 初始化set指向的信号集,使其清除所有信号
sigfillset(sigset_t *set); 初始化set指向的信号集,使其包含所有信号。
sigaddset(sigset_t *set ,int signo); 将一个信号添加到现存信号集中
sigdelset(sigset_t *set,int signo); 从现存信号集中删除一个信号
sigismemberset(const sigset_t *set,int signo); 查询一个信号是否在信号集中。
sigaction 检查或修改与指定信号相关的处理动作。
sigprocmask 检查或修改与指定信号的屏蔽字
sigsuspend 在一个原子早在中实现恢复信号屏蔽字,然后使进程睡眠。
sigpending 返回对于调用进程被阻塞不能递送和当前未判决的信号集。
使用sigaction的程序:
#include
#include
static void sig_usr(int);
int main()
{
struct sigaction action;
sigemptyset(&action.sa_mask);
action.sa_handler=sig_usr;
sigaction(SIGUSR1,&action,NULL);
for(;;)
pause();
return 0;
}
static void sig_usr(int signo)
{
if(signo==SIGUSR1)
printf("recv SIGUSR1\n");
return ;
}
sigaction的原型是:
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact);
为指定的signum设置信号处理程序,act 是新的信号部署,oldact返回原来的信号部署。sigaction定义如下:
struct sigaction {
void  (*sa_handler)(int);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
sa_handler是函数指针,指向信号处理函数,也可以是SIG_DEL(设置成默认动作)或者SIG_IGN(忽略该信号)。sa_mask定义了在指向处理期间应该阻塞的其他信号集合的掩码。
sa_flags修正sa_handler的行为,可以下面几个值之一:
SA_NOCLDSTOP 进程忽略子进程产生的任何SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU
SA_ONESHOT 登记的自定义信号处理程序仅执行一次。执行完毕后恢复默认动作。
SA_RESTART 让可重启的系统调用起作用
SA_NOMASK 不避免在信号自己的处理程序中接受信号本身。
sa_restorer已经被废弃。
实时信号编程
// rtsig.c
#include
#include
static void sig_proc(int,siginfo_t *,void *);
int main()
{
struct sigaction action;
sigemptyset(&action.sa_mask);
action.sa_sigaction=sig_proc;
action.sa_flags=SA_SIGINFO;
sigaction(SIGRTMIN+5,&action,NULL);
pause();
return 0;
}
static void sig_proc(int signo,siginfo_t *info,void *p)
{
if(signo==SIGRTMIN+5)
{
int val=info->si_value.sival_int;
// int val=*((int *)info->si_value.sival_ptr);     will be error
printf("recv value %d\n",val);
printf("sender pid %d\n",info->si_pid);
printf("signo %d\n",info->si_signo);
}
return ;
}
信号处理程序有两点变化,首先信号处理的接口变化了添加了两个参数,重要的是第二个参数siginfo_t (定义参看man sigaction),它包含了很多信息,包括发送消息的PID,UID,信号携带的数据。其次sa_flags设成了SA_SIGINFO,只有这样处理程序才能接收到信号携带的数据。
// sendsig.c
#include
#include
int main()
{
int pid,val;
printf("input pid and value :");
scanf("%d %d",&pid,&val);
union sigval sigv;
sigv.sival_int=val; //sigv.sival_ptr=(void *)&val; will be error
sigqueue(pid,SIGRTMIN+5,sigv);
printf("pid is %d\n",getpid());
return 0;
}
这里发送信号改成了sigqueue, sigval携带信号的数据,参看(man sigqueue)。
程序运行结果:
sendmsg:
input pid and value :24479 232
pid is 24482
rtsig:
recv value 232
sender pid 24482
signo 39