信号是异步的。
信号本质上是一种int类型的数据 。
在Linux中又不可靠信号和可靠信号,1~31对应的是不可靠信号,其他的是可靠信号,具体信号如下图
除了可靠和非可靠以外还有实时信号和非实时信号,非实时信号不支持排队是可靠信号,实时信号支持排队是可靠信号。
常见信号和默认操作:
SIGINT:例如按下CTRL+C时会有一个该信号发送给前台进程,该信号的系统默认操作时终止进程的运行。
SIGQUIT:当用户在终端按下退出字符(通常是 CTRL + \)时,内核将发送 SIGQUIT 信号给前台进程组中的每一 个进程。该信号的系统默认操作是终止进程的运行、并生成可用于调试的核心转储文件。
SIGILL:如果进程试图执行非法(即格式不正确)的机器语言指令,系统将向进程发送该信号。该信号的系统默 认操作是终止进程的运行。
SIGABRT: 当进程调用 abort()系统调用时(进程异常终止),系统会向该进程发送 SIGABRT 信号。该信号的系统 默认操作是终止进程、并生成核心转储文件。
SIGBUS :产生该信号(总线错误,bus error)表示发生了某种内存访问错误。该信号的系统默认操作是终止进程。
SIGFPE :该信号因特定类型的算术错误而产生,譬如除以 0。该信号的系统默认操作是终止进程。
SIGKILL: 此信号为“必杀(sure kill)”信号,用于杀死进程的终极办法,此信号无法被进程阻塞、忽略或者捕 获,故而“一击必杀”,总能终止进程。
SIGUSR1 :该信号和 SIGUSR2 信号供程序员自定义使用,内核绝不会为进程产生这些信号,在我们的程序中,可 以使用这些信号来互通通知事件的发生,或是进程彼此同步操作。该信号的系统默认操作是终止进程。
进程对信号的处理:
处理方式有:忽略信号,捕获信号和执行系统默认操作,系统提供了signal()和 sigaction()用于设置信号的处理方式。
signal():时linux下设置信号处理方式最简单的接口,原型如下
函数参数:
signum:此参数需要设置信号,可使用信号名或者信号的数字编号。
handler:sig_t类型的函数指针,指向信号对应的信号处理函数,当进程接收到信号后自动执行该处理函数,参数handler既可以设置用户自定义的函数,也可以设置SIG_IGN(忽略信号)或者SIG_DFL(执行系统默认操作),sig_t 函数指针的 int 类型参数指的是,当前触发该函数的信号,可将多个信号绑定到同一个信号处理函数 上,此时就可通过此参数来判断当前触发的是哪个信号。 Tips:SIG_IGN、SIG_DFL 分别取值如下:
/* Fake signal functions. */
#define SIG_ERR ((sig_t) -1) /* Error return. */
#define SIG_DFL ((sig_t) 0) /* Default action. */
#define SIG_IGN ((sig_t) 1) /* Ignore signal. */
返回值:成功返回指向在此之前的信号,错误回返回SIG_ERR,并且设置errno。
(PS:在Linux中 显示当前系统的进程)
sigaction():设置信号处理方式函数, 推荐使用该函数,具有更好的灵活性和移植性。
允许单独获取信号的处理函数而不是设置,并且还可以设置各种属性对调用信号处理函数时的行为实施更加精准的控制,函数原型如下:
函数参数和返回值含义:
signum:需要设置的信号
act:该参数是一个struct sigaction类型指针,指向一个该类型的结构体,该数据描述 了信号的处理方式,如果act不是NULL,则表示需要为信号设置新的处理方式,如果为NULL则不需要改变信号的处理方式。
oldact:该参数也是一个struct sigaction类型指针,指向一个该类型的结构体,如果参数不为NULL则会将信号之前的处理方式等信息通过参数oldact返回出来,如果无意获取此类信息可以将该参数设置为NULL。
返回值:成功返回0,失败返回-1,并设置errno。
struct sigaction 结构体如下:
struct sigaction
{
void (*sa_handler)(int);//信号处理函数,与signal()中的handler相同是一个函数指针
void (*sa_sigaction)(int, siginfo_t *, void *);/* 也用于指定信号处理函数,这是一个替代的信号处理函数,它提供了更多的参数,这些信号通过siginfo_t参数获取sa_handler 和 sa_sigaction 是互斥的,不能同时设置,对于标准信号来说,使用 sa_handler 就可以了,可通过 SA_SIGINFO 标志进行选择。*/
sigset_t sa_mask;//**参数定义了一组信号,当进程在执行sa_handler所定义的信息处理函数之前,回将这组信号添加到进程的信号掩码字段中,执行完函数后恢复,然后将这组信号从掩码里面删除。这个信号掩码用于保护不被同样信号打断,被添加在掩码中的信号再次出现时会被阻塞,直到它移除掩码,如果还需要阻塞其他信号可以通过设置sa_mask**/
int sa_flags;//定义了一组标志,用于控制信号的处理过程
void (*sa_restorer)(void);
};
sa_flags可以设置的标志如下:
SA_NOCLDSTOP 如果signum为SIGCHLD,则子进程停止时(即当它们接收到SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU 中的一种时)或恢复(即它们接收到 SIGCONT)时不会收到 SIGCHLD 信号。
SA_NOCLDWAIT 如果 signum 是 SIGCHLD,则在子进程终止时不要将其转变为僵尸进程。
SA_NODEFER 不要阻塞从某个信号自身的信号处理函数中接收此信号。也就是说当进程此时正在执行某个信号的处 理函数,默认情况下,进程会自动将该信号添加到进程的信号掩码字段中,从而在执行信号处理函数期间阻 塞该信号,默认情况下,我们期望进程在处理一个信号时阻塞同种信号,否则引起一些竞态条件;
如果设置 了 SA_NODEFER 标志,则表示不对它进行阻塞。 SA_RESETHAND 执行完信号处理函数之后,将信号的处理方式设置为系统默认操作。
SA_RESTART 被信号中断的系统调用,在信号处理完成之后将自动重新发起。
SA_SIGINFO 如果设置了该标志,则表示使用 sa_sigaction 作为信号处理函数、而不是 sa_handler,关于 sa_sigaction 信号处理函数的参数信息。
向进程发送信号
linux提供了kill(),killpg(),raise(),实现信号的发送。
1.kill()
函数原型:
函数参数与返回值含义:
pid:该参数为正数,用于接收此信号的进程pid;除此之外,该参数可以设置为0或者-1以及小于-1等不同值,取不同值含义如下:
正:sig将发送到pid 指定的进程。
0:发送sig到当前进程的进程中的每个进程。
-1:将sig发送到当前进程有权发送信号的每个进程,但是进程1(int)除外。
小于-1:将sig发送到ID为-pid的进程组中的每个进程
sig:该参数指定需要发送的信号,也可以设置为0,如果为0则表示不发送信号,但是执行检查错误,通常用于检查参数pid指定的进程是否出现。
返回值:成功返回0,失败返回-1,并设置errno。
2.raise():
可以向自身进程发送信息,函数原型如下
参数和返回值如下:
sig:需要发送的信号。
返回值:成功返回 0,失败返回非0值。
alarm()
使用该函数可以设置一个定时器,当定时器时间到时,内核会向进程发送SIGALRM信号,函数原型如下:
返回值和参数含义:
seconds:设置定时时间,以秒为单位,当参数为0时,则表示取消之前设置的alarm闹钟。
返回值:如果在调用alarm()时,之前已经为该进程设置的alarm闹钟还没有超时,则该闹钟的剩余值作为本次函数调用的返回值,之前设置的闹钟则被新的替代;否则返回0.
alarm闹钟不能循环触发,只能触发一次,若想要实现循环触发,可以在SIGALRM信号处理函数中再次调用alarm()设置定时器。
pause()函数
该函数可以使得进程暂停运行,进入休眠状态,直到进程捕获到一个信号为止,只有执行了信号处理函数并从其获得返回时,pause()才返回,在这种情况下返回-1,并且将errno设置为EINTR,函数原型如下。
信号集
信号集:能表示多个信号的数据类型---信号集(signal set )
信号集本质上就是sigset_t类型数据 结构
图片中的结构体可以表示一组信号,可以将多个信号添加到该数据结构中。
1初始化信号集:
sigemptyset()和sigfillset()用于初始化信号集,sigemptyset()初始化信号集,使其不包含任何信号,而sigfillset()函数初始化信号集,使其包含所有信号(包含所有实时信号),函数原型如下:
函数参数和返回值含义:
set:指向需要进行初始化的信号集变量。
返回值:成功返回0,失败返回-1,并设置errnno。
使用实例:
向信号集中添加/删除信号
分别使用sigaddset()和sigdelset()函数向信号集添加或者移除一个信号。函数原型如下:
函数参数和返回值含义如下:
set:指向信号集。
signum:需要添加或者删除的信号
返回值:成功返回0,失败返回-1.并设置errno。
使用示例:
测试信号是否在信号集中
使用sigismember()函数测试某一个信号是否在信号集中,函数原型如下:
函数参数和返回值含义如下:
set:指定信号集、
signum:需要测试的信号
返回值:在测试信号在信号集中,则返回1,如果不在信号集中,则返回0;失败则返回-1,并设置errno。
信号掩码(阻塞信号传递)
内核为每一个进程维护了一个信号掩码(其实就是一个信号集)。当进程收到一个属于信号掩码中定义的信号时,该信号将会被阻塞,无法传递给进程进行处理,那么内核会将其阻塞,直到信号从信号掩码中移除。
sigprocmask():函数参数和返回值定义如下:
how:指定了调用函数时的一些行为。
how可以设置一下宏。
SIG_BLOCK:将参数set指向的信号集内的所有信号添加到进程的信号掩码中
SIG_UNBLOCK:将参数set指向的信号集内的所有信号从进程信号掩码中移除。
SIG_SETMASK:进程信号掩码直接设置为参数set指向的信号集。
set:将参数set指向的信号集内的所有i新年好添加到信号掩码中或者从信号掩码中移除;如果set为NULL,则表示无需对当前信号掩码做出改动。
oldset:如果参数不为NULL,在向信号掩码中添加新的信号之前,获取到进程当前的信号掩码,存放在oldset所指定的信号集中;如果为NULL则表示不获取当前信号集。
返回值:成功返回0,失败返回-1,并设置errno。
阻塞等待信号
sigsuspend():更改i你好的信号掩码可以阻塞所选择的信号,或者解除对他们的阻塞,使用这种技术可以保护不希望由信号中断的关键代码段
实时信号
果进程当前正在执行信号处理函数,在处理信号期间接收到了新的信号,如果该信号是信号掩码中的 成员,那么内核会将其阻塞,将该信号添加到进程的等待信号集(等待被处理,处于等待状态的信号)中, 为了确定进程中处于等待状态的是哪些信号,可以使用 sigpending()函数获取。
sigpending():函数原型
参数和返回值含义如下:
set:处于等待状态的信号会存放到参数set所指向的信号集中
返回值:成功返回0,失败返回-1,并设置errno。
、
发送实时信号
使用sigqueue()函数发送实时信号:函数原型如下
参数和返回值含义如下:
pid:接收信号进程对应的pid。
sig:指定需要发送的信号。可以设定为0用于检查pid指定的进程是否存在。
value:参数指定信号的伴随数据:union sigval数据类型
返回值:成功返回0,失败返回-1,并设置errno。
异常退出函数abort():
而对于异常退出程序,则一般使用 abort()库函数,使 用 abort()终止进程运行,会生成核心转储文件,可用于判断程序调用 abort()时的程序状态。