实时信号

【摘自《Linux/Unix系统编程手册》】

较之于标准信号,实时信号的优势如下:

  • 实时信号的信号范围有所扩大,可应用于应用程序自定义的目的。而标准信号中可供应用随意使用的信号仅有两个:SIGUSR1 和 SIGUSR2。
  • 对实时信号所采取的是队列化管理。如果将某一实时信号的多个实例发送给一进程,那么将会多次传递信号。相反,如果某一标准信号已经在等待某一进程,而此时即使再次向该进程发送信号的实例,信号也只会传递一次。
  • 当发送一个实时信号时,可为信号指定伴随数据(一整型数或者指针值),供接收进程的信号处理器获取。
  • 不同实时信号的传递顺序得到保障。如果有多个不同的实时信号处于等待状态,那么将率先传递具有最小编号的信号。换言之,信号的编号越小,其优先级越高。如果是同一类型的多个信号在排队,那么信号(以及伴随数据)的传递顺序与信号发送来时的顺序保持一致。

使用实时信号

  • 发送进程使用 sigqueue() 系统调用来发送信号及其伴随数据。
  • 要为该信号建立一个处理器函数,接收进程应以 SA_SIGINFO 标志发起对 sigaction() 的调用。因此,调用信号处理器时就会附带额外参数,其中之一是实时信号的伴随数据。

在 Linux 中,即使接收进程在建立信号处理器时并未指定 SA_SIGINFO 标志,也能对实时信号进行队列化管理(但在这种情况下,将不可能获得信号的伴随数据)。

发送实时信号

系统调用 sigqueue() 将由 sig 指定的实时信号发送给有 pid 指定的进程

#define _POSIX_C_SOURCE 199309
#include <signal.h>

int sigqueue(pid_t pid, int sig, const union sigval value);
        Returns 0 on success, or -1 on error

使用 sigqueue() 发送信号所需要的权限与 kill() 的要求一致。也可以发送空信号(即信号 0),其语义与 kill() 中的含义相同。(不同于 kill(),sigqueue() 不能通过将 pid 指定为负值而向整个进程组发送信号)

参数 value 指定了信号的伴随数据,具有以下形式:

union sigval {
   int sival_int; /* Integer value for accompanying data */
   void* sival_ptr; /* Pointer value for accompanying data */
};

对该参数的解释则取决于应用程序,由其选择对联合体(union)中的 sival_int 属性还是 sival_ptr 属性进行设置。sigqueue() 中很少使用 sival_ptr,因为指针的作用范围在进程内部,对于另一进程几乎没意义。

一旦触及对排队信号的数量限制,sigqueue() 调用将会失败,同时将 errno 置为 EAGAIN,以示需要再次发送该信号。

 1 /* t_sigqueue.c */
 2 #define _POSIX_C_SOURCE 199309
 3 #include <signal.h>
 4 #include "tlpi_hdr.h"
 5
 6 int main(int argc, char* argv[])
 7 {
 8     int sig, numSigs, j, sigData;
 9     union sigval sv;
10
11     if (argc < 4 || strcmp(argv[1], "--help") == 0)
12         usageErr("%s pid sig-num data [num-sigs]\n", argv[0]);
13
14     /* Display our PID and UID, so that they can be compared with the
15          corresponding fields of the siginfo_t argument supplied to the
16          handler in the receiving process */
17     printf("%s: PID is %ld, UID is %ld\n", argv[0], (long)getpid(), (long)getuid());
18
19     sig = getInt(argv[2], 0, "sig-num");
20     sigData = getInt(argv[3], GN_ANY_BASE, "data");
21     numSigs = (argc > 4) ? getInt(argv[4], GN_GT_O, "num-sigs") : 1;
22
23     for (j = 0; j < numSigs; j++) {
24         sv.sival_int = sigData + j;
25         if (sigqueue(getLong(argv[1], 0, "pid"), sig, sv) == -1)
26             errExit("sigqueue %d", j);
27     }
28
29     exit(EXIT_SUCCESS);
30 }

处理实时信号

