1.基本概念 中断: 中断是系统对于异步事件的响应 中断信号 中断源 现场信息 中断处理程序 中断向量表 异步事件的响应:进程执行代码的过程中可以随时被打断,然后去执行异常处理程序 生活中的中断和计算机系统中的中断 1) 无中断生活场景 张三看书,厨房烧水 2)有中断的生活场景 张三看书,设置闹钟,厨房烧水。 闹钟发出中断信号,张三把书合好(第20页),去厨房把开水事情处理好,张三重新打开20页进行阅读。 3)计算机系统的中断场景 中断源发出中断信号,CPU判断中断是否屏蔽屏蔽、保护现场,cpu执行中断处理程序,cpu恢复现场,继续原来的任务。 4)中断的其他概念 中断向量表保存了中断处理程序的入口地址。 中断个数固定,操作系统启动时初始化中断向量表。 中断有优先级(有人敲门,有人打电话,有优先级) 中断可以屏蔽(张三可以屏蔽电话)。 中断分类: 硬件中断(外部中断) 外部中断是指由外部设备通过硬件请求的方式产生的中断,也称为硬件中断 软件中断(内部中断) 内部中断是由CPU运行程序错误或执行内部程序调用引起的一种中断,也称为软件中断。 x86平台INT指令 ARM软中断指令SWI 信号概念: 信号是UNIX系统响应某些状况而产生的事件,进程在接收到信号时会采取相应的行动。 信号是因为某些错误条件而产生的,比如内存段冲突、浮点处理器错误或者非法指令等 信号是在软件层次上对中断的一种模拟,所以通常把它称为是软中断 信号和中断的区别: 信号与中断的相似点: (1)采用了相同的异步通信方式; (2)当检测出有信号或中断请求时,都暂停正在执行的程序而转去执行相应的处理程序; (3)都在处理完毕后返回到原来的断点; (4)对信号或中断都可进行屏蔽。 信号与中断的区别: (1)中断有优先级,而信号没有优先级,所有的信号都是平等的; (2)信号处理程序是在用户态下运行的,而中断处理程序是在核心态下运行; (3)中断响应是及时的,而信号响应通常都有较大的时间延迟。2.信号名称及常用信号解释 kill –l 可以查看linux内核支持的信号 Man 7 signal 查看信号的默认动作、信号的含义 信号名称 描述 SIGABRT 进程停止运行 6 SIGALRM 警告钟 SIGFPE 算述运算例外 SIGHUP 系统挂断 SIGILL 非法指令 SIGINT 终端中断 2 SIGKILL 停止进程(此信号不能被忽略或捕获) SIGPIPE 向没有读者的管道写入数据 SIGSEGV 无效内存段访问 SIGQUIT 终端退出3 SIGTERM 终止 SIGUSR1 用户定义信号1 SIGUSR2 用户定义信号2 SIGCHLD 子进程已经停止或退出 SIGCONT 如果被停止则继续执行 SIGSTOP 停止执行 SIGTSTP 终端停止信号 SIGTOUT 后台进程请求进行写操作 SIGTTIN 后台进程请求进行读操作3.信号处理 进程对信号的三种响应 1)忽略信号 不采取任何操作、有两个信号不能被忽略:SIGKILL(9号信号)和SIGSTOP。 注意:为什么进程不能忽略SIGKILL、SIGSTOP信号。(如果应用程序可以忽略这2个信号,系统管理无法杀死、暂停进程,无法对系统进行管理。)。SIGKILL(9号信号)和SIGSTOP信号是不能被捕获的。 2)捕获并处理信号 内核中断正在执行的代码,转去执行先前注册过的处理程序。 3)执行默认操作 默认操作通常是终止进程,这取决于被发送的信号。 注意:信号的默认操作:通过 man 7 signal 进程查看4.signal信号安装函数 1)signal函数作用 (1):站在应用程序的角度,注册一个信号处理函数。 (2):忽略信号、设置信号默认处理信号的安装和恢复 typedef void (*__sighandler_t) (int); #define SIG_ERR ((__sighandler_t) -1) #define SIG_DFL ((__sighandler_t) 0) #define SIG_IGN ((__sighandler_t) 1) 2)函数原型 __sighandler_t signal(intsignum, __sighandler_t handler); 参数: 1)signal是一个带signum和handler两个参数的函数,准备捕捉或屏蔽的信号由参数signum给出,接收到指定信号时将要调用的函数由handler给出 2)handler这个函数必须有一个int类型的参数(即接收到的信号代码),它本身的类型是void 3)handler也可以是下面两个特殊值: SIG_IGN 屏蔽该信号 SIG_DFL 恢复默认行为5.信号发送 1)kill函数 kill基本用法: 发送信号的函数有kill和raise 区别:kill既可以向自身发送信号,也可以向其他进程发送信号; raise函数向进程自身发送信号。 intkill(pid_tpid, intsiq) int raise(intsigno) 参数组合情况解释: kill(pid_tpid, intsiq) pid>0 将信号sig发给pid进程 pid=0 将信号sig发给同组进程 pid=-1 将信号sig发送给所有进程,调用者进程有权限发送的每一个进程(除了1号进程之外,还有它自身) pid<-1 将信号sig发送给进程组是pid(绝对值)的每一个进程 注意:如果在fork之前安装信号,则子进程可以继承信号。 sleep函数几点说明: (1)sleep函数作用,让进程睡眠。 (2)能被信号打断,然后处理信号函数以后,就不再睡眠了。直接向下执行代码 (3)sleep函数的返回值,是剩余的秒数 2)raise函数 raise给自己发送信号。raise(sig)等价于kill(getpid(), sig); killpg给进程组发送信号。killpg(pgrp, sig)等价于kill(-pgrp, sig); sigqueue给进程发送信号,支持排队,可以附带额外数据信息。 3)alarm函数 unsigned int alarm(unsigned int seconds); alarm函数,设置一个闹钟延迟发送信号告诉linux内核n秒中以后,发送SIGALRM信号
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include <signal.h> 4 #include <unistd.h> 5 #include<errno.h> 6 void myhandle(int num) 7 { 8 printf("recv signal id num : %d \n", num); 9 alarm(1); 10 } 11 int main() 12 { 13 printf("main ....begin\n"); 14 if (signal(SIGALRM, myhandle) == SIG_ERR) 15 { 16 perror("func signal err\n"); 17 return 0; 18 } 19 alarm(1); 20 while(1) 21 { 22 pause(); 23 printf("pause return\n"); 24 } 25 return 0; 26 }6.信号的阻塞和未达 信号在内核中的表示: 执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。进程可以选择阻塞(Block)某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。 注意:阻塞和忽略是不同,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。信号在内核中的表示可以看作是这样的:
说明1)PCB进程控制块中结构体中有信号屏蔽状态字(block),信号未决状态字(pending)还有是否忽略标志; 说明2)信号屏蔽状态字(block),1代表阻塞、0代表不阻塞;信号未决状态字(pending)的1代表未决(表示有未达信号),0代表信号可以抵达了; 说明3)向进程发送SIGINT,内核首先判断信号屏蔽状态字是否阻塞,信号未决状态字(pending相应位制成1;若阻塞解除,信号未决状态字(pending)相应位制成0;表示信号可以抵达了。 说明4)block状态字、pending状态字 64bit;//socket select 说明5)block状态字用户可以读写,pending状态字用户只能读;这是信号设计机制。
信号集操作函数(状态字表示) intsigemptyset(sigset_t *set);把信号集清空64bit/8=8个字节 intsigfillset(sigset_t *set);把信号集置成1 intsigaddset(sigset_t *set, intsigno);根据signo,把信号集中的对应bit置成1 intsigdelset(sigset_t *set, intsigno);根据signo,把信号集中的对应bit置成0 intsigismember(constsigset_t *set, intsigno);//判断signo是否在信号集中 sigprocmask读取或更改进程的信号屏蔽状态字(block) int sigprocmask(int how, constsigset_t *set, sigset_t *oset); 返回值:若成功则为0,若出错则为-1 如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。
SIG_BLOCK ,讲信号集set添加到进程block状态字中。
sigpending获取信号未决状态字(pending)信息:int sigpending(sigset_t *set);
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include <signal.h> 4 #include <unistd.h> 5 #include<errno.h> 6 void handler(int sig) 7 { 8 if (sig == SIGINT) 9 printf("recv a sig=%d\n", sig); 10 else if (sig == SIGQUIT) 11 { 12 sigset_t uset; 13 sigemptyset(&uset); 14 sigaddset(&uset, SIGINT); 15 //ctr + \ 用来触发 SIGINT 信号 16 //解除阻塞 17 sigprocmask(SIG_UNBLOCK, &uset, NULL); 18 } 19 } 20 21 void printsigset(sigset_t *set) 22 { 23 int i; 24 for (i=1; i<NSIG; ++i) 25 { 26 if (sigismember(set, i)) 27 putchar('1'); 28 else 29 putchar('0'); 30 } 31 printf("\n"); 32 } 33 // 连续的按ctrl+c键盘,虽然发送了多个SIGINT信号,但是因为信号是不稳定的,只保留了一个。 34 //不支持排队 35 int main(int argc, char *argv[]) 36 { 37 sigset_t pset; //用来打印的信号集 38 sigset_t bset; //用来设置阻塞的信号集 39 40 sigemptyset(&bset); 41 sigaddset(&bset, SIGINT); 42 43 if (signal(SIGINT, handler) == SIG_ERR) 44 ERR_EXIT("signal error"); 45 46 if (signal(SIGQUIT, handler) == SIG_ERR) 47 ERR_EXIT("signal error"); 48 49 //读取或更改进程的信号屏蔽字这里用来阻塞ctrl+c信号 50 //ctrl+c信号被设置成阻塞,即使用户按下ctl+c键盘,也不会抵达 51 sigprocmask(SIG_BLOCK, &bset, NULL); 52 53 for (;;) 54 { 55 //获取未决字信息 56 sigpending(&pset); 57 58 //打印信号未决sigset_t字 59 printsigset(&pset); 60 sleep(1); 61 } 62 return 0; 63 }7.sigaction函数 包含头文件<signal.h> 功能:sigaction函数用于改变进程接收到特定信号后的行为。 原型:int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact); 参数: 该函数的第一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误) 第二个参数是指向结构sigaction的一个实例的指针,在结构sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理 第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。 返回值:函数成功返回0,失败返回-1 sigaction结构体: 第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些函数等等 struct sigaction { void (*sa_handler)(int); //信号处理程序不接受额外数据 void (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序能接受额外数据,和sigqueue配合使用 sigset_t sa_mask; // int sa_flags; //影响信号的行为SA_SIGINFO表示能接受数据 void (*sa_restorer)(void); //废弃 }; 注意:回调函数句柄sa_handler、sa_sigaction只能任选其一。 sigaction的函数注册信号,基本用法代码:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include <signal.h> 4 #include <unistd.h> 5 #include<errno.h> 6 void handler(int sig) 7 { 8 printf("recv a sig=%d\n", sig); 9 } 10 11 __sighandler_t my_signal(int sig, __sighandler_t handler) 12 { 13 struct sigaction act; 14 struct sigaction oldact; 15 act.sa_handler = handler; 16 sigemptyset(&act.sa_mask); 17 act.sa_flags = 0; 18 19 if (sigaction(sig, &act, &oldact) < 0) 20 return SIG_ERR; 21 22 return oldact.sa_handler; 23 } 24 25 int main(int argc, char *argv[]) 26 { 27 struct sigaction act; 28 sigset_t sa_mask; 29 30 act.sa_handler = handler; 31 act.sa_flags = 0; 32 sigemptyset(&act.sa_mask); 33 34 //测试信号安装函数 35 sigaction(SIGINT, &act, NULL); 36 37 //模拟signal函数 38 //my_signal(SIGINT, handler); 39 40 for (;;) 41 { 42 pause(); 43 } 44 return 0; 45 }如上图修改对应地方编译后,都可以实现效果
测试sigaction结构体第三个参数sigset_tsa_mask的作用,代码如下:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include <signal.h> 4 #include <unistd.h> 5 #include<errno.h> 6 /* 7 struct sigaction { 8 void (*sa_handler)(int); 9 void (*sa_sigaction)(int, siginfo_t *, void *); 10 sigset_t sa_mask; 11 int sa_flags; 12 void (*sa_restorer)(void); 13 } */ 14 //测试sigaction结构体第三个参数sigset_t sa_mask的作用 15 //作用sigaddset(&act.sa_mask, SIGQUIT); 加入到sa_mask中的信号,被阻塞(信号处理函数执行的过程中被阻塞)。 16 //注意:SIGQUIT信号最终还会抵达 17 void handler(int sig); 18 int main(int argc, char *argv[]) 19 { 20 struct sigaction act; 21 act.sa_handler = handler; 22 23 sigemptyset(&act.sa_mask); 24 sigaddset(&act.sa_mask, SIGQUIT); 25 act.sa_flags = 0; 26 27 if (sigaction(SIGINT, &act, NULL) < 0) 28 { 29 perror("sigaction error"); 30 } 31 32 for (;;) 33 { 34 pause(); 35 } 36 return 0; 37 } 38 39 void handler(int sig) 40 { 41 printf("recv a sig=%d 信号处理函数执行的时候,阻塞sa_mask中的信号\n", sig); 42 sleep(5); 43 }结论:sigaddset(&act.sa_mask, SIGQUIT) 加入到sa_mask中的信号,被阻塞(信号处理函数执行的过程中被阻塞),SIGQUIT信号最终还会抵达;
8.sigqueue函数 功能:新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用。 注意:和kill函数相比int kill(pid_tpid, intsiq)多了参数 原型:int sigqueue(pid_t pid, int sig, const union sigval value); 参数: sigqueue的第1个参数是指定接收信号的进程id,第2个参数确定即将发送的信号,第3个参数是一个联合数据结构union sigval,指定了信号传递的参数,即通常所说的4字节值。 union sigval { int sival_int; void *sival_ptr; }; 返回值:成功返回0,失败返回-1 sigqueue()比kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号,而不能发送信号给一个进程组。9.其他介绍 三种睡眠函数: unsigned int sleep(unsigned int seconds);秒 若被中断打断,返回剩余时间 int usleep(useconds_tusec);微妙 若被中断打断,返回剩余时间 int nanosleep(const struct timespec *req, struct timespec *rem);纳秒时间 要睡眠的时间req;剩余睡眠时间,如果要中断,通过rem返回过来。 三种时间结构: time_t秒 struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */微妙 }; struct timespec {纳秒 time_ttv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ }; setitime函数:间隙性产生 包含头文件<sys/time.h> 功能setitimer()比alarm功能强大,支持3种类型的定时器 原型:int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value); 参数: 第一个参数which指定定时器类型 第二个参数是结构itimerval的一个实例,结构itimerval形式 第三个参数可不做处理。 返回值:成功返回0失败返回-1 ITIMER_REAL:经过指定的时间后,内核将发送SIGALRM信号给本进程 ITIMER_VIRTUAL :程序在用户空间执行指定的时间后,内核将发送SIGVTALRM信号给本进程 ITIMER_PROF :进程在内核空间中执行时,时间计数会减少,通常与ITIMER_VIRTUAL共用,代表进程在用户空间与内核空间中运行指定时间后,内核将发送SIGPROF信号给本进程.
转载于:https://www.cnblogs.com/liunianshiwei/p/6109689.html