您的位置:首页 > 其它

实时信号

2016-02-15 14:18 127 查看
【摘自《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,以示需要再次发送该信号。

/* t_sigqueue.c */
#define _POSIX_C_SOURCE 199309
#include <signal.h>
#include "tlpi_hdr.h"

int main(int argc, char* argv[])
{
int sig, numSigs, j, sigData;
union sigval sv;

if (argc < 4 || strcmp(argv[1], "--help") == 0)
usageErr("%s pid sig-num data [num-sigs]\n", argv[0]);

/* Display our PID and UID, so that they can be compared with the
corresponding fields of the siginfo_t argument supplied to the
handler in the receiving process */
printf("%s: PID is %ld, UID is %ld\n", argv[0], (long)getpid(), (long)getuid());

sig = getInt(argv[2], 0, "sig-num");
sigData = getInt(argv[3], GN_ANY_BASE, "data");
numSigs = (argc > 4) ? getInt(argv[4], GN_GT_O, "num-sigs") : 1;

for (j = 0; j < numSigs; j++) {
sv.sival_int = sigData + j;
if (sigqueue(getLong(argv[1], 0, "pid"), sig, sv) == -1)
errExit("sigqueue %d", j);
}

exit(EXIT_SUCCESS);
}


处理实时信号

可以向标准信号一样,使用常规(单参数)信号处理器来处理实时信号。也可以用带有 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

/* catch_rtsigs.c */
#define _GNU_SOURCE
#include <string.h>
#include <signal.h>
#include "tlpi_hdr.h"

static volatile int handlerSleepTime;
static volatile int sigCnt = 0; /* Number of signals received */
static volatile int allDone = 0;

static void siginfoHandler(int sig, siginfo_t* si, void* ucontext)
{
/* UNSAFE: This handler uses non-async-signal-safe functions (printf())*/
/* SIGINT or SIGTERM can be used to terminate program */
if (sig == SIGINT || sig == SIGTERM) {
allDone = 1;
return;
}

sigCnt++;
printf("caugth signal %d\n", sig);
printf("    si_signo = %d, si_code = %d (%s), ", si->si_signo, si->si_code,
(si->si_code == SI_USER) ? "SI_USER" :
(si->si_code == SI_QUEUE) ? "SI_QUEUE" : "other");
printf("si_value = %d\n", si->si_value.sival_int);
printf("    si_pid = %ld, si_uid = %ld\n", (long)si->si_pid, (long)si->si_uid);

sleep(handlerSleepTime);
}

int main(int argc, char* argv[])
{
struct sigaction sa;
int sig;
sigset_t prevMask, blockMask;

if (argc > 1 && strcmp(argv[1], "--help") == 0)
usageErr("%s [block-time [handler-sleep-time]]\n", argv[0]);

printf("%s: PID is %ld\n", argv[0], (long)getpid());

handlerSleepTime = (argc > 2) ? getInt(argv[2], GN_NONNEG, "handler-sleep-time") : 1;

/* Establish handler for most signals, During execution of the handler,
mask all other signals to prevent handlers recursively interrupting
each other (which would make the output hard to read). */
sa.sa_sigaction = siginfoHandler;
sa.sa_flags = SA_SIGINFO;
sigfillset(&sa.sa_mask);

for (sig = 1; sig < NSIG; sig++)
if (sig != SIGTSTP && sig != SIGQUIT)
sigaction(sig, &sa, NULL);

/* Optionally block signals and sleep, allowing signals to be sent to us
before they are unblocked and handled */
if (argc > 1) {
sigfillset(&blockMask);
sigdelset(&blockMask, SIGINT);
sigdelset(&blockMask, SIGTERM);

if (sigprocmask(SIG_SETMASK, &blockMask, &prevMask) == -1)
errExit("sigprocmask");

printf("%s: signals blocked - sleeping %s seconds\n", argv[0], argv[1]);
sleep(getInt(argv[1], GN_GT_O, "block-time"));
printf("%s: sleep complete\n", argv[0]);

if (sigprocmask(SIG_SETMASK, &prevMask, NULL) == -1)
errExit("sigprocmask");
}

while (!allDone)
pause();
}


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() 的组合稍快一些。

/* t_sigwaitinfo.c */
#define _GNU_SOURCE
#include <string.h>
#include <signal.h>
#include <time.h>
#include "tlpi_hdr.h"

int main(int argc, char* argv[])
{
int sig;
siginfo_t si;
sigset_t allSigs;

if (argc > 1 && strcmp(argv[1], "--help") == 0)
usageErr("%s [delay-secs]\n", argv[0]);

printf("%s: PID is %ld\n", argv[0], (long)getpid());

/* Block all signals (except SIGKILL and SIGSTOP) */

sigfillset(&allSigs);
if (sigprocmask(SIG_SETMASK, &allSigs, NULL) == -1)
errExit("sigprocmask");

printf("%s: signals blocked\n", argv[0]);

if (argc > 1) { /* Delay so that signals can be sent to us */
printf("%s: about to delay %s seconds\n", argv[0], argv[1]);
sleep(getInt(argv[1], GN_GT_O, "delay-secs"));
printf("%s: finished delay\n", argv[0]);
}

for (;;) { /* Fetch signals until SIGINT (^c) or SIGTERM */
sig = sigwaitinfo(&allSigs, &si);
if (sig == -1)
errExit("sigwaitinfo");

if (sig == SIGINT || sig == SIGTERM)
exit(EXIT_SUCCESS);

printf("got signal: %d (%s)\n", sig, strsignal(sig));
printf("    si_signo = %d, si_code = %d (%s), ", si.si_signo, si.si_code,
(si.si_code == SI_USER) ? "SI_USER" :
(si.si_code == SI_QUEUE) ? "SI_QUEUE" : "other");
printf("si_value = %d\n", si.si_value.sival_int);
printf("    si_pid = %ld, si_uid = %ld\n", (long)si.si_pid, (long)si.si_uid);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: