实时信号
2016-02-15 14:18
127 查看
【摘自《Linux/Unix系统编程手册》】
较之于标准信号,实时信号的优势如下:
实时信号的信号范围有所扩大,可应用于应用程序自定义的目的。而标准信号中可供应用随意使用的信号仅有两个:SIGUSR1 和 SIGUSR2。
对实时信号所采取的是队列化管理。如果将某一实时信号的多个实例发送给一进程,那么将会多次传递信号。相反,如果某一标准信号已经在等待某一进程,而此时即使再次向该进程发送信号的实例,信号也只会传递一次。
当发送一个实时信号时,可为信号指定伴随数据(一整型数或者指针值),供接收进程的信号处理器获取。
不同实时信号的传递顺序得到保障。如果有多个不同的实时信号处于等待状态,那么将率先传递具有最小编号的信号。换言之,信号的编号越小,其优先级越高。如果是同一类型的多个信号在排队,那么信号(以及伴随数据)的传递顺序与信号发送来时的顺序保持一致。
使用实时信号
发送进程使用 sigqueue() 系统调用来发送信号及其伴随数据。
要为该信号建立一个处理器函数,接收进程应以 SA_SIGINFO 标志发起对 sigaction() 的调用。因此,调用信号处理器时就会附带额外参数,其中之一是实时信号的伴随数据。
在 Linux 中,即使接收进程在建立信号处理器时并未指定 SA_SIGINFO 标志,也能对实时信号进行队列化管理(但在这种情况下,将不可能获得信号的伴随数据)。
发送实时信号
系统调用 sigqueue() 将由 sig 指定的实时信号发送给有 pid 指定的进程
使用 sigqueue() 发送信号所需要的权限与 kill() 的要求一致。也可以发送空信号(即信号 0),其语义与 kill() 中的含义相同。(不同于 kill(),sigqueue() 不能通过将 pid 指定为负值而向整个进程组发送信号)
参数 value 指定了信号的伴随数据,具有以下形式:
对该参数的解释则取决于应用程序,由其选择对联合体(union)中的 sival_int 属性还是 sival_ptr 属性进行设置。sigqueue() 中很少使用 sival_ptr,因为指针的作用范围在进程内部,对于另一进程几乎没意义。
一旦触及对排队信号的数量限制,sigqueue() 调用将会失败,同时将 errno 置为 EAGAIN,以示需要再次发送该信号。
处理实时信号
可以向标准信号一样,使用常规(单参数)信号处理器来处理实时信号。也可以用带有 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
sigsuspend()
sigsuspend() 将解除信号阻塞和挂起进程这两个动作封装成一个原子操作。将以 mask 所指向的信号集来替换进程的信号掩码,然后挂起进程的执行,直到其捕获到信号,并从信号处理器中返回。一旦处理器返回,sigsuspend()会将进程信号掩码恢复为调用前的值。
调用 sigsuspend(),相当于以不可中断方式执行如下操作:
若 sigsuspend() 因信号的传递而中断,则将返回 -1,并将 errno 置为 EINTR。如果 mask 指向的地址无效,则 sigsuspend() 调用失败,并将 errno 置为 EFAULT。
以同步方式等待信号
使用 sigsuspend() 需要编写信号处理器函数,还需要应对信号异步传递所带来的复杂性。对于某些应用而言,这种方法过于复杂。作为替代方案,可以利用 sigwaitinfo() 系统调用来同步接收信号。
sigwaitinfo() 系统调用挂起进程的执行,直至 set 指向信号集中的某一信号抵达。如果调用 sigwaitinfo() 时,set 中的某一信号已经处于等待状态,那么 sigwaitinfo() 将立即返回。传递来的信号就此从进程的等待信号队列中移除,并且将返回信号编号作为函数结果。info 参数如果不为空,则会指向经过初始化处理的 siginfo_t 结构,其中所含信息与提供给信号处理器函数的 siginfo_t 参数相同。
sigwaitinfo() 所接受信号的传递顺序和排队特性与信号处理器所捕获的信号相同,就是说,不对标准信号进行排队处理,对实时信号进行排队处理,并且对实时信号的传递遵循低编号优先的原则。
除了卸去编写信号处理器的负担之外,使用 sigwaitinfo() 来等待信号也要比信号处理器外加 sigsuspend() 的组合稍快一些。
较之于标准信号,实时信号的优势如下:
实时信号的信号范围有所扩大,可应用于应用程序自定义的目的。而标准信号中可供应用随意使用的信号仅有两个: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); } }
相关文章推荐
- Android数据存储(二)----PreferenceFragment详解
- 装饰器
- 解读分库分表中间件Sharding-JDBC
- 复制介绍
- iOS-关闭应用时想让应用直接退出的方法
- 配置vimrc
- mysql的安装配置Linux
- 关于spring <mvc:resources> 标签的使用
- 放肆地使用UIBezierPath和CAShapeLayer画各种图形
- OpenCV 2.4.x 功能模块介绍
- Linux中top命令参数详解
- mysql使用ROW_COUNT()返回插入、更新、删除操作影响行数
- cygwin中运行命令提示command not found的解决方法
- 子网掩码
- Fleury 算法,求欧拉回路
- Lua string用法
- java开发中的23种设计模式
- 利用Google浏览器自定义搜索
- 第40讲:Set、Map、TreeSet、TreeMap操作代码实战
- uva 1204 Fun Game 有趣的游戏 字符串集合的动态规划