Unix环境高级编程-学习-11-信号之初识

目录

一、验证环境

二、介绍

三、信号宏

四、可重入函数

五、信号术语

六、函数介绍

1、sigaction

(1)描述

(2)声明

(3)参数

(4)返回值

(5)其他

2、sigsuspend

(1)描述

(2)声明

(3)参数

(4)返回值

(5)其他

3、sigemptyset

(1)描述

(2)声明

(3)参数

(4)返回值

(5)其他

4、sigfillset

(1)描述

(2)声明

(3)参数

(4)返回值

(5)其他

5、sigaddset

(1)描述

(2)声明

(3)参数

(4)返回值

(5)其他

6、sigdelset

(1)描述

(2)声明

(3)参数

(4)返回值

(5)其他

7、sigismember

(1)描述

(2)声明

(3)参数

(4)返回值

(5)其他

8、kill

(1)描述

(2)声明

(3)参数

(4)返回值

(5)其他

9、sigprocmask

(1)描述

(2)声明

(3)参数

(4)返回值

(5)其他

10、sigpending

(1)描述

(2)声明

(3)参数

(4)返回值

(5)其他

七、自定义函数

1、MySignal

(1)描述

(2)定义

(3)参数

(4)返回值

(5)其他

八、DEMO

1、保护代码临界区,不被特定信号中断的正确方法

(1)源码

(2)验证

2、等待信号处理程序,修改一个全局变量

(1)源码

(2)验证


一、验证环境

名称
CPUIntel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz
操作系统CentOS Linux release 7.9.2009 (Core)
内存5G
逻辑核数6

二、介绍

信号是软件中断,它提供了一种处理异步事件的方法,很多比较重要的程序都需要处理信号。信号也是异步事件的经典实例,产生信号的事件对于进程是随机出现的。当接收到某个信号时,我们可以告知内核按照三种方法:忽略此信号、捕捉信号、执行系统默认动作,进行信号的处理。

本文只是简单介绍信号,详细的还是要看《Unix环境高级编程》这本书,书山有路勤为径,学海无涯苦作舟。

三、信号宏

参考头文件#include <bits/signum.h>

