信号是响应某些状况而产生的事件,进程在接收到信号时会采取相应的行动。某些状况就是指某些条件错误,如内存段冲突、浮点处理器错误或者非法指令等。信号是在软件层次上对中断的一种模拟,所以信号也称为是软中断
信号与中断的相似点:
1,都采用相同的额异步通信方式
2,当检测出有信号或中断请求时,都暂停证在执行的程序而转去执行相应的处理程序
3,都在处理完毕之后返回到原来的断点
4,对信号或中断都可进行屏蔽
信号与中断的区别:
1,中断有优先级,而信号没有,所有的信号都是平等的
2,信号处理程序是在用户态下运行的,而中断处理程序是在核心态下运行的
3,中断响应是及时的,而信号响应通常都有较大的事件延迟
进程对信号的三种响应:
1,忽略信号,除了SIGKILL
,SIGSTOP
2,捕获并处理信号,内核中断正在执行的代码,转去执行先前注册处
3,执行默认操作,默认操作通常是终止进程,取决于被发送的信号
中断一个中断处理程序到系统中__sighandler_t signal(iny signum, __sighandler_t handler);
signal是一个带signum和handler两个参数的函数,准备捕捉或屏蔽的信号由signum给出,接收到指定信号时将要调用的函数由handler给出;
handler这个函数必须有一个int类型的参数,即接收到的信号代码,当然handler也可以是SIG_IGN(屏蔽该信号)、SIG_DFL(恢复默认行为)这两个特殊值
超简单例子
SIGINT对应键盘ctrl+c发出的信号
#include
2 #include<sys/stat.h>
3 #include<sys/wait.h>
4 #include<sys/types.h>
5 #include<fcntl.h>
6
7 #include<stdlib.h>
8 #include<stdio.h>
9 #include<errno.h>
10 #include<string.h>
11 #include<signal.h>
12
13 #define ERR_EXIT(m) \
14 do \
15 { \
16 perror(m); \
17 exit(EXIT_FAILURE); \
18 }while(0)
19
20 void handler(int sig);
21
22 int main(int argc, char *argv[])
23 {
24 signal(SIGINT, handler);//注册一个ctrlc的信号,如果捕捉到了这个信号,就去执行handler函数
25 for(;;);
26 return 0;
27 }
28 void handler(int sig)
29 {
30 printf("recv a sig = %d\n",sig);
31 }
1,执行信号的处理动作称为信号递达,信号产生到递达之间的状态称为信号未决,进程可以选择阻塞某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
信号集操作函数:
#include
int sigemptyset(sigset_t* set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t * set, int_signo);
int sigdelset(sigset_t* set, int signo);
int sigismember(const sigset_t *set, int signo);
获取/改变 进程中的信号屏蔽字:int sigprocmask(int how, const sigset_t *set, sigset_t *oset)
第三个参数是改变之前的信号屏蔽字的状态,第一个参数改变方式有三种SIG_BLOCK
、SIG_UNBLOCK
、SIG_SETMASK
sigpending(&set)
函数可以获取进程中的未决信号集合保存到set中
例子信号从产生到递达的一个过程
bset将SIG_INIT进行阻塞,但接收到SIG_QUIT即ctrl/时取消阻塞,始终用函数将未决信号集打印出来
设置阻塞或者取消阻塞都是要用自定义的一个set来进行sigprocmask
来设置的
16 void printsigset(sigset_t *set)//打印未决信号集内容
17 {
18 int i;
19 for(i=1; i<NSIG; i++)
20 {
21 if(sigismember(set, i))
22 putchar('1');
23 else
24 putchar('0');
25 }
26 printf("\n");
27 }
28 void handler(int sig);
29 int main(int argc, char*argv[])
30 {
31 sigset_t pset;
32 sigset_t bset;sigemptyset(&bset);sigaddset(&bset, SIGINT);
33 if(signal(SIGINT, handler) == SIG_ERR)
34 ERR_EXIT("signal error");
35 if(signal(SIGQUIT, handler) == SIG_ERR)//if ctrl/, unblock
36 ERR_EXIT("signal error");
37 sigprocmask(SIG_BLOCK, &bset, NULL);//use bset
38 for(;;)
39 {
40 sigpending(&pset);
41 printsigset(&pset);
42 sleep(1);
43 }
44
45
46 return 0;
47 }
48
49 void handler(int sig)
50 {
51 if(sig == SIGINT)
52 printf("recv a sig = %d\n", sig);
53 else if(sig == SIGQUIT)
54 {
55 sigset_t uset;
56 sigemptyset(&uset);
57 sigaddset(&uset, SIGINT);
58 sigprocmask(SIG_UNBLOCK, &uset, NULL);
59 }
60 }
int sigaction(int signum, const struct sigaction *act, const struct sigaction *old)
第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些信号等
struct sigaction{
void (*sa_handler)(int);//1
void (*sa_sigaction)(int, siginfo_t*, void*);//2,只能选一个,一般就选1
sigset_t sa_mask;//指定信号屏蔽字
int sa_flags;//改变信号的一些行为
void(*sa_restorer)(void);//废弃
}
handler包含在sigaction这个结构体中
void handler(int sig);
29 int main(int argc, char*argv[])
30 {
31 struct sigaction act;
32 act.sa_handler = handler;
33 sigemptyset(&act.sa_mask);
34 act.sa_flags = 0;
35
36 if(sigaction(SIGINT, &act, NULL)<0)
37 ERR_EXIT("sigaction error\n");
38 for(;;)
39 pause();
40 return 0;
41 }
42 void handler(int sig)
43 {
44 printf("recv a sig = %d\n", sig);
45 }
关于sa_mask,指定信号掩码,在一个信号执行过程中,sa_mask中的信号发生了也不会递达,会被阻塞直到真正该执行的信号函数结束
在sa_mask中加入SIGQUIT信号,那么在SIGINT捕捉到执行handler的过程中,发生sa_mask中的信号也会被屏蔽
int main(int argc, char*argv[])
30 {
31 struct sigaction act;
32 act.sa_handler = handler;
33 sigemptyset(&act.sa_mask);
34 sigaddset(&act.sa_mask, SIGQUIT);//
35 act.sa_flags = 0;
36
37 if(sigaction(SIGINT, &act, NULL)<0)
38 ERR_EXIT("sigaction error\n");
39 for(;;)
40 pause();
41 return 0;
42 }
43 void handler(int sig)
44 {
45 printf("recv a sig = %d\n", sig);
46 sleep(5);
47 }
sa_flags的用法有需要再补吧233
三种不同精度的睡眠
1,unisigned int sleep(uninsigned int seconds);
,返回剩余秒数while(n = sleep(n))
2,int usleep(useconds_t usec);
微秒
3,int nanosleep(const struct timepec *req, struct timespec *rem);
纳秒,第二个参数时剩余睡眠时间
三种不同精度就对应三种时间结构
getitimer()/setitimer()功能描述:
获取或设定间歇计时器的值。系统为进程提供三种类型的计时器,每一类以不同的时间域递减其值。当计时器超时,信号被发送到进程,之后计时器重启动。
用法:
#include
int getitimer(int which, struct itimerval *value);
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);
参数:
which:间歇计时器类型,有三种选择
ITIMER_REAL //数值为0,计时器的值实时递减,发送的信号是SIGALRM。
ITIMER_VIRTUAL //数值为1,进程执行时递减计时器的值,发送的信号是SIGVTALRM。
ITIMER_PROF //数值为2,进程和系统执行时都递减计时器的值,发送的信号是SIGPROF。