目录
一、验证环境
名称 | 值 |
CPU | Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz |
操作系统 | CentOS Linux release 7.9.2009 (Core) |
内存 | 5G |
逻辑核数 | 6 |
二、介绍
信号是软件中断,它提供了一种处理异步事件的方法,很多比较重要的程序都需要处理信号。信号也是异步事件的经典实例,产生信号的事件对于进程是随机出现的。当接收到某个信号时,我们可以告知内核按照三种方法:忽略此信号、捕捉信号、执行系统默认动作,进行信号的处理。
本文只是简单介绍信号,详细的还是要看《Unix环境高级编程》这本书,书山有路勤为径,学海无涯苦作舟。
三、信号宏
参考头文件#include <bits/signum.h>
宏 | 值 | 描述 |
SIGHUP | 1 | 如果终端接口检测到一个连接断开,则将此此信号送给与该终端相关的控制进程(会话首进程)。此信号被送给session结构中s_leader字段所指向的进程。仅当终端的CLOCAL标志没有设置时,在上述条件下才产生此信号。注意,接到此信号的会话首进程可能在后台,这区别于由终端正常产生的几个信号(中断、退出和挂起),这些信号总是传递给前台进程组。 如果会话首进程终止,也产生此信号。在这种情况,此信号送给前台进程组中的每一个进程。 通常用此信号通知守护进程,再次读取他们的配置文件。选用SIGHUP的理由是,守护进程不会有控制终端,通常绝不会接收到这种信号。 |
SIGINT | 2 | 当用户按中断键(Delete或Ctrl+c)时,终端驱动程序产生此信号并发送至前台进程组的每一个进程。当一个进程在运行时失控,特别是它正在屏幕上产生大量不需要的输出时,常用此信号终止它。 |
SIGQUIT | 3 | 当用户在终端上按退出键(Ctrl+\)时,中断驱动程序产生此信号,并发送给前台进程组中的所有进程,此信号不仅终止前台进程组,同时产生一个core文件。 |
SIGILL | 4 | 进程已执行一条非法硬件指令。 |
SIGTRAP | 5 | 指一个实现定义的硬件故障,此信号名来自于PDP-11的TRAP指令。当执行断点指令时,实现常用此信号将控制转移至调试程序。 |
SIGABRT | 6 | 调用abort函数产生的信号。 |
SIGIOT | 6 | 实现定义的硬件故障。Linux将SIGIOT的值和SIGABRT相同。 |
SIGBUS | 7 | 一个实现定义的硬件故障。当出现某些类型的内存故障时,实现常常产生此信号。 |
SIGFPE | 8 | 浮点异常 (ANSI)。 |
SIGKILL | 9 | 这是两个不能被捕获或忽略信号中的一个。它向系统管理员提供一种可以杀死任意进程的可靠方法。 |
SIGUSR1 | 10 | 用户自定义信号,可用于应用程序。 |
SIGSEGV | 11 | 指示进程进行了一次无效的内存引用(通常说明程序有错,比如访问了一个未经初始化的指针)。 |
SIGUSR2 | 12 | 用户自定义信号,可用于应用程序。 |
SIGPIPE | 13 | 如果在管道的读进程已终止时写管道,则产生此信号。当类型为SOCK_STREAM的套接字已不再连接时,进程写该套接字也产生此信号。 |
SIGALRM | 14 | 当用alarm、setitimer函数设置的定时器超时时,产生此信号。 |
SIGTERM | 15 | 这是由kill目录发送的系统默认终止信号。由于该信号是应用程序捕获的,使用SIGTERM也让程序有机会在退出之前做好清理工作,从而优雅的终止(相对于SIGKILL而言,SIGKILL不能被捕获或忽略)。 |
SIGSTKFLT | 16 | 此信号仅由Linux定义。她出现在Linux的早期版本,企图用于数学协处理器的栈故障。该信号并非由内核产生,但仍保留以向后兼容。 |
SIGCLD | SIGCHLD | |
SIGCHLD | 17 | 在一个进程终止或停止时,此信号被发送给其父进程。系统默认忽略此信号。如果父进程希望被告知其子进程的这种状态,则捕获此信号。信号捕捉函数中通常调用一种wait函数以取得子进程ID和其终止状态。 |
SIGCONT | 18 | 此作业控制信号发送给需要继续运行,但当前处于停止状态的进程。如果接收到此信号的进程处于停止状态,则系统默认动作时使该进程继续运行。否则默认动作是忽略此信号。例如,全屏编辑程序在捕获到此信号后,使用信号处理程序发出重新绘制终端屏幕的通知。 |
SIGSTOP | 19 | 这是一个作业控制信号,它停止一个进程。类似于交互停止信号(SIGTSTP),但是SIGSTOP不能被捕获或忽略。 |
SIGTSTP | 20 | 交互停止信号,当用户在终端上按挂起键(Ctrl+Z)时,终端驱动程序产生此信号。该信号发送至前台进程组中的所有进程。 |
SIGTTIN | 21 | 当一个后台进程组进程视图读其控制终端时,终端驱动程序产生此信号。在下列例外情况下不产生此信号: (a)读进程忽略或阻塞此信号。 (b)读进程所属的进程组时孤儿进程组,此时读操作返回出错,errno设置为EIO。 |
SIGTTOU | 22 | 当一个后台进程组进程试图写其控制终端时,终端驱动程序产生信号。与SIGTTIN信号不同,一个进程可以选择允许后台进程写控制终端。 如果不允许后台进程写,则与SIGTTIN相似,分两种特殊情况: (a)写进程忽略或阻塞此信号。 (b)写进程所属的进程组时孤儿进程组,此时读操作返回出错,errno设置为EIO。 不论是否允许后台进程写,一些除写以外的下列终端操作也能产生SIGTTOU信号,如:tcsetattr,tcsendbreak,tcdrain,tcflush,tcflow,tcsetpgrp。 |
SIGURG | 23 | 此信号通知进程已经发生一个紧急状况。在网络连接上接到带外的数据时,可选择地产生此信号。 |
SIGXCPU | 24 | Single UNIX Specification的XSI扩展支持资源限制的概念。如果进程超过了其软CPU时间限制,则产生此信号。 |
SIGXFSZ | 25 | 如果进程超过了其软文件长度限制,则产生此信号。 |
SIGVTALRM | 26 | 当一个由setitimer函数设置的虚拟间隔事件已经超时时,产生此信号。 |
SIGPROF | 27 | 分析闹钟 (4.2 BSD) |
SIGWINCH | 28 | 内核维持与每个终端或伪终端相关联窗口的大小。进程可以用ioctl函数得到或设置窗口的大小。如果进程用ioctl的设置窗口大小命令修改了窗口大小,则内核将此信号发送至前台进程组。 |
SIGPOLL | SIGIO | 此信号在SUSv4中已被标记为弃用,将来的标准可能会将此信号移除。当在一个可轮询设备上发生一个特定事件时产生此信号。 |
SIGIO | 29 | 表示一个异步IO事件。 |
SIGPWR | 30 | 电源故障重新启动 (System V)。 |
SIGSYS | 31 | 该信号指示一个无效的系统调用。由于某种未知的原因,进程执行了一条机器指令,内核认为这是一条系统调用,但该指令指示系统调用类型的参数却是无效的。这种情况是可能发生的,例如,用户编写了一道使用新系统调用的程序,然后运行该程序的可执行程序,所用的操作系统却不支持该系统调用的较早版本,于是出现了上述情况。 |
四、可重入函数
进程捕获到信号并对其进行处理时,进程正在执行的正常指令就会被信号处理程序临时中断,它会首先执行信号处理程序的中的指令。如果从信号处理程序返回,则继续执行再捕捉到信号时进程正在执行的正常的指令,但信号处理程序不能判断捕捉到信号时进程执行到何处。如果程序正在执行malloc,此时被信号打断了,信号程序中又调用malloc,可能会破坏进程,因为malloc通常会为它所分配的存储区维护一个链表,malloc例如正在修改链表,此时捕获到信号,信号处理程序中也进行malloc修改链表,导致问题的发生。
abort accept access aio_error aio return aio_suspend alarm bind cfgetispeed cfgetospeed cfsetispeed cfsetospeed chdir chmod chown clock_gettime close connect creat dup dup2 execl execle execv execye _Exit _exit | faccessat fchmod fchmodat fchown fchownat fcntl fdatasync fexecve fork fstat fstatat fsync ftruncate futimens getegid geteuid getgid getgroups getpeername getpgrp getpid getppid getsockname getsockopt getuid kill link | linkat listen lseek lstat mkdir mkdirat mkfifo mkfifoat mknod mknodat open openat pause pipe poll posix_trace_event pselect raise read readlink readlinkat recy recvfrom recvmsg rename renameat rmdir | select sem_post send sendmsg sendto setgid setpgid setsid setsockopt setuid shutdown sigaction sigaddset sigdelset sigemptyset sigfillset sigismember signal sigpause sigpending sigprocmask sigqueue sigset sigsuspend sleep socketmark socket | socketpair stat symlink symlinkat tcdrain tcflow teflush tcgetattr tcgetpgrp tcsendbreak tesetattr csetpgrp time timer_getoverrun timer_gettime timer_settime times umask uname unlink ulinkat utime utimensat utimes wait waitpid write |
除上述函数之外的大多数是不可重入的,原因如下:
1、用的是静态数据结构(static关键字)。
2、调用malloc和free。
3、标准I/O函数,很多标准I/O函数的实现都是以不可重入的方式使用全局数据结构。
建议大家不要在信号处理程序中调用一个非可重入函数,因为则其结果是不可预知的。
五、信号术语
术语 | 描述 |
未决的信号 | 当一个信号产生,但却还没有递送给进程,则称这个信号是未决的。 |
信号屏蔽字 | 规定了当前要阻塞递送给进程的信号集。 |
六、函数介绍
1、sigaction
(1)描述
检查或修改与指定信号相关联的处理动作。
(2)声明
int sigaction(int __sig, const struct sigaction *__restrict__ __act, struct sigaction *__restrict__ __oact)
(3)参数
参数 | 描述 |
__sig | 信号。 |
__act | 修改__sig的动作。 |
__oact | __sig的上一个动作。 |
(4)返回值
参数 | 描述 |
0 | 成功。 |
-1 | 失败。 |
(5)其他
struct sigaction定义:
/* Structure describing the action to be taken when a signal arrives. */
struct sigaction
{
/* Signal handler. */
#ifdef __USE_POSIX199309
union
{
/* Used if SA_SIGINFO is not set. */
__sighandler_t sa_handler;
/* Used if SA_SIGINFO is set. */
void (*sa_sigaction) (int, siginfo_t *, void *);
}
__sigaction_handler;
# define sa_handler __sigaction_handler.sa_handler
# define sa_sigaction __sigaction_handler.sa_sigaction
#else
__sighandler_t sa_handler;
#endif
/* Additional set of signals to be blocked. */
__sigset_t sa_mask;
/* Special flags. */
int sa_flags;
/* Restore handler. */
void (*sa_restorer) (void);
};
参数 | 描述 |
sa_handler | 信号处理函数,sa_flags没有设置为SA_SIGINFO时的处理方式。 |
sa_sigaction | 备用信号处理函数,sa_flags设置为SA_SIGINFO时的处理方式。 |
sa_mask | 信号屏蔽字。 |
sa_flags | 特殊标记。 |
sa_restorer | 恢复处理函数。 |
struct sigaction中的sa_flags对信号处理的各个选项。
宏 | 描述 |
SA_INTERRUPT | 由此信号中断的系统调用不自动重启。 |
SA_NOCLDSTOP | 若signo是SIGCHLD,当子进程停止作业控制,不产生此信号,当子进程终止时,人就产生此信号(参考SA_NOCLDWAIT)。若已设置此标志,则当停止的进程继续运行时,作为XSI扩展,不产生SIGCHLD信号。 |
SA_NOCLDWAIT | 若signo是SIGCHLD,则当调用进程的子进程终止时,不创建僵死进程。若调用进程随后调用wait,则阻塞到它所有子进程都终止,此时返回-1,errno设置为ECHILD。 |
SA_NODEFER | 当捕捉到信号时,在执行其信号捕捉函数时,系统不自动阻塞此信号,除非sa_mask包含了此信号。注意,此种类型的操作对应于早期的不可靠信号。 |
SA_ONSTACK | 若用sigaltstack已声明了一个替换栈,则此信号递送给替换栈上的进程。 |
SA_RESETTHAND | 在此信号捕捉函数的入口处,将此信号的处理方式重置为SIG_DFL,并清除SA_SIGINFO标志。注意,此种类型的信号对应于早期的不可靠信号。但是,不能自动重置SIGILL和SIGTRAP这两个信号的配置。设置此标志使sigaction的行为如同设置了SA_NODEFER标志。 |
SA_RESTART | 由此信号中断的系统调用自动重启动。 |
SA_SIGINFO | 此选项对信号处理程序提供了附加信息:一个指向siginfo结构的指针以及一个指向进程上下文标识符的指针。 |
2、sigsuspend
(1)描述
将阻塞信号集更改为 Set ,等待信号到达,然后恢复被阻止的信号集。
(2)声明
int sigsuspend(const sigset_t *__set)
(3)参数
参数 | 描述 |
__set | 信号屏蔽字。 |
(4)返回值
参数 | 描述 |
-1 | 并设置errno 为 EINTR。 |
(5)其他
3、sigemptyset
(1)描述
初始化信号集中所有信号。
(2)声明
int sigemptyset(sigset_t *__set)
(3)参数
参数 | 描述 |
__set | 信号屏蔽字。 |
(4)返回值
参数 | 描述 |
0 | 成功 |
其他 | 失败 |
(5)其他
4、sigfillset
(1)描述
初始化信号集,填充所有信号。
(2)声明
int sigfillset(sigset_t *__set)
(3)参数
参数 | 描述 |
__set | 信号屏蔽字。 |
(4)返回值
参数 | 描述 |
0 | 成功 |
其他 | 失败 |
(5)其他
5、sigaddset
(1)描述
信号集填充信号。
(2)声明
int sigaddset(sigset_t *__set, int __signo)
(3)参数
参数 | 描述 |
__set | 信号屏蔽字。 |
__signo | 信号。 |
(4)返回值
参数 | 描述 |
0 | 成功 |
其他 | 失败 |
(5)其他
6、sigdelset
(1)描述
信号集删除信号。
(2)声明
int sigdelset(sigset_t *__set, int __signo)
(3)参数
参数 | 描述 |
__set | 信号屏蔽字。 |
__signo | 信号。 |
(4)返回值
参数 | 描述 |
0 | 成功 |
其他 | 失败 |
(5)其他
7、sigismember
(1)描述
判断信号集是否存在某信号。
(2)声明
int sigismember(const sigset_t *__set, int __signo)
(3)参数
参数 | 描述 |
__set | 信号集。 |
__signo | 信号。 |
(4)返回值
参数 | 描述 |
-1 | 报错 |
1 | 成功,找到信号。 |
0 | 没有找到信号。 |
(5)其他
8、kill
(1)描述
发送信号给进程或进程组。
(2)声明
int kill(pid_t __pid, int __sig)
(3)参数
参数 | 描述 |
__pid | >0 : 发送信号给指定进程。 ==0 : 发送信号给与发送进程属于同一进程组的所有进程, 并且发送进程有权限向这些进程发送信号。 <0 : 发送信号给进程组ID等于Pid绝对值的进程, 并且发送进程有权限向这些进程发送信号。 ==-1 : 发送进程有权限发送信号的所有进程, 所有进程不包括系统进程集中的进程。 |
__sig | 信号。 |
(4)返回值
参数 | 描述 |
0 | 成功 |
其他 | 失败 |
(5)其他
9、sigprocmask
(1)描述
单线程进程设置或检测信号屏蔽字。
(2)声明
int sigprocmask(int __how, const sigset_t *__restrict__ __set, sigset_t *__restrict__ __oset)
(3)参数
参数 | 描述 |
__how | __set行为。 |
__set | 信号屏蔽字。 |
__oset | 原始信号屏蔽字。 |
(4)返回值
参数 | 描述 |
0 | 成功 |
其他 | 失败 |
(5)其他
__how支持如下参数宏:
参数宏 | 描述 |
SIG_BLOCK | 单线程进程添加信号屏蔽字,当前信号屏蔽字与SIG_SET的并集。 |
SIG_UNBLOCK | 单线程进程删除信号屏蔽字,当前信号屏蔽字减去SIG_SET。 |
SIG_SETMASK | 单线程进程设置信号屏蔽字。 |
10、sigpending
(1)描述
检查进程的未决信号集。
未决信号集:
是指那些已经被发送给进程,但是还没有被处理(即还没有被当前进程的信号处理函数捕获)的信号。
(2)声明
int sigsuspend(const sigset_t *__set)
(3)参数
参数 | 描述 |
__set | 信号屏蔽字。 |
(4)返回值
参数 | 描述 |
0 | 成功 |
其他 | 失败 |
(5)其他
七、自定义函数
1、MySignal
(1)描述
信号管理。
由于signal函数是由ISO C定义。因为其不涉及多进程、进程组以及终端I/O等,所以对信号的定义比较模糊,以至于对UNIX系统而言无用处。
UNIX SYSTEM V支持signal函数,但是该函数提供旧的不可靠语义,为的是向后兼容,新的应用程序不应使用这些不可靠的信号。所以我们用sigaction封装signal函数。
(2)定义
Status MySignal(int SigNo, SigFt SigFunc, SigFt *PreSigFunc)
{
struct sigaction Action;
struct sigaction PreAction;
/*将需要屏蔽的信号清除,也就是不屏蔽任何信号。*/
if(SigSetEmpty(&(Action.sa_mask)) != SUCCESS_FLAG)
{
*PreSigFunc = SIG_ERR;
goto ERR_STAGE;
}
Action.sa_handler = SigFunc;
/*除告警信号外,所有的系统调用都设置为自动重启。*/
if (SigNo == SIGALRM)
{
#ifdef SA_INTERRUPT
Action.sa_flags |= SA_INTERRUPT;
#endif
}
else
{
Action.sa_flags |= SA_RESTART;
}
/*添加信号屏蔽字,防止出现SigNo信号处理函数嵌套SigNo信号处理函数*/
if (SigSetAdd(&(Action.sa_mask),SigNo) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
if (SigAction(SigNo,&Action,&PreAction) != SUCCESS_FLAG)
{
*PreSigFunc = SIG_ERR;
goto ERR_STAGE;
}
*PreSigFunc = PreAction.sa_handler;
LOG_FORMAT(Debug,"OK, SigNo : %d.\n",SigNo);
return SUCCESS_FLAG;
ERR_STAGE:
LOG_FORMAT(Error,"Fail, SigNo : %d, SigFunc : %p, PreSigFunc : %p.\n",SigNo,SigFunc,PreSigFunc);
return FAIL_FLAG;
}
(3)参数
参数 | 描述 |
SigNo | 信号。 |
SigFunc | 处理信号的函数。 |
PreSigFunc | 返回此信号的上一个处理信号函数。 |
(4)返回值
参数 | 描述 |
SUCCESS_FLAG | 成功。 |
FAIL_FLAG | 失败。 |
(5)其他
typedef void (*SigFt)(int);
八、DEMO
DEMO中除了MySignal函数,其他的自定义函数和原始信号基本没有差别,只是加了正确性判断。
1、保护代码临界区,不被特定信号中断的正确方法
(1)源码
为了方便大家观察现象,所以在信号处理函数中加入了不可重入的函数。
#include "MySignal.h"
static void TestSigInt(int SigNo);
Status main()
{
LOG_LEVEL = Debug;
sigset_t NewMask;
sigset_t OldMask;
sigset_t WaitMask;
SigFt PreSigFunc;
/*安装信号处理程序*/
if (MySignal(SIGINT,TestSigInt,&PreSigFunc) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
/*清空信号屏蔽字变量*/
if (SigSetEmpty(&NewMask) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
if (SigSetEmpty(&WaitMask) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
/*添加信号屏蔽字*/
if (SigSetAdd(&NewMask,SIGINT) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
if (SigSetAdd(&WaitMask,SIGUSR1) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
/*设置信号屏蔽字,保存原始信号屏蔽字*/
if (SIG_PROC_MASK_ADD(&NewMask,&OldMask) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
/*代码临界区*/
/*打印当前信号屏蔽字*/
CurSigMaskPrint(Debug);
/*等待,允许所有信号除了SIGUSR1*/
if (SigSuspend(&WaitMask) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
/*打印当前信号屏蔽字*/
CurSigMaskPrint(Debug);
/*恢复原有信号屏蔽字*/
if (SIG_PROC_MASK_SET(&OldMask,NULL) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
/*打印当前信号屏蔽字*/
CurSigMaskPrint(Debug);
return SUCCESS_FLAG;
}
static void TestSigInt(int SigNo)
{
LOG_FORMAT(Debug,"OK. SigNo : %d.\n",SigNo);
/*打印当前信号屏蔽字*/
CurSigMaskPrint(Debug);
}
(2)验证
运行程序:
[gbase@czg2 Exec]$ ./TestSigSuspend
2024-12-18 16:30:43-P[14581]-T[14581]-[Debug]-SigSetEmpty : OK.
2024-12-18 16:30:43-P[14581]-T[14581]-[Debug]-SigSetAdd : OK, SigNo : 2.
2024-12-18 16:30:43-P[14581]-T[14581]-[Debug]-SigAction : OK.
2024-12-18 16:30:43-P[14581]-T[14581]-[Debug]-MySignal : OK, SigNo : 2.
2024-12-18 16:30:43-P[14581]-T[14581]-[Debug]-SigSetEmpty : OK.
2024-12-18 16:30:43-P[14581]-T[14581]-[Debug]-SigSetEmpty : OK.
2024-12-18 16:30:43-P[14581]-T[14581]-[Debug]-SigSetAdd : OK, SigNo : 2.
2024-12-18 16:30:43-P[14581]-T[14581]-[Debug]-SigSetAdd : OK, SigNo : 10.
2024-12-18 16:30:43-P[14581]-T[14581]-[Debug]-SigProcMask : OK, How : 0
2024-12-18 16:30:43-P[14581]-T[14581]-[Debug]-SigProcMask : OK, How : 0
2024-12-18 16:30:43-P[14581]-T[14581]-[Debug]-SigProcMaskPrint :
[ SIGINT ]
这里停在了SigSuspend函数,屏蔽了特定信号SIGUSR1。
发送信号:
[root@czg2 ~]# kill -SIGINT `pidof TestSigSuspend`
程序继续:
2024-12-18 16:31:03-P[14581]-T[14581]-[Debug]-TestSigInt : OK. SigNo : 2.
2024-12-18 16:31:03-P[14581]-T[14581]-[Debug]-SigProcMask : OK, How : 0
2024-12-18 16:31:03-P[14581]-T[14581]-[Debug]-SigProcMaskPrint :
[ SIGINT,SIGUSR1 ]
2024-12-18 16:31:03-P[14581]-T[14581]-[Debug]-SigSuspend : OK.
2024-12-18 16:31:03-P[14581]-T[14581]-[Debug]-SigProcMask : OK, How : 0
2024-12-18 16:31:03-P[14581]-T[14581]-[Debug]-SigProcMaskPrint :
[ SIGINT ]
2024-12-18 16:31:03-P[14581]-T[14581]-[Debug]-SigProcMask : OK, How : 2
2024-12-18 16:31:03-P[14581]-T[14581]-[Debug]-SigProcMask : OK, How : 0
2024-12-18 16:31:03-P[14581]-T[14581]-[Debug]-SigProcMaskPrint :
[ ]
程序捕获到信号SIGINT,跳到SIGINT信号处理程序,SIGINT信号处理程序中屏蔽了信号SIGINT和SIGUSR1。从信号处理程序返回跳到SigSuspend函数,信号屏蔽字变回了原来的SIGINT,最后我们将信号屏蔽字置为了最初的状态,也就是没有屏蔽任何信号。
2、等待信号处理程序,修改一个全局变量
(1)源码
捕获中断信号和退出信号,只有退出信号才唤醒主进程。
为了方便大家观察现象,所以在信号处理函数中加入了不可重入的函数。
#include "MySignal.h"
volatile sig_atomic_t QuitFlag;
static void TestSigInt(int SigNo);
Status main()
{
LOG_LEVEL = Debug;
sigset_t NewMask;
sigset_t OldMask;
sigset_t ZeroMask;
SigFt PreSigFunc;
/*安装信号处理程序*/
if (MySignal(SIGINT,TestSigInt,&PreSigFunc) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
if (MySignal(SIGQUIT,TestSigInt,&PreSigFunc) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
/*清空信号屏蔽字变量*/
if (SigSetEmpty(&NewMask) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
if (SigSetEmpty(&ZeroMask) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
/*添加信号屏蔽字*/
if (SigSetAdd(&NewMask,SIGQUIT) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
/*设置信号屏蔽字,保存原始信号屏蔽字*/
if (SIG_PROC_MASK_ADD(&NewMask,&OldMask) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
/*打印当前信号屏蔽字*/
CurSigMaskPrint(Debug);
/*等待,允许所有信号*/
while (QuitFlag == 0)
{
if (SigSuspend(&ZeroMask) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
}
QuitFlag = 0;
/*打印当前信号屏蔽字*/
CurSigMaskPrint(Debug);
/*恢复原有信号屏蔽字*/
if (SIG_PROC_MASK_SET(&OldMask,NULL) != SUCCESS_FLAG)
{
return FAIL_FLAG;
}
/*打印当前信号屏蔽字*/
CurSigMaskPrint(Debug);
return SUCCESS_FLAG;
}
static void TestSigInt(int SigNo)
{
SigFt PreSigFunc;
if (SigNo == SIGINT)
{
LOG_FORMAT(Debug,"OK. SIGINT.\n");
/*安装信号处理程序*/
if (MySignal(SIGINT,TestSigInt,&PreSigFunc) != SUCCESS_FLAG)
{
return;
}
}
else if(SigNo == SIGQUIT)
{
LOG_FORMAT(Debug,"OK. SIGQUIT.\n");
QuitFlag = 1;
/*安装信号处理程序*/
if (MySignal(SIGQUIT,TestSigInt,&PreSigFunc) != SUCCESS_FLAG)
{
return;
}
}
/*打印当前信号屏蔽字*/
CurSigMaskPrint(Debug);
}
(2)验证
运行程序:
[gbase@czg2 Exec]$ ./TestSigSuspend2
2024-12-18 17:45:19-P[35259]-T[35259]-[Debug]-SigSetEmpty : OK.
2024-12-18 17:45:19-P[35259]-T[35259]-[Debug]-SigSetAdd : OK, SigNo : 2.
2024-12-18 17:45:19-P[35259]-T[35259]-[Debug]-SigAction : OK.
2024-12-18 17:45:19-P[35259]-T[35259]-[Debug]-MySignal : OK, SigNo : 2.
2024-12-18 17:45:19-P[35259]-T[35259]-[Debug]-SigSetEmpty : OK.
2024-12-18 17:45:19-P[35259]-T[35259]-[Debug]-SigSetAdd : OK, SigNo : 3.
2024-12-18 17:45:19-P[35259]-T[35259]-[Debug]-SigAction : OK.
2024-12-18 17:45:19-P[35259]-T[35259]-[Debug]-MySignal : OK, SigNo : 3.
2024-12-18 17:45:19-P[35259]-T[35259]-[Debug]-SigSetEmpty : OK.
2024-12-18 17:45:19-P[35259]-T[35259]-[Debug]-SigSetEmpty : OK.
2024-12-18 17:45:19-P[35259]-T[35259]-[Debug]-SigSetAdd : OK, SigNo : 3.
2024-12-18 17:45:19-P[35259]-T[35259]-[Debug]-SigProcMask : OK, How : 0
2024-12-18 17:45:19-P[35259]-T[35259]-[Debug]-SigProcMask : OK, How : 0
2024-12-18 17:45:19-P[35259]-T[35259]-[Debug]-SigProcMaskPrint :
[ SIGQUIT ]
这里停在了SigSuspend函数,不屏蔽信号。
发送3个打断信号:
[root@czg2 ~]# kill -SIGINT `pidof TestSigSuspend2`
[root@czg2 ~]# kill -SIGINT `pidof TestSigSuspend2`
[root@czg2 ~]# kill -SIGINT `pidof TestSigSuspend2`
程序继续:
2024-12-18 17:46:28-P[35259]-T[35259]-[Debug]-TestSigInt : OK. SIGINT.
2024-12-18 17:46:28-P[35259]-T[35259]-[Debug]-SigSetEmpty : OK.
2024-12-18 17:46:28-P[35259]-T[35259]-[Debug]-SigSetAdd : OK, SigNo : 2.
2024-12-18 17:46:28-P[35259]-T[35259]-[Debug]-SigAction : OK.
2024-12-18 17:46:28-P[35259]-T[35259]-[Debug]-MySignal : OK, SigNo : 2.
2024-12-18 17:46:28-P[35259]-T[35259]-[Debug]-SigProcMask : OK, How : 0
2024-12-18 17:46:28-P[35259]-T[35259]-[Debug]-SigProcMaskPrint :
[ SIGINT ]
2024-12-18 17:46:28-P[35259]-T[35259]-[Debug]-SigSuspend : OK.
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-TestSigInt : OK. SIGINT.
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-SigSetEmpty : OK.
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-SigSetAdd : OK, SigNo : 2.
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-SigAction : OK.
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-MySignal : OK, SigNo : 2.
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-SigProcMask : OK, How : 0
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-SigProcMaskPrint :
[ SIGINT ]
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-SigSuspend : OK.
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-TestSigInt : OK. SIGINT.
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-SigSetEmpty : OK.
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-SigSetAdd : OK, SigNo : 2.
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-SigAction : OK.
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-MySignal : OK, SigNo : 2.
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-SigProcMask : OK, How : 0
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-SigProcMaskPrint :
[ SIGINT ]
2024-12-18 17:46:29-P[35259]-T[35259]-[Debug]-SigSuspend : OK.
程序捕获到信号SIGINT三次,跳到SIGINT信号处理程序,SIGINT信号处理程序中屏蔽了信号SIGINT。从信号处理程序返回跳到SigSuspend函数。
发送退出信号:
[root@czg2 ~]# kill -SIGQUIT `pidof TestSigSuspend2`
程序继续:
2024-12-18 17:48:18-P[35259]-T[35259]-[Debug]-TestSigInt : OK. SIGQUIT.
2024-12-18 17:48:18-P[35259]-T[35259]-[Debug]-SigSetEmpty : OK.
2024-12-18 17:48:18-P[35259]-T[35259]-[Debug]-SigSetAdd : OK, SigNo : 3.
2024-12-18 17:48:18-P[35259]-T[35259]-[Debug]-SigAction : OK.
2024-12-18 17:48:18-P[35259]-T[35259]-[Debug]-MySignal : OK, SigNo : 3.
2024-12-18 17:48:18-P[35259]-T[35259]-[Debug]-SigProcMask : OK, How : 0
2024-12-18 17:48:18-P[35259]-T[35259]-[Debug]-SigProcMaskPrint :
[ SIGQUIT ]
2024-12-18 17:48:18-P[35259]-T[35259]-[Debug]-SigSuspend : OK.
2024-12-18 17:48:18-P[35259]-T[35259]-[Debug]-SigProcMask : OK, How : 0
2024-12-18 17:48:18-P[35259]-T[35259]-[Debug]-SigProcMaskPrint :
[ SIGQUIT ]
2024-12-18 17:48:18-P[35259]-T[35259]-[Debug]-SigProcMask : OK, How : 2
2024-12-18 17:48:18-P[35259]-T[35259]-[Debug]-SigProcMask : OK, How : 0
2024-12-18 17:48:18-P[35259]-T[35259]-[Debug]-SigProcMaskPrint :
[ ]
程序捕获到信号SIGQUIT,跳到SIGQUIT信号处理程序,SIGQUIT信号处理程序中屏蔽了信号SIGQUIT。从信号处理程序返回跳到SigSuspend函数。信号屏蔽字变回了原来的SIGQUIT,最后我们将信号屏蔽字置为了最初的状态,也就是没有屏蔽任何信号。