描述
SIGHUP1如果终端接口检测到一个连接断开,则将此此信号送给与该终端相关的控制进程(会话首进程)。此信号被送给session结构中s_leader字段所指向的进程。仅当终端的CLOCAL标志没有设置时,在上述条件下才产生此信号。注意,接到此信号的会话首进程可能在后台,这区别于由终端正常产生的几个信号(中断、退出和挂起),这些信号总是传递给前台进程组。
如果会话首进程终止,也产生此信号。在这种情况,此信号送给前台进程组中的每一个进程。
通常用此信号通知守护进程,再次读取他们的配置文件。选用SIGHUP的理由是,守护进程不会有控制终端,通常绝不会接收到这种信号。
SIGINT2当用户按中断键(Delete或Ctrl+c)时,终端驱动程序产生此信号并发送至前台进程组的每一个进程。当一个进程在运行时失控,特别是它正在屏幕上产生大量不需要的输出时,常用此信号终止它。
SIGQUIT3当用户在终端上按退出键(Ctrl+\)时,中断驱动程序产生此信号,并发送给前台进程组中的所有进程,此信号不仅终止前台进程组,同时产生一个core文件。
SIGILL4进程已执行一条非法硬件指令。
SIGTRAP5指一个实现定义的硬件故障,此信号名来自于PDP-11的TRAP指令。当执行断点指令时,实现常用此信号将控制转移至调试程序。
SIGABRT6调用abort函数产生的信号。
SIGIOT6实现定义的硬件故障。Linux将SIGIOT的值和SIGABRT相同。
SIGBUS7一个实现定义的硬件故障。当出现某些类型的内存故障时,实现常常产生此信号。
SIGFPE8浮点异常 (ANSI)。
SIGKILL9这是两个不能被捕获或忽略信号中的一个。它向系统管理员提供一种可以杀死任意进程的可靠方法。
SIGUSR110用户自定义信号,可用于应用程序。
SIGSEGV11指示进程进行了一次无效的内存引用(通常说明程序有错,比如访问了一个未经初始化的指针)。
SIGUSR212用户自定义信号,可用于应用程序。
SIGPIPE13如果在管道的读进程已终止时写管道,则产生此信号。当类型为SOCK_STREAM的套接字已不再连接时,进程写该套接字也产生此信号。
SIGALRM14当用alarm、setitimer函数设置的定时器超时时,产生此信号。
SIGTERM15这是由kill目录发送的系统默认终止信号。由于该信号是应用程序捕获的,使用SIGTERM也让程序有机会在退出之前做好清理工作,从而优雅的终止(相对于SIGKILL而言,SIGKILL不能被捕获或忽略)。
SIGSTKFLT16此信号仅由Linux定义。她出现在Linux的早期版本,企图用于数学协处理器的栈故障。该信号并非由内核产生,但仍保留以向后兼容。
SIGCLDSIGCHLD
SIGCHLD17在一个进程终止或停止时,此信号被发送给其父进程。系统默认忽略此信号。如果父进程希望被告知其子进程的这种状态,则捕获此信号。信号捕捉函数中通常调用一种wait函数以取得子进程ID和其终止状态。
SIGCONT18此作业控制信号发送给需要继续运行,但当前处于停止状态的进程。如果接收到此信号的进程处于停止状态,则系统默认动作时使该进程继续运行。否则默认动作是忽略此信号。例如,全屏编辑程序在捕获到此信号后,使用信号处理程序发出重新绘制终端屏幕的通知。
SIGSTOP19这是一个作业控制信号,它停止一个进程。类似于交互停止信号(SIGTSTP),但是SIGSTOP不能被捕获或忽略。
SIGTSTP20交互停止信号,当用户在终端上按挂起键(Ctrl+Z)时,终端驱动程序产生此信号。该信号发送至前台进程组中的所有进程。
SIGTTIN21当一个后台进程组进程视图读其控制终端时,终端驱动程序产生此信号。在下列例外情况下不产生此信号:
(a)读进程忽略或阻塞此信号。
(b)读进程所属的进程组时孤儿进程组,此时读操作返回出错,errno设置为EIO。
SIGTTOU22当一个后台进程组进程试图写其控制终端时,终端驱动程序产生信号。与SIGTTIN信号不同,一个进程可以选择允许后台进程写控制终端。
如果不允许后台进程写,则与SIGTTIN相似,分两种特殊情况:
(a)写进程忽略或阻塞此信号。
(b)写进程所属的进程组时孤儿进程组,此时读操作返回出错,errno设置为EIO。
不论是否允许后台进程写,一些除写以外的下列终端操作也能产生SIGTTOU信号,如:tcsetattr,tcsendbreak,tcdrain,tcflush,tcflow,tcsetpgrp。
SIGURG23此信号通知进程已经发生一个紧急状况。在网络连接上接到带外的数据时,可选择地产生此信号。
SIGXCPU24Single UNIX Specification的XSI扩展支持资源限制的概念。如果进程超过了其软CPU时间限制,则产生此信号。
SIGXFSZ25如果进程超过了其软文件长度限制,则产生此信号。
SIGVTALRM26当一个由setitimer函数设置的虚拟间隔事件已经超时时,产生此信号。
SIGPROF27分析闹钟 (4.2 BSD)
SIGWINCH28内核维持与每个终端或伪终端相关联窗口的大小。进程可以用ioctl函数得到或设置窗口的大小。如果进程用ioctl的设置窗口大小命令修改了窗口大小,则内核将此信号发送至前台进程组。
SIGPOLLSIGIO此信号在SUSv4中已被标记为弃用,将来的标准可能会将此信号移除。当在一个可轮询设备上发生一个特定事件时产生此信号。
SIGIO29表示一个异步IO事件。
SIGPWR30电源故障重新启动 (System V)。
SIGSYS31该信号指示一个无效的系统调用。由于某种未知的原因,进程执行了一条机器指令,内核认为这是一条系统调用,但该指令指示系统调用类型的参数却是无效的。这种情况是可能发生的,例如,用户编写了一道使用新系统调用的程序,然后运行该程序的可执行程序,所用的操作系统却不支持该系统调用的较早版本,于是出现了上述情况。

四、可重入函数

进程捕获到信号并对其进行处理时,进程正在执行的正常指令就会被信号处理程序临时中断,它会首先执行信号处理程序的中的指令。如果从信号处理程序返回,则继续执行再捕捉到信号时进程正在执行的正常的指令,但信号处理程序不能判断捕捉到信号时进程执行到何处。如果程序正在执行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,最后我们将信号屏蔽字置为了最初的状态,也就是没有屏蔽任何信号。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值