可以向标准信号一样,使用常规(单参数)信号处理器来处理实时信号。也可以用带有 3 个参数的信号处理器函数来处理实时信号,其建立则会用到 SA_SIGINFO 标志。一旦采用了 SA_SIGINFO 标志,传递给信号处理器函数的第二个参数将是一个 siginfo_t 结构,内含实时信号的附加信息,会设置如下字段:

  • si_signo 字段,其值与传递给信号处理器函数的第一个参数相同
  • si_code 字段表示信号来源。对于通过 sigqueue() 发送的实时信号来说,该字段值总是为 SI_QUEUE
  • si_value 字段所含数据,由进程于使用 sigqueue() 发送信号时在 value 参数(sigval union)中指定。
  • si_pid 和 si_uid 字段分别包含信号发送进程的进程 ID 和实际用户 ID
 1 /* catch_rtsigs.c */
 2 #define _GNU_SOURCE
 3 #include <string.h>
 4 #include <signal.h>
 5 #include "tlpi_hdr.h"
 6
 7 static volatile int handlerSleepTime;
 8 static volatile int sigCnt = 0; /* Number of signals received */
 9 static volatile int allDone = 0;
10
11 static void siginfoHandler(int sig, siginfo_t* si, void* ucontext)
12 {
13     /* UNSAFE: This handler uses non-async-signal-safe functions (printf())*/
14     /* SIGINT or SIGTERM can be used to terminate program */
15     if (sig == SIGINT || sig == SIGTERM) {
16         allDone = 1;
17         return;
18     }
19
20     sigCnt++;
21     printf("caugth signal %d\n", sig);
22     printf("    si_signo = %d, si_code = %d (%s), ", si->si_signo, si->si_code,
23         (si->si_code == SI_USER) ? "SI_USER" :
24         (si->si_code == SI_QUEUE) ? "SI_QUEUE" : "other");
25     printf("si_value = %d\n", si->si_value.sival_int);
26     printf("    si_pid = %ld, si_uid = %ld\n", (long)si->si_pid, (long)si->si_uid);
27
28     sleep(handlerSleepTime);
29 }
30
31 int main(int argc, char* argv[])
32 {
33     struct sigaction sa;
34     int sig;
35     sigset_t prevMask, blockMask;
36
37     if (argc > 1 && strcmp(argv[1], "--help") == 0)
38         usageErr("%s [block-time [handler-sleep-time]]\n", argv[0]);
39
40     printf("%s: PID is %ld\n", argv[0], (long)getpid());
41
42     handlerSleepTime = (argc > 2) ? getInt(argv[2], GN_NONNEG, "handler-sleep-time") : 1;
43
44     /* Establish handler for most signals, During execution of the handler,
45          mask all other signals to prevent handlers recursively interrupting
46          each other (which would make the output hard to read). */
47     sa.sa_sigaction = siginfoHandler;
48     sa.sa_flags = SA_SIGINFO;
49     sigfillset(&sa.sa_mask);
50
51     for (sig = 1; sig < NSIG; sig++)
52         if (sig != SIGTSTP && sig != SIGQUIT)
53             sigaction(sig, &sa, NULL);
54
55     /* Optionally block signals and sleep, allowing signals to be sent to us
56          before they are unblocked and handled */
57     if (argc > 1) {
58         sigfillset(&blockMask);
59         sigdelset(&blockMask, SIGINT);
60         sigdelset(&blockMask, SIGTERM);
61
62         if (sigprocmask(SIG_SETMASK, &blockMask, &prevMask) == -1)
63             errExit("sigprocmask");
64
65         printf("%s: signals blocked - sleeping %s seconds\n", argv[0], argv[1]);
66         sleep(getInt(argv[1], GN_GT_O, "block-time"));
67         printf("%s: sleep complete\n", argv[0]);
68
69         if (sigprocmask(SIG_SETMASK, &prevMask, NULL) == -1)
70             errExit("sigprocmask");
71     }
72
73     while (!allDone)
74         pause();
75 }

sigsuspend()

#include <signal.h>

