实时信号
如果进程当前正在执行信号处理函数,在处理信号期间接收到了新的信号,如果该信号是信号掩码中的成员,那么内核会将其阻塞,将该信号添加到进程的等待信号集(等待被处理/处于等待状态的信号)中, 为了确定进程中处于等待状态的信号,可以使用 sigpending()函数获取。
sigpending()函数
其函数原型如下所示:
#include <signal.h>
int sigpending(sigset_t *set);
函数参数和返回值含义如下:
set:处于等待状态的信号会存放在参数 set 所指向的信号集中。
返回值:成功返回 0;失败将返回-1,并设置 errno。
使用示例
/* 定义信号集 */
sigset_t sig_set;
/* 将信号集初始化为空 */
sigemptyset(&sig_set);
/* 获取当前处于等待状态的信号 */
sigpending(&sig_set);
/* 判断 SIGINT 信号是否处于等待状态 */
if (1 == sigismember(&sig_set, SIGINT))
puts("SIGINT 信号处于等待状态");
else if (!sigismember(&sig_set, SIGINT))
puts("SIGINT 信号未处于等待状态")
发送实时信号
等待信号集只是一个掩码,仅表明一个信号是否发生,而不能表示其发生的次数。换言之,如果一个同一个信号在阻塞状态下产生了多次,那么会将该信号记录在等待信号集中,并在之后仅传递一次(仅当做发生了一次),这是标准信号的缺点之一。
实时信号较之于标准信号,其优势如下:
实时信号的信号范围有所扩大,可应用于应用程序自定义的目的,而标准信号仅提供了两个信号可用于应用程序自定义使用:SIGUSR1 和 SIGUSR2。 |
内核对于实时信号所采取的是队列化管理。如果将某一实时信号多次发送给另一个进程,那么将会 多次传递此信号。相反,对于某一标准信号正在等待某一进程,而此时即使再次向该进程发送此信 号,信号也只会传递一次。 |
当发送一个实时信号时,可为信号指定伴随数据(一整形数据或者指针值),供接收信号的进程在它的信号处理函数中获取。 |
不同实时信号的传递顺序得到保障。如果有多个不同的实时信号处于等待状态,那么将率先传递具有最小编号的信号。换言之,信号的编号越小,其优先级越高,如果是同一类型的多个信号在排队, 那么信号(以及伴随数据)的传递顺序与信号发送来时的顺序保持一致。 |
Linux 内核定义了 31 个不同的实时信号,信号编号范围为 34~64,使用 SIGRTMIN 表示编号最小的实时信号,使用 SIGRTMAX 表示编号最大的实时信号,其它信号编号可使用这两个宏加上一个整数或减去一 个整数。
应用程序当中使用实时信号,需要有以下的两点要求:
1、发送进程使用 sigqueue()系统调用向另一个进程发送实时信号以及伴随数据。
2、接收实时信号的进程要为该信号建立一个信号处理函数,使用sigaction函数为信号建立处理函数, 并加入 SA_SIGINFO,这样信号处理函数才能接收到实时信号及伴随数据,也就是要使用 sa_sigaction 指针指向的处理函数,如果使用 sa_handler则不能获取到实时信号的伴随数据。
使用 sigqueue()函数发送实时信号,其函数原型如下所示:
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
函数参数和返回值含义如下:
pid:指定接收信号的进程对应的 pid,将信号发送给该进程。
sig:指定需要发送的信号。与 kill()函数一样,也可将参数 sig 设置为 0,用于检查参数 pid 所指定的进 程是否存在。
value:参数 value 指定了信号的伴随数据,union sigval 数据类型。
返回值:成功将返回 0;失败将返回-1,并设置 errno。
union sigval 数据类型(共用体)如下所示:
typedef union sigval{
int sival_int;
void *sival_ptr;
} sigval_t;
携带的伴随数据,既可以指定一个整形的数据,也可以指定一个指针。
使用示例
(1)发送进程使用 sigqueue()系统调用向另一个进程发送实时信号
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int main(int argc, char *argv[]){
union sigval sig_val;
int pid;
int sig;
/* 判断传参个数 */
if (3 > argc)
exit(-1);
/* 获取用户传递的参数 */
pid = atoi(argv[1]);
sig = atoi(argv[2]);
printf("pid: %d\nsignal: %d\n", pid, sig);
/* 发送信号 */
sig_val.sival_int = 10; //伴随数据
if (-1 == sigqueue(pid, sig, sig_val)) {
perror("sigqueue error");
exit(-1);
}
puts("信号发送成功!");
exit(0);
}
(2)接收进程使用 sigaction()函数为信号绑定处理函数
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
static void sig_handler(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);
}
int main(int argc, char *argv[]){
struct sigaction sig = {0};
int num;
/* 判断传参个数 */
if (2 > argc)
exit(-1);
/* 获取用户传递的参数 */
num = atoi(argv[1]);
/* 为实时信号绑定处理函数 */
sig.sa_sigaction = sig_handler;
sig.sa_flags = SA_SIGINFO;
if (-1 == sigaction(num, &sig, NULL)) {
perror("sigaction error");
exit(-1);
}
/* 死循环 */
for ( ; ; )
sleep(1);
exit(0);
}
关于Linux信号的学习告一段落,接下来学习进程的知识。