Linux 对信号的总结
对信号本质的理解:
类似于中断,区别在于中断是由硬件产生的,而信号是由软件实现的。
信号的来源:
触发硬件(触发键盘,或是硬件故障);软件信号函数kill 、alarm、setitimer、sigqueue 等函数。
信号的分类:
可靠信号与不可靠信号,实时信号与非实时信号;
不可靠信号:
sigrtmin前的信号称为不可靠信号,在早期这段信号可能做出错误的反应,或是丢失。因此对此段信号成为不可靠信号。
可靠信号:
在sigrtmin与sigrtmax之间的信号称做可靠信号,可靠信号是为了防止信号丢失的。这些信号可以排队处理。
实时信号与非实时信号:
非实时信号都不支持排队,都是不可靠信号;实时信号都是支持排队的,都是可靠信号;
对信号的响应:
响应的三种方式
(1)忽略信号
有两个特殊的信号sigkill 和sigstop信号不能被忽略。
(2)捕捉信号
给对应的信号绑定响应的处理函数,带到信号产生时,执行对应的函数。
(3)执行缺省信号
进程对实时信号的缺省反应时进程的终止。
信号的发送:
发送信号的函数有,kill(), alarm(),raise(),setitimer();
(1)kill(int pid, int signal);
pid参数 | 信号的就收进程 |
---|---|
pid>0 | 进程的id为pid的进程 |
pid=0 | 同一个进程组 |
pid<0 && pid!=-1 | 进程组id为pid绝对值的所有进程 |
pid=-1 | 发送至所有id大于1的进程 |
参数介绍:
pid为进程号,singnal为信号值。
kill常用于pid>0的信号处理,调用成功返回0,否则返回-1。
(2)alarm(unsigned int seconds)
专门为sigalrm信号而设函数,seconds表示时间,此函数意味着在seconds秒后向sigalrm信号发送消息。
进程调用alarm后,以前的alarm()调用都将无效。若调用alarm()前,进程中已经设置了闹钟,则返回上一个闹钟生于的时间,否则返回0;
eg:
#include<signal.h>
#include<stdio.h>
int main(void)
{
printf("first time return:%d\n",alarm(4));
sleep(1);
printf("after sleep(1),remain:%d\n",alarm(2));
printf("renew alarm,remain:%d\n",alarm(1));
}
//运行结果为
first time return:0
after sleep(1),remain:3
renew alarm,remain:2
(3)raise(int signal);
此函数时向本进程发送signal信号的,signal为即将发送的信号值。调用成功返回0;否则返回 -1。
(4)setitimer()函数
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
结构体介绍:
struct itimerval
{
struct timeval it_interval;//间隔时间
struct timeval it_value; //初始时间
};
struct timeval
{
long tv_sec; //秒
long tv_usec; //微妙
};
参数描述:
which:表示定时器类型,setitimer有三种定时器类型。
itimer_real : 以系统真实的时间来计算,它送出sigalrm信号。
itimer_virtual : 设定程序执行时间;经过指定的时间后,内核将发送sigvtalrm信号给本进程;
itimer_prof : 设定进程执行以及内核因本进程而消耗的时间和,经过指定的时间后,内核将发送itimer_virtual信号给本进程;
it_interval指定间隔时间,it_value指定初始定时时间。如果只指定it_value,就是实现一次定时;如果同时指定 it_interval,则超时后,系统会重新初始化it_value为it_interval,实现重复定时;
eg:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>
void sigroutine(int signo) {
switch (signo) {
case sigalrm:
printf("catch a signal -- sigalrm\n");
break;
case sigvtalrm:
printf("catch a signal -- sigvtalrm\n");
break;
}
}
int main(int argc, char ** argv) {
struct itimerval value,ovalue,value2;
printf("process id is %d\n",getpid());
signal(sigalrm, sigroutine); //为sigalrm信号绑定sigroutine函数
signal(sigvtalrm, sigroutine); //为sigvtalrm信号绑定sigroutine函数
value.it_value.tv_sec = 1;//设定起始时间
value.it_value.tv_usec = 0;
value.it_interval.tv_sec = 1;//设定终止时间
value.it_interval.tv_usec = 0;
setitimer(itimer_real, &value, &ovalue);
value2.it_value.tv_sec = 0;
value2.it_value.tv_usec = 500000;
value2.it_interval.tv_sec = 0;
value2.it_interval.tv_usec = 500000;
setitimer(itimer_virtual, &value2, &ovalue);
for (;;) ;
}
//运行结果为
process id is 3136
catch a signal -- sigvtalrm
catch a signal -- sigalrm
catch a signal -- sigvtalrm
catch a signal -- sigvtalrm
catch a signal -- sigalrm
catch a signal -- sigvtalrm
catch a signal -- sigvtalrm
catch a signal -- sigalrm
catch a signal -- sigvtalrm
catch a signal -- sigvtalrm
catch a signal -- sigalrm
catch a signal -- sigvtalrm
catch a signal -- sigvtalrm
catch a signal -- sigalrm
catch a signal -- sigvtalrm
catch a signal -- sigvtalrm
catch a signal -- sigalrm
catch a signal -- sigvtalrm
catch a signal -- sigvtalrm
安装信号:
由signal()、sigaction()处理:
(1)signal(int signum, sighandler_t handler);
此函数的作用是,为handler函数,或处理过程绑定一个信号,每当出现信号后,进行handler处理。
signum:所指信号;
handler:处理过程,可以时函数的指针。这个也可设置为"sig_ign",表示忽略此信号,"sig_dfl"表示以系统默认方式处理此信号。(注意:sigkill sigstop不可被安装)
此函数的例子可参考上一个例子。
(2)sigaction();
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
signum:指定的信号(除sigkill,sigstop外)
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);
};
(1)结构体中的sa_restorer已经不使用可以忽略。
(2)sa_handler指定的处理函数只有一个参数类似于使用signal()函数
(3)sa_sigaction也是指定信号的处理函数,不过可以带三个参数:
第一个参数为信号值;
第二个参数是指向siginfo_t结构的指针;
第三个参数没有使用;
siginfo_t结构体如下:
siginfo_t
{
int si_signo; /* signal number */
int si_errno; /* an errno value */
int si_code; /* signal code */
int si_trapno; /* trap number that caused
hardware-generated signal
(unused on most architectures) */
pid_t si_pid; /* sending process id */
uid_t si_uid; /* real user id of sending process */
int si_status; /* exit value or signal */
clock_t si_utime; /* user time consumed */
clock_t si_stime; /* system time consumed */
sigval_t si_value; /* signal value */
int si_int; /* posix.1b signal */
void *si_ptr; /* posix.1b signal */
int si_overrun; /* timer overrun count; posix.1b timers */
int si_timerid; /* timer id; posix.1b timers */
void *si_addr; /* memory location which caused fault */
long si_band; /* band event (was int in
glibc 2.3.2 and earlier) */
int si_fd; /* file descriptor */
short si_addr_lsb; /* least significant bit of address
(since linux 2.6.32) */
};
(4)sa_mask指定在信号处理程序执行过程中,哪些信号应当被阻塞。缺省情况下当前信号本身被阻塞,防止信号的嵌套发送,除非指定sa_nodefer或者sa_nomask标志位。
注:请注意sa_mask指定的信号阻塞的前提条件,是在由sigaction()安装信号的处理函数执行过程中由sa_mask指定的信号才被阻塞。
(5)sa_flags标志位:
当sa_flags设置为sa_siginfo时,示信号附带的参数可以传递到信号处理函数中。即使sa_sigaction指定信号处理函数,如果不设置sa_siginfo,信号处理函数同样不能得到信号传递过来的数据,在信号处理函数中对这些信息的访问都将导致段错误。
信号,介绍先到此,如若觉得又不对的地方,请指出,共同进步谢谢!
参考博客有: