实时信号之所以是可靠的,因为在进程阻塞该信号的时间内,发给该进程的所有实时信号会排队,而非实时信号则会合并为一个信号。

早期的kill函数只能向特定的进程发送一个特定的信号,并且早期的信号处理函数也不能接受附加数据。siqueue和sigaction解决了这个问题。平时没有有机会使用实时信号,所以想体验下它的排队特性。
       下面这个例子中,进程先屏蔽SIGINT和SIGRTMIN两个信号,其中SIGINT是非实时信号,而SIGRTMIN为实时信号,接着进程睡眠,睡眠完成之后再接触对这两个信号的屏蔽,此时可以比较对两种信号的处理方式是否一样。

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>

void sig_handler(int,siginfo_t*,void*);

int main(int argc,char**argv)
{
   struct sigaction act;
   sigset_t newmask, oldmask;
   int rc;
  
   sigemptyset(&newmask);

   //往信号集中添加一个非实时信号
   sigaddset(&newmask, SIGINT);

   //往信号集中添加一个实时信号
   sigaddset(&newmask, SIGRTMIN);

   //屏蔽实时信号SIGRTMIN
   sigprocmask(SIG_BLOCK, &newmask, &oldmask);

   act.sa_sigaction = sig_handler;
   act.sa_flags = SA_SIGINFO;

   if(sigaction(SIGINT, &act, NULL) < 0)
   {
       printf("install sigal error\n");
   }

   if(sigaction(SIGRTMIN, &act, NULL) < 0)
   {
       printf("install sigal error\n");
   }

   printf("pid = %d\n", getpid());
  
   //进程睡眠,在此时间内的发给该进程的所有实时信号
   //将排队,不会有信号丢失
   sleep(20);
  
   //解除对SIGRTMIN信号的屏蔽
   //信号处理函数将会被调用
   sigprocmask(SIG_SETMASK, &oldmask, NULL);

   return 0;
}
void sig_handler(int signum,siginfo_t *info,void *myact)
{
   if(signum == SIGINT)
       printf("Got a common signal\n");
   else
       printf("Got a real time signal\n");
}

将程序编译好之后,再开一个终端用于发送实时信号。
ecy@ecy-geek:~/pthreads$ ./sigqueue_receive
pid = 8871
进程开始睡眠……
在新的终端输入:
ecy@ecy-geek:~/pthreads$ kill -SIGRTMIN 8871
ecy@ecy-geek:~/pthreads$ kill -SIGRTMIN 8871
ecy@ecy-geek:~/pthreads$ kill -SIGRTMIN 8871
ecy@ecy-geek:~/pthreads$ kill -SIGRTMIN 8871
连续发送四个SIGRTMIN,接着回到之前的终端,连续四次按下"ctrl+c"。
^C^C^C^C
最后进程终于醒来,整个输出如下:
pid = 8871
^C^C^C^CGot a real time signal
Got a real time signal
Got a real time signal
Got a real time signal
Got a common signal
果然接受到四个实时信号,并且四次调用了信号处理函数,而对于SIGINT,虽然也按下了四次"ctrl+c",但是进程对其只做一次处理。我发现这儿有个细节有点意思,这个例子中是先发实时信号后发非实时信号,所以信号处理函数先处理实时信号,如果只是按照顺序注册信号的话,这很好理解,但是换一下,先按下了四次"ctrl+c"然后使用kill发四次实时信号,结果发现输出的结果仍然一样,这就有点怪了,这说明实时信号的优先级比非实时信号要高,内核每个进程的信号组成一个双向链表,实时信号插入的时候就不是随便插在尾部了。哎,不懂内核什么都要靠猜测,不爽~~在网上找到这样一段话:

信号的优先级:信号实质上是软中断,中断有优先级,信号也有优先级。如果一个进程有多个未决信号,则对于同一个未决的实时信号,内核将按照发送的顺序来递送信号。如果存在多个未决的实时信号,则值(或者说编号)越小的越先被递送。如果既存在不可靠信号,又存在可靠信号(实时信号),虽然POSIX对这一情况没有明确规 定,但Linux系统和大多数遵循POSIX标准的操作系统一样,将优先递送不可靠信号。
经过我反反复复地试验,我发现实验结果和上面描述的刚好相反,信号的编号越大越先被递送,一个进程如果处理SIGQUIT(3),SIGINT(2),SIGHUP(1)(通过"kill -l"可以查看信号的编号),那么先后给该进程发送SIGINT,SIGHUP,SIGQUIT,处理的顺序会是SIGQUIT,SIGINT,SIGHUP,不论改变这个三个信号的发送顺序,处理的顺序都是一样的。
Logo

更多推荐