int sigsuspend(const sigset_t* mask);
        (Normally) returns -1 with errno set to EINTR

sigsuspend() 将解除信号阻塞和挂起进程这两个动作封装成一个原子操作。将以 mask 所指向的信号集来替换进程的信号掩码,然后挂起进程的执行,直到其捕获到信号,并从信号处理器中返回。一旦处理器返回,sigsuspend()会将进程信号掩码恢复为调用前的值。

调用 sigsuspend(),相当于以不可中断方式执行如下操作:

sigprocmask(SIG_SETMASK, &mask, &prevMask); /* Assign new mask */
pause();
sigprocmask(SIG_SETMASK, &prevMask, NULL); /* Restore old mask */

若 sigsuspend() 因信号的传递而中断,则将返回 -1,并将 errno 置为 EINTR。如果 mask 指向的地址无效,则 sigsuspend() 调用失败,并将 errno 置为 EFAULT。

以同步方式等待信号

使用 sigsuspend() 需要编写信号处理器函数,还需要应对信号异步传递所带来的复杂性。对于某些应用而言,这种方法过于复杂。作为替代方案,可以利用 sigwaitinfo() 系统调用来同步接收信号。

#define _POSIX_C_SOURCE 199309
#include <signal.h>

int sigwaitinfo(const sigset_t* set, siginfo_t* info);
        Returns number of delivered signal on success, or -1 on error

sigwaitinfo() 系统调用挂起进程的执行,直至 set 指向信号集中的某一信号抵达。如果调用 sigwaitinfo() 时,set 中的某一信号已经处于等待状态,那么 sigwaitinfo() 将立即返回。传递来的信号就此从进程的等待信号队列中移除,并且将返回信号编号作为函数结果。info 参数如果不为空,则会指向经过初始化处理的 siginfo_t 结构,其中所含信息与提供给信号处理器函数的 siginfo_t 参数相同。

sigwaitinfo() 所接受信号的传递顺序和排队特性与信号处理器所捕获的信号相同,就是说,不对标准信号进行排队处理,对实时信号进行排队处理,并且对实时信号的传递遵循低编号优先的原则。

除了卸去编写信号处理器的负担之外,使用 sigwaitinfo() 来等待信号也要比信号处理器外加 sigsuspend() 的组合稍快一些。

 1 /* t_sigwaitinfo.c */
 2 #define _GNU_SOURCE
 3 #include <string.h>
 4 #include <signal.h>
 5 #include <time.h>
 6 #include "tlpi_hdr.h"
 7
 8 int main(int argc, char* argv[])
 9 {
10     int sig;
11     siginfo_t si;
12     sigset_t allSigs;
13
14     if (argc > 1 && strcmp(argv[1], "--help") == 0)
15         usageErr("%s [delay-secs]\n", argv[0]);
16
17     printf("%s: PID is %ld\n", argv[0], (long)getpid());
18
19     /* Block all signals (except SIGKILL and SIGSTOP) */
20
21     sigfillset(&allSigs);
22     if (sigprocmask(SIG_SETMASK, &allSigs, NULL) == -1)
23         errExit("sigprocmask");
24
25     printf("%s: signals blocked\n", argv[0]);
26
27     if (argc > 1) { /* Delay so that signals can be sent to us */
28         printf("%s: about to delay %s seconds\n", argv[0], argv[1]);
29         sleep(getInt(argv[1], GN_GT_O, "delay-secs"));
30         printf("%s: finished delay\n", argv[0]);
31     }
32
33     for (;;) { /* Fetch signals until SIGINT (^c) or SIGTERM */
34         sig = sigwaitinfo(&allSigs, &si);
35         if (sig == -1)
36             errExit("sigwaitinfo");
37
38         if (sig == SIGINT || sig == SIGTERM)
39             exit(EXIT_SUCCESS);
40
41         printf("got signal: %d (%s)\n", sig, strsignal(sig));
42         printf("    si_signo = %d, si_code = %d (%s), ", si.si_signo, si.si_code,
43             (si.si_code == SI_USER) ? "SI_USER" :
44             (si.si_code == SI_QUEUE) ? "SI_QUEUE" : "other");
45         printf("si_value = %d\n", si.si_value.sival_int);
46         printf("    si_pid = %ld, si_uid = %ld\n", (long)si.si_pid, (long)si.si_uid);
47     }
48 }
时间: 2024-10-03 19:04:05

