📄 reliable.html
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"><HTML><HEAD> <TITLE>可靠信号</TITLE> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb2312-80"> <META NAME="COPYRIGHT" CONTENT="魏永明"> <META NAME="AUTHOR" CONTENT="魏永明"> <STYLE> <!-- H1 { color: #ffff00 } H2 { color: #ffff00 } H3 { color: #ff00ff } H4 { color: #ffff00 } TD P { color: #b880b8 } LI { color: #ffffff } P { color: #00ffff } PRE { color: #ffffff; font-family: "fixed" } A:link { color: #00b8ff } A:visited { color: #ff3366 } --> </STYLE></HEAD><BODY TEXT="#ffff00" LINK="#00b8ff" VLINK="#ff3366" BACKGROUND="images/velvet.jpg"><A HREF="unreliable.html"><IMG SRC="prev.gif" ALT="Previous"></A><A HREF="function.html"><IMG SRC="next.gif" ALT="Next"></A><A HREF="index.html"><IMG SRC="toc.gif" ALT="Contents"></A><H1 ALIGN=CENTER>6.3 可靠信号<BR><BR></H1><UL> <LI>术语和原语</LI> <LI>信号集及其操作</LI> <LI>可靠信号系统调用</LI> <LI>sigsetjmp 和 siglongjmp</LI> <LI>作业控制信号</LI></UL><H3>6.3.1 术语和原语</H3><UL> <LI>信号产生, 即发送给进程: 当导致信号的事件发生时</LI> <LI>信号递达: 当信号的动作发生时</LI> <LI>进程可阻塞信号的递达</LI><PRE>===============================================================================产生被进程阻塞的信号时, 如果信号的动作是默认动作和捕获信号, 则下列情形发生之前, 信号保持挂起: * 进程解开信号的阻塞 * 改变动作为忽略===============================================================================</PRE> <LI>系统在信号递达时才考虑如何处理阻塞信号, 而不是信号产生时</LI> <LI>如果阻塞信号再次产生, 则系统可选择多次递达信号, 或只递达一次.</LI> <LI>前者称为排队信号.</LI> <LI>常见系统对信号不进行排队. Linux?</LI> <LI>每个进程有一个定义当前阻塞信号集的信号掩码</LI> <LI>信号集和 select 函数中的文件描述符集一样, 是 POSIX 引人的新数据类型, 用来定义信号集合</LI></UL><H3>6.3.2 信号集及其操作</H3><UL> <LI>信号集的操作函数, sigsetops</LI><PRE>=============================================================================== #include <signal.h> int sigemptyset (sigset_t *set); int sigfillset (sigset_t *set); int sigaddset (sigset_t *set, int signum); int sigdelset (sigset_t *set, int signum); int sigismember (const sigset_t *set, int signum);-------------------------------------------------------------------------------上述函数的一种实现: #define sigemptyset (ptr) ( *(ptr) = 0 ) #define sigfillset (ptr) ( *(ptr) = ~(sigset_t)0, 0 ) /* C 语言的逗号运算符: 返回右边的值作为表达式的返回值 */ #include <signal.h> #include <errno.h> #define SIGBAD (signo) ((signo) <=0 || (signo) >= NSIG) int sigaddset (sigset_t *set, int signo) { if (SIGBAD (signo)) {errno = EINVAL; return -1;} *set != 1 << (signo -1); return 0; } int sigdelset (sigset_t *set, int signo) { if (SIGBAD (signo)) {errno = EINVAL; return -1;} *set &= ~(1 << (signo -1)); return 0; } int sigismember (sigset_t *set, int signo) { if (SIGBAD (signo)) {errno = EINVAL; return -1;} return ((*set & (1<< (signo - 1))) != 0); }===============================================================================</PRE></UL><H3>6.3.3 可靠信号系统调用</H3><UL> <LI>sigaction, sigprocmask, sigpending, sigsuspend</LI><PRE>=============================================================================== #include <signal.h> int sigaction (int signum, const struct sigaction *act, struct sigaction *oldact); int sigprocmask (int how, const sigset_t *set, sigset_t *oldset); int sigpending (sigset_t *set); int sigsuspend (const sigset_t *mask);------------------------------------------------------------------------------- struct sigaction { void (*sa_handler) (int); void (*sa_sigaction) (int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); // 废弃的元素, 不应使用 }-------------------------------------------------------------------------------sigaction: * sigaction 用于改变进程在接收到信号时的动作. * sigaction 可指定除 SIGKILL 和 SIGSTOP 之外的信号的动作 (act->sa_handler), 可取 SIG_DFL, SIG_IGN 和用户定义函数. * sigaction 可指定在处理 signum 信号时, 应该阻塞的信号集 (act->sa_mask). * sigaction 可指定信号处理过程中的行为 (act->sa_flags), 下述标志 "或" 的结果: o SA_NOCLDSTOP: 子进程停止时不接收 SIGCHLD. o SA_ONESHOT 或 SA_RESETHAND: 重置信号动作为默认值. o SA_RESTART: 如果该信号中断慢系统调用, 则重新启动系统调用. o SA_NOMASK 或 SA_NODEFER: 不要避免在信号处理函数中接收同一信号. o SA_SIGINFO: 信号处理函数接受三个参数. 这时, 必须设置 act->sa_sigaction 元素, 其中 siginfo_t 是内核传递到信号处理函数中的发生信号时进程的状态 信息. 详细信息, 可参阅 sigaction(2) 手册页. * oldact 非空时, 可返回先前的设置. * 利用 SA_SIGINFO 和 siginfo_t 实现有效的存储管理.-------------------------------------------------------------------------------sigprocmask: * sigprocmask 用于改变进程的当前阻塞信号集. * how 可取 SIG_BLOCK, SIG_UNBLOCK 和 SIG_MASKSET. 前两个动作分别在当前阻塞 信号集中添加或删除由 set 指定的信号集, SIG_MASK 用于完全设置阻塞信号集. * oldset 非空时, 可返回先前的设置.-------------------------------------------------------------------------------sigpending: * sigpending 用于检验挂起的信号.-------------------------------------------------------------------------------sigsuspend: * sigsuspend 用于在接收到某个信号之前, 临时用 mask 替换进程的信号掩码, 并 暂停进程执行. * sigsuspend 返回后将恢复调用之前的信号掩码. * 该系统调用始终返回 -1, 并将 errno 设置为 EINTR. * 该系统调用实际是阻塞并暂停两个动作的原子操作.===============================================================================</PRE> <LI>保护关键代码段示例</LI><PRE>=============================================================================== #include <signal.h> #include <stdio.h> #include <errno.h> #include <sys/types.h> #include <signal.h> #include <unistd.h> static void sig_int (int); void err_sys (const char* info) { perror (info); exit (1); } void pr_mask (const char* str) { sigset_t sigset; int errno_save; errno_save = errno; /* this function may be called by signal handler */ if (sigprocmask (0, NULL, &sigset) < 0) err_sys ("sigprocmask error"); printf ("%s", str); if (sigismember (&sigset, SIGINT)) printf ("SIGINT "); if (sigismember (&sigset, SIGQUIT)) printf ("SIGQUIT "); if (sigismember (&sigset, SIGUSR1)) printf ("SIGUSR1 "); if (sigismember (&sigset, SIGALRM)) printf ("SIGALRM "); printf ("\n"); errno = errno_save; } int main (void) { sigset_t newmask, oldmask, zeromask; if (signal (SIGINT, sig_int) == SIG_ERR) err_sys ("signal (SIGINT) error"); sigemptyset (&zeromask); sigemptyset (&newmask); sigaddset (&newmask, SIGINT); /* block SIGINT and save current signal mask */ if (sigprocmask (SIG_BLOCK, &newmask, &oldmask) < 0) err_sys ("SIG_BLOCK error"); /* critical region of code */ pr_mask ("in critical region: "); /* allow all signals and pause */ if (sigsuspend (&zeromask) != -1) err_sys ("sigsuspend error"); pr_mask ("after return from sigsuspend: "); /* reset signal mask whick unblocks SIGINT */ if (sigprocmask (SIG_SETMASK, &oldmask, NULL) < 0) err_sys ("SIG_SETMASK error"); /* and continue processing ... */ exit (0); } static void sig_int (int signo) { pr_mask ("\nin sig_int: "); return; }------------------------------------------------------------------------------- * 在 Linux 下, 必须包含头文件: #include <bsd/signal.h> 以便使用可靠的 signal 函数. 或者使用 sigaction 函数.===============================================================================</PRE></UL><H3>6.3.4 sigsetjmp 和 siglongjmp</H3><UL> <LI>sigsetjmp 和 siglongjmp 专用于从信号处理函数中进行远跳转</LI> <LI>并能够明确指定是否保存信号掩码</LI><PRE>=============================================================================== #include <setjmp.h> int sigsetjmp (sigjmp_buf env, int savesigs); void siglongjmp (sigjmp_buf env, int val);------------------------------------------------------------------------------- * 如果 savesigs 非零, 则 siglongjmp 在添转之后将恢复保存的信号掩码.===============================================================================</PRE></UL><H3>6.3.5 作业控制信号</H3><UL> <LI>#自学#</LI></UL><P><BR><BR></P><P ALIGN=CENTER><IMG SRC="images/striped.gif" NAME="Ruler" ALIGN=BOTTOM WIDTH=532 HEIGHT=13 BORDER=0></P><P><BR><BR></P><A HREF="unreliable.html"><IMG SRC="prev.gif" ALT="Previous"></A><A HREF="function.html"><IMG SRC="next.gif" ALT="Next"></A><A HREF="index.html"><IMG SRC="toc.gif" ALT="Contents"></A></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -