Linux中的 实时信号 (又称为 可靠信号 )在被阻塞的时候,不会像 非实时信号 ( 不可靠信号 )那样丢失。
因为 非实时信号的等待信号集是以集合的数据结构进行管理的 ,所谓集合有一个重要的性质就是元素不能够重复,而 实时信号是以队列的形式来管理等待信号的,所以不会存在信号丢失的问题
对于 非实时信号 ,当进程的信号掩码中存在某个非实时信号,那么当这个非实时信号发送给进程的时候,会被内核阻塞,内核会将其放在一个叫做等待信号集的数据结构里面,只有此信号从进程的信号掩码中移除之后,内核才会把等待信号集中的该信号发送给进程,如果在信号阻塞期间,又发送来了相同的信号,那么由于集合的元素不可重复,所以这个重复发送的信号将会被内核丢弃,称为信号丢失,当解除阻塞之后,仅仅会响应一次被阻塞的信号处理函数
对于实时信号而言,由于等待信号(即被内核阻塞期间又发送同样的信号)采用队列管理,所以不存在信号丢失的情况发生,如果某个信号在阻塞期间又多次发送,那么内核会记录信号发送的次数。也就是说:实时信号是可以在阻塞期间重复发送,而不会丢失的,当解除阻塞之后,会多次响应实时信号的信号处理函数!
怎么验证实时信号不可以被阻塞呢?
当然是实践出真知喽:下面通过代码来实践一下:
程序1:阻塞接收来此其他进程发送的实时信号
/**
****************************************************************************************
* @file 09_sigaction.c
* @author GuiStar-李什么恩
* @version V1.1.0
* @date 2023-4-14
* @brief 挂起进程,阻塞参数1指定的实时信号,当接收到SIGINT信号之后,进程会被唤醒
* 并取消对参数1指定的实时信号的阻塞(即从信号掩码中移除),此程序用来验证实
* 时信号不会丢失
****************************************************************************************
*/
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
static void sig_handler1(int sig)
{
printf("接收到SIGINT,即将唤醒程序!\n");
}
static void sig_handler2(int sig, siginfo_t *info, void *context)
{
sigval_t sig_val = info->si_value;
printf("接收到实时信号: %d\n", sig);
printf("伴随数据为: %d\n", sig_val.sival_int);
printf("发送方的PID是:%d\n",info->si_pid);
}
int main(int argc, char *argv[])
{
struct sigaction sig = {0};
sigset_t sig_set;
int num;
/* 判断传参个数 */
if (argc<2)
{
printf("参数过少\n");
exit(-1);
}
/* 获取用户传递的参数 */
num = atoi(argv[1]);
sigemptyset(&sig_set);
sigaddset(&sig_set, num);
/* 为实时信号绑定处理函数 */
sig.sa_handler = sig_handler1;
sig.sa_flags = 0;
if (sigaction(SIGINT, &sig, NULL)==-1)
{
perror("sigaction error");
exit(-1);
}
sig.sa_sigaction = sig_handler2;
sig.sa_flags = SA_SIGINFO;
if (sigaction(num, &sig, NULL)==-1)
{
perror("sigaction error");
exit(-1);
}
/*挂起此进程,等待SIGINT信号来唤醒,同时阻塞用户给此进程参数指定的实时信号*/
printf("开始挂起进程,同时阻塞%d信号\n",num);
if (sigsuspend(&sig_set)!=-1)
exit(-1);
printf("程序唤醒成功!\n");
exit(0);
}
程序2:向某个进程发送实时信号:
/**
****************************************************************************************
* @file 09_sigqueue.c
* @author GuiStar-李什么恩
* @version V1.1.0
* @date 2023-4-14
* @brief 向某个进程发送实时信号,参数1指定接受进程的pid号,参数2指定要发送的实时信号的编号
****************************************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int main(int argc, char *argv[])
{
union sigval sig_val;
int pid;
int sig;
/* 判断传参个数 */
if (argc<3)
{
puts("参数过少,参数一:pid,参数二:实时信号");
exit(-1);
}
/* 获取用户传递的参数 */
pid = atoi(argv[1]);
sig = atoi(argv[2]);
printf("pid: %d\nsignal: %d\n", pid, sig);
/* 发送信号 */
sig_val.sival_int = 10; //伴随数据
if (sigqueue(pid, sig, sig_val)==-1)
{
perror("sigqueue error");
exit(-1);
}
puts("信号发送成功!");
exit(0);
}
运行结果:
- 首先运行程序1,程序首先会挂起,会阻塞指定的实时信号34,如果按下ctrl+c,发送SIGINT信号,则程序会被唤醒,同时解除对实时信号34的阻塞
- 然后,在查询到程序1的pid之后,多次运行程序2,向程序1的进程多次发送实时信号34。此时进程1是被挂起的,并且实时信号34被内核阻塞,所以在进程1的终端看不到响应信息。只有取消阻塞,进程1才会响应这些实时信号34
- 按下ctrl+c,发送SIGINT信号,进程1被唤醒从下图中可以看出,进程1确实多次响应了在阻塞期间发送的实时信号34
至此实时信号不会被丢弃得到验证!