一、信号不是信号量
1.信号与信号量的区别
信号量是实现进程间同步与互斥的一种技术!
信号是一种软件中断,是一种事件通知机制!
信号通知进程发生了某个事件,打断进程当前的操作,去处理这个事件;
一个信号代表一个事件,且信号一定是可识别的。
2.信号分类
信号查看: kill -l
共有62种信号:
1-31号:继承之unix,是不可靠信号,非实时信号;
34-64号:后期扩展而来,是可靠信号,是实时信号。
二、信号生命周期
产生->注册->注销->处理,(阻塞)
1.信号产生
1.1 硬件产生
通过中断按键操作产生,
例如:ctrl+c / ctrl+z / ctrl+\
1.2 软件产生
通过调用系统函数产生,例如:
int kill(pid_t pid, int signum);
kill命令就是通过调用kill函数实现,kill函数可以给一个指定的进程发送指定的信号。
kill命令杀死进程的原理:给进程发送一个终止信号
int rease(int signum);
rease函数可以给当前进程发送指定的信号(自己给自己发送信号)。
void abort();
abort函数使当前进程接收到信号而异常终止。
unsigned int alarm(unsigned int seconds);
alarm函数可以设定一个时钟,相当于告诉内核在seconds秒后给当前进程发送SIGALRM信号,该信号的默认处理方式是终止当前进程。
2.信号注册
2.1 作用
让进程知道自己收到了某个信号
2.2 原理
在进程pcb中有一个pending位图(未决信号集合),也就是当前进程收到了,但是暂时还未被处理的信号集合。
信号的注册:就是在这个位图中标记信号值对应位置为1,并且在pcb中存在一个sigqueue链表,在这个链表中添加对应的信号节点。
2.3 可靠信号与非可靠信号的注册
1)可靠信号的注册:
无论当前是否有相同信号已经注册,都对位图置1,并在sigqueue链表添加一个节点。
2)非可靠信号的注册:
如果没有相同信号已经被注册,则注册;否则,直接返回。
非可靠信号注册存在的问题:可能存在事件丢失的情况。
3.信号注销
3.1 作用
在处理信号前,将信号存在的痕迹抹除。
3.2 原理
将sigqueue链表中对应信号的节点删除掉,并修改位图。
可靠信号:删除一个节点,当没有相同信号节点时,再去修改位图。
非可靠信号:删除信号的节点,直接修改位图置0。
4.信号处理
4.1 原理
处理一个信号,实际上就是调用这个信号的处理函数。
4.2 处理方式
默认处理方式:系统中已经定义好的处理方式。
忽略处理方式:对信号的处理就是忽略,什么都不做。
自定义处理方式:用户自己定义处理函数,然后替换掉内核中的默认处理方式。
系统接口:
typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); signum:指定的信号值 handler:处理方式 SIG_DFL:默认处理方式 SIG_IGN:忽略处理方式
返回值:
该信号原来的处理方式。
用户自定义:
void sigcb(int signum){……} 利用函数回调,处理对应信号: signal(1, sigcb); signal(2, sigcb); ……;
自定义处理方式的信号捕捉流程:
1.信号处理的时机:
程序从内核态切换回用户态之前,会先进行信号处理
2.信号的处理
信号的处理是一次性将所有的信号处理完毕之后,才会返回到用户态主控流程。
5.信号阻塞
5.1 功能
阻塞一个信号,指的就是暂时不去处理这个信号。
在pcb中,存在一个block信号阻塞集合,在这个集合中,标记哪个信号,就代表着要阻塞哪个信号,意味着当收到这个信号时,不去处理。
5.2操作接口
int sigprocmask(int how, sigset_t *set, sigset_t *old); how:要对阻塞集合进行的操作; SIG_BLOCK:相当于 block |= set 将set集合中的信号添加到block集合中。 SIG_UNBLOCK:相当于 block &= ~set 将set集合中的信号从block集合中移除。 SIG_SETMASK:相当于 block = set 将set集合中的信号设置为block集合信号 set:要操作的信号集合; old:用于保存修改前block集合中的数据。
辅助操作接口:
int sigemptyset(sigset_t *set);清空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);判断指定信号是否在集合中
5.3 两个特殊的信号
SIGKILL -9 & SIGSTOP -19;
这两个信号不会被阻塞、不会被忽略、不会被自定义。也就是说,这两个信号的处理方式无法被修改。
三、信号的应用
1. SIGCHLD信号
SIGCHLD:子进程退出后,通知父进程的信号。
常见应用:
signal(SIGCHLD, SIG_IGN);
手动设置处理方式为忽略处理方式。
此时手动设置为忽略处理方式:
意味着用户对此信号不关心,系统会自动处理,释放相关资源。
2. SIGPIPE信号
SIGPIPE:所有管道读端关闭后,继续write所触发的异常信号。
常见应用:
signal(SIGPIPE, SIG_IGN);
手动设置处理方式为忽略处理方式。
此时手动设置为忽略处理方式:
意味着用户对此信号不关心,系统会自动处理,释放相关资源。