前言
本篇文章我们来讲解传统信号和实时信号,这里我们将从实际应用给大家讲解。
一、传统信号和实时信号概念
传统信号(Traditional Signals):
传统信号是Unix系统早期定义的信号类型,被称为标准信号(Standard Signals)。
传统信号的范围是1到31,用整数方式表示,例如,SIGINT 是2,SIGALRM 是14。
传统信号的处理方式是异步的,即信号发送后立即触发信号处理程序执行,中断当前进程的正常执行流程。
传统信号的处理程序是函数指针,可以由进程注册自定义的信号处理函数,用于在接收到信号时执行特定的操作。
如果进程没有为某个信号注册处理函数,操作系统将采用默认的处理方式,例如终止进程、忽略信号或者产生核心转储文件。
实时信号(Real-Time Signals):
实时信号是在POSIX.1b标准中引入的一种更高级的信号机制。
实时信号的范围是从实时信号1(SIGRTMIN)到实时信号31(SIGRTMAX),共计64个信号。
实时信号的处理方式可以是同步的或异步的,具体取决于进程设置的信号发送和接收机制。
实时信号的处理程序是函数指针,可以由进程注册自定义的信号处理函数,用于在接收到信号时执行特定的操作。
实时信号的一个重要特点是可以传递一个整数值作为附加数据,这使得实时信号在进程间通信中非常有用。
实时信号相对于传统信号具有更高的灵活性和可靠性,且能够提供更细粒度的信号处理。传统信号在某些情况下可能会发生竞争条件或丢失信号的问题,而实时信号可以帮助解决这些问题。
二、重要函数介绍
1.sigfillset()函数:
int sigfillset(sigset_t *set);
sigfillset()函数将在指定的信号集 set 中设置所有的信号,相当于创建一个包含系统上所有信号的完整集合。信号集 set 会被填充满。
2.sigaddset()函数:
int sigaddset(sigset_t *set, int signum);
sigaddset()函数用于将指定的信号 signum 添加到信号集 set 中。这允许我们将多个信号添加到信号集中,以便后续对其进行操作。
3.sigdelset()函数:
int sigdelset(sigset_t *set, int signum);
sigdelset()函数用于从信号集 set 中删除指定的信号 signum。这个函数可以用来从信号集中删除不再感兴趣的信号。
sigemptyset()函数:
int sigemptyset(sigset_t *set);
sigemptyset()函数用于清空信号集 set,即将所有信号从信号集中删除,使其成为空集。
sigprocmask()函数:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
sigprocmask()函数用于设置进程的信号屏蔽字,控制哪些信号可以传递给进程或阻塞进程对信号的接收。
参数 how 指定了信号屏蔽字的操作方式,可以是以下三个值之一:
SIG_BLOCK:将 set 中的信号添加到进程的信号屏蔽字中。
SIG_UNBLOCK:从进程的信号屏蔽字中移除 set 中的信号。
SIG_SETMASK:设置进程的信号屏蔽字为 set。
参数 set 指定了要添加、删除或设置的信号集。
参数 oldset 是一个可选参数,用于存储之前的信号屏蔽字。
三、实时信号和传统信号被处理的次数
在处理传统信号时,操作系统默认情况下只保留一个挂起的信号。如果在处理信号时,相同类型的信号再次到达进程,且之前的信号尚未处理完毕,则后续到达的信号会被丢弃。
实时信号使用了队列的机制,即使在处理实时信号时,多个相同类型的信号会被排队等待处理。
实时信号有一个优先级的概念,具有更高优先级的实时信号会在低优先级实时信号之前得到处理。
代码实验:
接收端:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #include <sys/types.h> #include <unistd.h> static int g_count = 0;//计数值 static int g_sig = 0;//信号值 void signal_Handle(int sig, siginfo_t* info, void* ucontext) { if(sig = g_sig) { g_count++; } } int main(int argc, char** argv) { int i = 0; printf("pid :%d\n", getpid()); sigset_t set = {0}; g_sig = atoi(argv[1]); struct sigaction act = {0}; act.sa_sigaction = signal_Handle; act.sa_flags = SA_RESTART | SA_SIGINFO; /* 添加信号屏蔽字 */ /* 下面信号在信号处理程序执行时会被暂时阻塞 */ sigaddset(&act.sa_mask, g_sig); /* 设置信号的处理行为,设置后40和SIGINT信号将由act里面的信号处理函数处理 */ sigaction(g_sig, &act, NULL); sigaction(SIGINT, &act, NULL); /* 屏蔽所有信号 */ sigfillset(&set); sigprocmask(SIG_SETMASK, &set, NULL); for(i = 0; i < 15; i++) { printf("i : %d\n", i); sleep(1); } /* 解除所有屏蔽 */ sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, NULL); printf("g_count = %d\n", g_count); return 0; }
发送端:
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <signal.h> int main(int argc, char** argv) { pid_t pid = atoi(argv[1]); int sig = atoi(argv[2]); int i = 0; union sigval sv = {123456}; for(i = 0; i < 15000; i++) { sigqueue(pid, sig, sv); } raise(SIGINT); return 0; }
实时信号:
传统信号:
从上面的运行结果可以直接看出来:实时信号不会丢失,而传统信号则会丢失。
总结
本篇文章就讲解到这里,下篇文章见。