LV027-实时信号
一、实时信号
1. 实时信号的优势
如果进程当前正在执行信号处理函数,在处理信号期间接收到了新的信号,且该信号是信号掩码中的成员,那么内核会将其阻塞,将该信号添加到进程的等待信号集(等待被处理,处于等待状态的信号)中,为了确定进程中处于等待状态的是哪些信号,可以使用sigpending()函数获取。
等待信号集只是一个掩码,仅表明一个信号是否发生,而不能表示其发生的次数。意思就是,如果一个同一个信号在阻塞状态下产生了多次,那么会将该信号记录在等待信号集中,并在之后仅传递一次(仅当做发生了一次),这个情况上边其实我们有遇到过就是在sigsuspend中遇到过,只是发生在任务处理函数中,不过这与发生在信号处理函数中是一样的,在执行不可被打断的操作时期间来的信号都只会被记录一次,后期也只会被处理一次,就是这是标准信号,也就是非实时信号的缺点之一。
相对于标准信号,实时信号有如下优势:
(1)实时信号的信号可应用于用户自定义的信号数量较多,标准信号仅提供了两个信号SIGUSR1和SIGUSR2用于应用程序自定义使用。
(2)内核对于实时信号所采取的是队列化管理。如果将某一实时信号多次发送给另一个进程,那么将会多次传递此信号。对于某一标准信号正在等待某一进程,而此时即使再次向该进程发送此信号,信号也只会传递一次。
(3)当发送一个实时信号时,我们可以为信号指定伴随数据(一个整形数据或者指针值),然后接收信号的进程就可以在它的信号处理函数中获取这些数据。
(4)不同实时信号的传递顺序得到保障。如果有多个不同的实时信号处于等待状态,那么将率先传递具有最小编号的信号。也就是说,信号的编号越小,其优先级越高,如果是同一类型的多个信号在排队,那么信号(以及伴随数据)的传递顺序与信号发送来时的顺序保持一致。
2. 使用实时信号
Linux内核定义了31个不同的实时信号,信号编号范围为34~64,使用SIGRTMIN表示编号最小的实时信号,使用SIGRTMAX表示编号最大的实时信号,其它信号编号可使用这两个宏加上一个整数或减去一个整数。
使用实时信号的过程中,需要注意的有以下两点:
(1)发送进程使用sigqueue()系统调用向另一个进程发送实时信号以及伴随数据。
(2)接收实时信号的进程要为该信号建立一个信号处理函数,为了更便于我们的操作,我们应该选择使用sigaction函数为信号建立处理函数,并加入SA_SIGINFO,这样信号处理函数才能够接收到实时信号以及伴随数据,也就是要使用sa_sigaction指针指向的处理函数,而不是sa_handler,当然也允许使用sa_handler,但这样就无法获取到实时信号的伴随数据了。
2.1 sigqueue() 函数
2.1.1 函数说明
在linux下可以使用man 3 sigqueue 命令查看该函数的帮助手册。
/* 需包含的头文件 */
#include <signal.h>
/* 函数声明 */
int sigqueue(pid_t pid, int sig, const union sigval value);【函数说明】该函数是一个发送信号的系统调用,主要是针对实时信号(当然也支持标准信号),支持信号带有数据,与函数sigaction()配合使用。
【函数参数】
pid:pid_t类型,指定接收信号的进程对应的pid,后续会将信号发送给该进程。sig:int类型,表示需要发送的信号。与kill()函数一样,也可将参数sig设置为0,用于检查参数pid所指定的进程是否存在。value:union sigval类型的共用体,它指定了信号的伴随数据。
在使用man 3 sigqueue 查看使用手册的时候,下边有union sigval 这个共用体的介绍。
union sigval
{
int sival_int;
void *sival_ptr;
};【返回值】int类型,成功返回0,失败返回-1,并设置errno。
【使用格式】一般情况下基本使用格式如下:
/* 需要包含的头文件 */
#include <signal.h>
/* 至少应该有的语句 */
union sigval sig_val;
sigqueue(pid, sig, sig_val);【注意事项】none
2.1.2 使用实例
后面看最后的例子即可。
二、使用实例
1. signalSend.c
/* 头文件 */
#include <stdio.h> /* perror getchar */
#include <stdlib.h> /* exit */
#include <unistd.h> /* sleep */
#include <signal.h> /* sigqueue */
int main(int argc, char *argv[])
{
union sigval sig_val;/* 信号携带的数据的共用体变量 */
pid_t pid;
int sig;
char ch;
int i = 0;
printf("Please enter PID , signal:");
scanf("%d %d", &pid, &sig);
while((ch = getchar()) != EOF && ch != '\n') ; //清除缓冲区的内容
for(i = 0; i < 3; i++)
{
sig_val.sival_int = i;
if (sigqueue(pid, sig, sig_val) == -1)
{
perror("sigqueue error");
exit(-1);
}
printf("Send success[data=%d]!\n", sig_val.sival_int);
sleep(1);
}
return 0;
}2. signalReceive.c
/* 头文件 */
#include <stdio.h> /* perror */
#include <unistd.h> /* sleep getpid */
#include <signal.h> /* sigaction */
void sigactionHandle(int sig, siginfo_t *info, void *context);
/* 主函数 */
int main(int argc, char *argv[])
{
struct sigaction act; /* 定义一个处理信号行为的结构体变量 */
/* 1.捕获信号 */
/* 1.1初始化处理信号行为的结构体变量 */
act.sa_sigaction = sigactionHandle; /* 指定信号处理函数 */
act.sa_flags = SA_SIGINFO; /* a_flags指定了一组标志,这些标志用于控制信号的处理过程 */
sigemptyset(&act.sa_mask); /* 将信号集初始化为空 */
/* 1.2设置捕捉信号 */
sigaction(SIGRTMIN, &act, NULL); /* Ctrl + c 发送该信号 */
while(1)
{
printf("Please send signal.[PID=%d]\n", getpid());
sleep(1);
}
return 0;
}
void sigactionHandle(int sig, siginfo_t *info, void *context)
{
static int count = 0;
int i = 0;
union sigval sig_val = info->si_value;
printf("I catch the signal [%d] %d times,data=%d \n", sig, ++count, sig_val.sival_int);
for(i = 1; i < 7; i++)
{
printf("sigactionHandle running![%d]\n", i);
sleep(1);
}
printf("sigactionHandle end!\n");
}3. Makefile
CC = gcc
DEBUG = -g -O2 -Wall
CFLAGS += $(DEBUG)
# 所有.c文件去掉后缀
TARGET_LIST = ${patsubst %.c, %, ${wildcard *.c}}
all : $(TARGET_LIST)
%.o : %.c
$(CC) $(CFLAGS) -c $< -o $@
.PHONY: all clean clean_o
clean : clean_o
@rm -vf $(TARGET_LIST)
clean_o :
@rm -vf *.o3. 测试效果
在终端执行以下命令编译程序:
make # 生成可执行文件
./signalSend # 在一个终端执行发送信号函数
./signalReceive # 在另一个终端执行信号接收函数然后,终端会有以下信息显示:
# 执行 ./signalSend 的终端
hk@vm:/mnt/hgfs/Sharedfiles/temp$ ./signalSend
Please enter PID , signal:30650 34
Send success[data=0]!
Send success[data=1]!
Send success[data=2]!
# 执行 ./signalReceive 的终端
hk@vm:~/2Sharedfiles/temp$ ./signalReceive
Please send signal.[PID=30650]
Please send signal.[PID=30650]
I catch the signal [34] 1 times,data=0
sigactionHandle running![1]
sigactionHandle running![2]
sigactionHandle running![3]
sigactionHandle running![4]
sigactionHandle running![5]
sigactionHandle running![6]
sigactionHandle end!
I catch the signal [34] 2 times,data=1
sigactionHandle running![1]
sigactionHandle running![2]
sigactionHandle running![3]
sigactionHandle running![4]
sigactionHandle running![5]
sigactionHandle running![6]
sigactionHandle end!
I catch the signal [34] 3 times,data=2
sigactionHandle running![1]
sigactionHandle running![2]
sigactionHandle running![3]
sigactionHandle running![4]
sigactionHandle running![5]
sigactionHandle running![6]
sigactionHandle end!
Please send signal.[PID=30650]会发现,发送的三次信号并未打断正在执行的信号处理函数,并且所有的信号都保留了下来。