信号是为了通知进程发生了某个事件,因为事件比较紧急,因此会打断当前进程正在执行的工作,然后去处理事件,事件处理完毕之后进程回到原先运行位置继续运行
信号更多的是通知事件发生,如红绿灯
在LInux下,用户输入命令在Shell下启动一个前台进程,而当按下ctrl+C时,产生一个硬中断,如果说CPU当前正在执行这个进程的代码,则该进程的用户空间代码暂停执行,CPU从用户态切换到内核态处理硬件中断(终端驱动程序将 ctrl+c 解释成一个SIGINT信号,记在其进程的PCB中(也可以说是发送了一个SIGINT信号给该进程)),而当要从内核返回到进程的用户空间代码继续执行之前,首先处理PCB中记录的信号,此时会发现有一个SIGINT信号待处理,而这个信号的默认处理动作是终止进程,所以直接终止进程而不再返回它的用户空间代码执行
信号产生之后第一时间也不是直接处理而是先存储下来,处理信号
信号的生命周期:
信号的产生 =》信号的注册 =》信号的注销 =》(信号的阻塞(只是不处理)) =》信号的处理
信号处理常见方式:
1.忽略此信号
2.执行该信号的默认处理动作
3.提供一个对信号处理的函数,要求内核在处理该信号时切换到用户状态执行这个函数(捕捉信号)
信号分了两类:
1.非实时信号(不可靠信号)1~31
- 实时信号 (可靠信号) 34~64
信号的产生:
1.通过硬件中断产生 (ctrl+c)
2.程序异常产生 SIGFPE SIGSEGV
3.软件条件(代码)产生
软件条件产生:
1.直接使用kill命令:
kill并不是真的杀死进程,而是向一个进程发送一个信号(SIGTERM)
kill -15 PID kill -15 PID
2.kill接口
int kill(pid_t pid, int sig); 向指定的进程发送指定的信号
int raise(int sig); 发送一个信号给自身
void abort(void); 使当前进程接受到信号而异常终止(像exit一样总会成功,所以没有返回值)
unsigned int alarm(unsigned int seconds);
在second秒之后,给进程发送一个STGALRM信号,在函数中可以多次调用
如:设置一个定时器,取消上一个定时器,并且返回上一个定时器剩余时间
信号的注册:
信号的注册(给一个进程发送信号),就是修改这个进程pcb中关于信号的pending位图,将相应的信号位置1
信号的阻塞:
暂时不处理信号,并不是不接收信号,实际是暂时阻止信号的递达
原理:要阻塞一个信号,那么就是修改pcb中关于信号的block位图,将相应的信号位置1,这个位图就像是 一个备注说明如果接收到这个信号暂时不处理
注意:
1.实际执行信号的处理动作称之为信号递达(动作)
2.信号从产生到递达之间的状态,称之为信号未决(状态)
3.进程可以选择阻塞某个信号
4.被阻塞的信号产生时将保持在未决状态,知道进程解除对该信号的阻塞才执行递达动作
5.阻塞和忽略时不同的,阻塞就不会递达,而忽略则是在递达之后可选的一种处理的动作
sigprocmask:
int sigprocmask(int how, const sigset_t set, sigset_t oldset);
调用sigprcmask()接口,要么阻塞函数,要么对信号进行解除阻塞
set:要阻塞或解除阻塞的集合
oldset:保存原先阻塞集合中的信号
如果oset非空,则读取进程当前的信号屏蔽字通过oset传出;
若set非空,则根据how参数指示更改进程的信号屏蔽字;(解除阻塞或者设置阻塞)
若oset和set均非空,则将原来的信号屏蔽字备份到oset中,然后根据how参数更改信号屏蔽字
how参数:SIGBLOCK
SIG_UNBLOCK
SIG_SETMASK
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);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42/* 这是一个演示信号阻塞的demo
2 */
3
4 #include<stdio.h>
5 #include<unistd.h>
6 #include<signal.h>
7
8 int main()
9 {
10 //我要阻塞这个集合中的信号
11 sigset_t new_block;
12 //用于保存原来阻塞集合中的信号,防止后续要将阻塞集合还原回去
13 sigset_t old_block;
14
15 //int sigemptyset(sigset_t *set);
16 //清空一个信号集合
17 sigemptyset(&new_block);
18 //int sigfillset(sigset_t *set);
19 //将所有的信号添加到set集合中
20 // int sigaddset(sigset_t *set, int signum);
21 //添加指定的单个信号到set集合中
22 //int sigdelset(sigset_t *set, int signum);
23 //从集合中移除一个信号
24 //int sigismember(const sigset_t *set, int signum);
25 //判断一个信号是否在一个集合中
26 sigfillset(&new_block);
27 //int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
28 //阻塞信号或解除阻塞
29 //SIGBLOCK 阻塞集合中的信号
30 //SIG_UNBLOCK 对集合中的信号解除阻塞
31 //SIG_SETMASK 将集合中的信号设置到阻塞集合中
32 //set:要阻塞或解除阻塞的集合
33 //oldset:保存原先阻塞集合中的信号
34 sigprocmask(SIG_BLOCK,&new_block,&old_block);
35 //不按回车不往下走
36 getchar();
37 //解除阻塞
38 sigprocmask(SIG_UNBLOCK,&new_block,NULL);
39 //将原先阻塞集合中的信号还原回去
40 //sigprocmask(SIG_BLOCK,&old_block,NULL);
41 return 0;
42 }
有两个信号是不会被阻塞的:1.SIGKILL 2. SIGSTOP
sigpending:
获取一个未决信号
int sigpending(sigset_t *set);
(如果调用了sigpending接口,并且传入了一个集合地址,那么就会将pending集合中所有数据返回回来,相当于有哪些信号没有被处理则会返回回来)
信号注销:
就是从pending集合中将即将要处理的信号置0(从pcb的pending集合中移除)
非可靠信号注册就是将相应pending位图置1,然后添加一个sigqueue链表结构到链表中(一意味着这个信号来过),之后如果有相同信号到来,一看位图已经置1了,就不做操作了(意味着后来的信号在前一个信号未处理之前不会重复注册,代表丢了)
可靠信号注册不管之前有没有注册都要置1 ,并且添加节点到链表中,所以不会丢掉信号
非可靠注销就是删除链表结点,相应位图置0
可靠信号注销就是删除节点,判断是否有相同信号节点,如果没有位图置0,如果有就不置0,位图该位置还为1,下次会还处理这个信号
struct sigqueue;
信号处理:
1.忽略此信号——-直接将信号丢掉
2.执行该信号的默认处理动作——-按照操作系统中对信号事件的既定处理方式
3.自定义———-用户自己提供一个对信号处理的函数,要求内核在处理该信号时切换到用户状态执行这个函数(捕捉信号)
哪些接口可以供我们改变处理方式:
1.signal
#include <signal.h>
typedef void (sighandler_t)(int); //函数指针的类型定义
sighandler_t signal(int signum, sighandler_t handler);
signum:信号的ID,指定要改变的信号
sighandler_t handler:*信号的处理方式
SIG_IGN(忽略信号) SIG_DFL(默认处理方式) or 自定义1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48/*
2 * 这是一个演示signal接口修改信号处理方式的demo
3 * 信号的处理方式有三种:
4 * 忽略
5 * 默认
6 * 自定义
7 */
8
9
10
11
12
13
14 void sigcb(int signo)
15 {
16 printf("receive signo:%d\n",signo);
17 /* if(signo == SIGINT ) //因为可能有很多信号来调用这个函数,所以需要做出判断
18 {
19 }
20 else
21 {
22 }
23 */
24 return;
25 }
26
27
28 int main()
29 {
30 // sighandler_t signal(int signum, sighandler_t handler);
31 //signum:信号的编号
32 //handler:处理方式
33 // SIG_IGN 忽略
34 // SIG_DFL 默认
35 int i = 0;
36 //signal(SIGINT,SIG_IGN); //提前备注,当这个信号到来时忽略它
37 signal(SIGINT,sigcb); //自定义方式处理
38 while(1)
39 {
40 printf("--------------\n");
41 kill(getpid(),SIGINT);
42 if(++i == 3)
43 {
44 signal(SIGINT,SIG_DFL);
45 }
46 }
47 return 0;
48 }