实时信号的相关文章

实时信号和非实时信号

#include <assert.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> void myHandler(int num) { int ret = 0; if (SIGUSR1 == num) { sigset_t set; ret = sigemptyset(&se

Linux信号、信号处理和信号处理函数

信号(signal)是一种软件中断,它提供了一种处理异步事件的方法,也是进程间惟一的异步通信方式.在Linux系统中,根据POSIX标准扩展以后的信号机制,不仅可以用来通知某种程序发生了什么事件,还可以给进程传递数据. 一.信号的来源 信号的来源可以有很多种试,按照产生条件的不同可以分为硬件和软件两种. 1.  硬件方式 当用户在终端上按下某键时,将产生信号.如按下组合键后将产生一个SIGINT信号. 硬件异常产生信号:除数据.无效的存储访问等.这些事件通常由硬件(如:CPU)检测到,并将其通知

LINUX 信号概念详解

LINUX 信号概念详解 我们运行如下命令,可看到Linux支持的信号列表: # kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD 18) SIGCONT 19) SIGSTOP

Linux信号列表

我们运行如下命令,可看到Linux支持的信号列表:~$ kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR213) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) S

Linux高性能server编程——信号及应用

?? 信号 信号是由用户.系统或者进程发送给目标进程的信息.以通知目标进程某个状态的改变或系统异常. Linux信号可由例如以下条件产生: 对于前台进程.用户能够通过输入特殊的终端字符来给它发送信号.比方输入Ctrl+C一般会给进程发送一个终端信号. 2.系统异常 系统状态变化 执行kill命令或调用kill函数 Linux信号概述 发送信号 Linux下,一个进程给其它进程发送信号的API是kill函数.其定义例如以下: #include <sys/types.h> #include <

Linux信号(signal) 机制分析

[摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核对于信号的处理流程包括信号的触发/注册/执行及注销等.最后介绍了应用层的相关处理,主要包括信号处理函数的安装.信号的发送.屏蔽阻塞等,最后给了几个简单的应用实例. [关键字]软中断信号,signal,sigaction,kill,sigqueue,settimer,sigmask,sigprocmask,sigset_t 1       信

进程间通信(五)—信号

我会用几篇博客总结一下在Linux中进程之间通信的几种方法,我会把这个开头的摘要部分在这个系列的每篇博客中都打出来 进程之间通信的方式 管道 消息队列 信号 信号量 共享存储区 套接字(socket) 进程间通信(三)—信号量传送门:http://www.cnblogs.com/lenomirei/p/5649792.html 进程间通信(二)—消息队列传送门:http://www.cnblogs.com/lenomirei/p/5642575.html 进程间通信(一)—管道传送门:http:

信号的基本概念

1.信号 信号主要用来通知进程发生了异步事件,而不会给进程传递任何数据.信号总共有62个,前32个被称为普通信号,34-64被称为实时信号.通常只关心普通信号. 2.信号的产生 1> 键盘 通过组合键发送一个信号,一定是给前台进程的.例如ctrl+c 2>用系统函数发送信号 可以给指定进程发送信号,例如kill命令用kill()函数实现,abort函数是当前进程接收到信号而异常终止.raise自己给自己发送信号. 3>由软件条件产生 alarm(时间数),当alarm完成后直接终止进程.

信号“未决”与“阻塞”

http://blog.csdn.net/sunyubo458/article/details/4484957 信号状态:    信号的”未决“是一种状态,指的是从信号的产生到信号被处理前的这一段时间:信号的”阻塞“是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生.     APUE例题在sleep前用sigprocmask阻塞了退出信号,然后sleep,然后在sleep的过程中产生一个退出信号,但是此时退出信号被阻塞过,(中文的”阻塞”在这里容易被误解为一种状态,实际上是一种类似于开关