对于信号不够了解的,可以查看这篇文章:链接
屏蔽字相关
可以通过信号屏蔽字来控制进程在处理信号时接受哪些信号,从而避免在处理信号时接收到新的信号。信号屏蔽字是一个位向量,其中每个位对应一个信号,如果该位被设置为1,则表示该信号被屏蔽,即进程不能接受该信号。
当进程处理一个信号时,该信号的信号屏蔽字会被设置为屏蔽该信号的位,从而确保在信号处理程序运行期间,该信号不会再次被送到该进程。在信号处理程序返回之前,进程会将信号屏蔽字恢复为原始值,从而允许接收其他信号。
如果进程需要在一段时间内阻止接收某些信号,可以使用sigprocmask()函数来修改信号屏蔽字。例如,可以在信号处理程序运行期间阻止某些信号,以确保在处理当前信号期间不会接收其他信号。然后在信号处理程序返回时,再次调用sigprocmask()函数,恢复原始信号屏蔽字。
进程捕捉函数sigaction也可以实现类似效果
具体实现
sigaction()函数是用于设置信号处理程序的函数,在Linux中,它是最常用的设置信号处理程序的函数之一。下面是sigaction()函数的函数原型:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
参数signum表示需要设置的信号编号,
参数act是一个指向struct sigaction结构的指针,用于设置新的信号处理程序,参数oldact是一个指向struct sigaction结构的指针,用于获取旧的信号处理程序。
struct 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);
};
其中,sa_handler和sa_sigaction用于设置信号处理程序,
sa_mask用于设置信号屏蔽字,仅仅在当前捕捉函数中有效,换句话说,仅仅在捕捉函数中暂时屏蔽其他信号,这样就可以防止被其他信号干扰
sa_flags用于设置信号处理程序的标志,当置为0时,表示屏蔽本信号,换句话说,再次收到本信号时会暂时屏蔽,后面再处理,也就是说可以不用刻意设置sa_mask
sa_restorer用于保存原始的信号处理程序。
1.#include <stdio.h>
2.#include <signal.h>
3.#include <stdlib.h>
4.#include <string.h>
5.#include <unistd.h>
6.#include <errno.h>
7.#include <pthread.h>
8.
9.void sys_err(const char *str)
10.{
11. perror(str);
12. exit(1);
13.}
14.
15.void sig_catch(int signo) // 回调函数
16.{
17. if (signo == SIGINT) {
18. printf("catch you!! %d\n", signo);
19. sleep(10);
20. }
21.
22. else if (signo == SIGQUIT)
23. printf("-----------catch you!! %d\n", signo);
24.
25. return ;
26.}
27.
28.int main(int argc, char *argv[])
29.{
30. struct sigaction act, oldact;
31.
32. act.sa_handler = sig_catch; // set callback function name 设置回调函数
33. sigemptyset(&(act.sa_mask)); // set mask when sig_catch working. 清空sa_mask屏蔽字, 只在sig_catch工作时有效
34. //sigaddset(&act.sa_mask, SIGQUIT);
35. act.sa_flags = 0; // usually use. 默认值,b表示屏蔽本信号
36.
37. int ret = sigaction(SIGINT, &act, &oldact); //注册信号捕捉函数
38. if (ret == -1)
39. sys_err("sigaction error");
40. ret = sigaction(SIGQUIT, &act, &oldact); //注册信号捕捉函数
41.
42. while (1);
43.
44. return 0;
45.}
linux正在处理信号时如果收到新的信号要怎么办
当Linux正在处理一个信号时,如果它收到了一个新的同类型信号(不同类型不影响),因为信号处理优先级最高
所以会根据信号的类型和进程的信号处理程序的设置来决定如何处理这个新信号。
Linux有两种类型的信号:可靠信号和非可靠信号。可靠信号会排队等待进程处理,而非可靠信号则可能会丢失。
结论
对于可靠信号,如果Linux正在处理一个信号并且收到了一个新的同类型信号,那么新的信号会被放入进程的信号队列中等待被处理。如果进程设置了合适的信号处理程序,它会按照队列的顺序处理每一个信号。
对于非可靠信号,如果Linux正在处理一个信号并且收到了一个新的同类型信号,那么新的信号可能会丢失。这是因为非可靠信号可能会被内核丢失或覆盖。如果一个进程需要处理非可靠信号,它应该尽可能快地完成当前的信号处理工作,以便能够及时地响应新的信号。
从结论上看,在捕捉函数执行期间,被屏蔽的非可靠信号多次发送,解除屏蔽后只处理一次!
信号捕捉特性:
1. 捕捉函数执行期间,信号屏蔽字 由 mask --> sa_mask , 捕捉函数执行结束。 恢复回mask
2. 捕捉函数执行期间,本信号自动被屏蔽(sa_flgs = 0).
3.捕捉函数执行期间,被屏蔽信号多次发送,解除屏蔽后只处理一次!
不可靠信号有哪些,为什么会丢失?
在Linux中,后32号信号是可靠的,
常见的非可靠信号有以下几种:
SIGALRM:定时器信号,会在一段时间后触发,通常用于超时检测和计时器。
SIGIO:异步输入输出信号,用于非阻塞IO操作。
SIGURG:紧急数据信号,用于TCP网络数据的处理。
SIGPIPE:管道破裂信号,用于检测管道是否破裂。
非可靠信号可能会丢失的原因是,当内核向进程发送一个信号时,如果该信号在处理之前被同类型的信号所覆盖,那么该信号就会丢失。这是因为内核只会为每个进程保留一个挂起的信号,如果该信号被覆盖,则后续的信号就会被忽略。
另外,如果进程在处理信号时进行了长时间的操作,可能会导致进程无法及时处理后续的信号,从而丢失信号。因此,在处理非可靠信号时,需要注意尽快完成当前的信号处理工作,以便能够及时响应后续的信号。
补充:信号操作函数
信号集操作函数:
sigset_t set; 自定义信号集。
sigemptyset(sigset_t *set); 清空信号集
sigfillset(sigset_t *set); 全部置1
sigaddset(sigset_t *set, int signum); 将一个信号添加到集合中
sigdelset(sigset_t *set, int signum); 将一个信号从集合中移除
sigismember(const sigset_t *set,int signum); 判断一个信号是否在集合中。 在--》1, 不在--》0
设置信号屏蔽字和解除屏蔽:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
how: SIG_BLOCK: 设置阻塞
SIG_UNBLOCK: 取消阻塞
SIG_SETMASK: 用自定义set替换mask。
set: 自定义set
oldset:旧有的 mask。
查看未决信号集:
int sigpending(sigset_t *set);
set: 传出的 未决信号集。