读书笔记-APUE第三版-(10)信号
来源:互联网 发布:买彩软件 编辑:程序博客网 时间:2024/05/07 20:56
试用一下CSDN博客的在线markdown编辑器,感觉还不错:)
信号概念
信号是一种软件中断,用于提供异步事件处理机制。以下情形会产生信号:
- 终端键盘输入,比如Ctrl+c(SIGINT)。
- 硬件异常,比如除零&浮点数溢出(SIGFPE),非法内存引用(SIGSEGV)等。硬件探测到异常后通知内核,内核向应用进程发送信号。
- kill函数或者kill命令给指定进程/进程组发送各种信号(不局限于杀死进程);abort函数发送SIGABRT信号导致进程非正常退出等。
- 软件产生信号,比如网络连接中的带外数据(SIGURG),向管道写入数据但无读取方(SIGPIPE),闹钟定时器alarm超时(SIGALARM),还有前一章中作业控制中的一些场景也会产生信号,子进程退出向父进程发送信号(SIGCHLD,可以在处理SIGCHLD信号时使用wait),Ctrl+z停止前台进程组(SIGTSTP),后台进程组需要进行输入/输出(SIGTTIN/SIGTTOU)等。
对信号的处理方式有忽略(不能忽略SIGKILL、SIGSTOP和硬件异常)、默认动作(大部分都是终止进程,部分还会产生core)和设置自定义处理函数三种。
signal函数
void (*signal(int signo, void (*func)(int))(int);或者:typedef void Sigfunc(int);Sigfunc *signal(int, Sigfunc *);#define SIG_ERR (void (*)())-1 #define SIG_DFL (void (*)())0 默认动作#define SIG_IGN (void (*)())1 无视信号
signal函数用于对信号signo设置信号函数func,返回之前的信号处理函数。signal函数在ISO C中定义,不涉及到多进程,定义够简明+含糊,所以基本上没什么用,一般用sigaction(后面介绍)
不可靠信号处理
早期UNIX系统中信号机制是不可靠的,主要存在以下几个问题:
- 每次信号处理时,信号的处理方式会被重置为默认动作。常见的处理方式是在信号处理函数中重新设置,但如果信号发生在重新设置生效之前,信号会被丢失掉。
- 不能某些时候选择阻塞信号,只能选择SIG_IGN。
自动重启被中断的系统调用
一些“低速”系统调用在信号发生时,可能会被中断,比如被阻塞的对管道或网络设备的读写操作、pause函数和某些ioctl函数。为了减少以下检查代码,操作系统对ioctl、read、write和wait等系统调用都会进行自动重启。
again: if ((n = read(fd, buf, BUFFSIZE)) < 0) { if (errno == EINTR) goto again; /* just an interrupted system call */ }
下表总结了使用signal和sigaction函数时,各系统是否重置默认动作、能否阻塞信号和自动重启动被中断的系统调用等行为。
可重入函数
因为信号处理函数是异步的,信号处理时中断程序正在运行的方法,所以要求在信号处理函数中被调用的方法具有可重入性(reentrant)或异步信号安全性(async-signalsafe),很多函数都不具备可重入性,比如:
- 使用了static数据结构(静态数据结构可能在主程序中被覆写)。
- 调用了malloc或者free(malloc中维护了已分配区域链表,被中断时可能正处于交换链表的过程中)。
- 标准I/O库(使用了全局数据结构)
可靠信号
几个术语:
- generated:事件发生,生成信号。
- delivered:信号已经传递到进程,开始被处理
- pending:未决,信号介于genaerated和delivered之间
- blocking:进程可以选择阻塞信号,被阻塞的信号处于pending状态,直到进程unbloc信号或者设置SIG_IGN(是的,只要在delivered之前都还重新设置信号处理函数)。
- queued:被阻塞后信号多次生成,一些系统支持信号队列,后续多次deliver信号。(使用sigqueue函数依次发送信号给指定进程)
alarm&pause&sleep函数
unsigned int alarm(unsigned int seconds);int pause(void);unsigned int sleep(unsigned int seconds)
alarm用于设置闹钟,超时后,内核产生SIGALARM信号,默认行为是终止进程。每个处理器只有一个闹钟时钟,调用alarm时,如果上次闹钟还没有超时,返回它剩余时间。pause函数挂起当前进程,直到信号生成并被处理后才返回。早期UNIX版本有用alarm和pause实现sleep,但alarm和pause调用存在竞争条件,如果alarm超时之后pause才被调用,进程可能被永久挂起,更好的方案是使用sigprocmask和sigsuspend。
信号集
因为各个系统信号数量都不同,比如Linux的信号数量已经超过40中,超出整型位数,所以POSIX定义了sigset_t用来代表信号集合,并提供了一系列函数用来操纵信号集。
#include <signal.h> int sigemptyset(sigset_t *set); int sigfillset(siget_t *set); int sigaddset(sigset_t *set, int signo); int sigdelset(setset_t *set, int signo); int sigismember(const sigset_t *set, int signo); int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset); int sigpending(sigset_t *set); int sigsuspend(const sigset_t *sigmask);
其中:
- sigprocmask被进程用于屏蔽(阻塞)/解除屏蔽信号集。
- sigpending返回当前被阻塞的信号集。
- sigsupend等于sigprocmask和sigpending的原子操作,用于挂起进程并等待特定信号。sigsuspend和kill(SIGUSR1和SIGUSR2信号)函数组合是有那个可以用来实现TELL_WAIT,TELL_PARENT,TELL_CHILD,WAIT_PARENT和WAIT_CHILD父子进程同步
sigaction函数
int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);struct sigactgion { void (*sa_handler)(int); sigset_t sa_mask; int sa_flags; void (*sa_sigaction)(int siginfo_t *, void *);}
sigaction函数非常强大,能够定义信号处理函数(sa_handler),同时设置屏蔽信号集(sa_mask),是否重启被中断的系统调用(sa_flags),更多信号和进程的信息(siginfo_t)。系统一般使用sigaction实现signal函数。
include "apue.h"/* Reliable version of signal(), using POSIX sigaction(). */Sigfunc *signal(int signo, Sigfunc *func){ struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (signo == SIGALRM) {#ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT;#endif } else { act.sa_flags |= SA_RESTART; } if (sigaction(signo, &act, &oact) < 0) return(SIG_ERR); return(oact.sa_handler);}
- 读书笔记-APUE第三版-(10)信号
- APUE读书笔记-第10章 信号
- APUE读书笔记---第10章 信号
- 读书笔记-APUE第三版-(7)进程环境
- 读书笔记-APUE第三版-(8)进程控制
- 读书笔记-APUE第三版-(9)进程关系
- 读书笔记-APUE第三版-(11)线程
- APUE读书笔记-第十章 信号
- APUE读书笔记-第十章-信号
- 读书笔记-APUE第三版-(1)UNIX系统概述
- 读书笔记-APUE第三版-(2)UNIX标准和实现
- 读书笔记-APUE第三版-(3)Unbuffered I/O
- 读书笔记-APUE第三版-(4)文件和目录
- 读书笔记-APUE第三版-(5)标准IO库
- 读书笔记-APUE第三版-(6)系统数据文件和信息
- APUE,TLPI读书笔记——信号
- APUE读书笔记-第十章 信号 (二)
- 《apue》读书笔记 第三章 文件I/O
- 【练习册】 2015-08-10 ClassicTrie by python
- 【effective c++读书笔记】【第6章】继承与面向对象设计(2)
- spring - ws Endpoint
- 九度oj 1013
- 模拟网站登录 cookie
- 读书笔记-APUE第三版-(10)信号
- 在 Visual C++ 中以错误的顺序链接 CRT 库和 MFC 库时出现 LNK2005 错误
- hdu 2120 Ice_cream's world I (简单并查集 + 判环)
- java邮件发送和短信发送(一)
- Linux下usb转串口工具minicom安装
- hdoj 1875 畅通工程再续 (最小生成树之prim算法)
- Kaldi学习笔记:跑tidigits样例
- SAS SATA SSD IDE硬盘介绍区别
- java web-上传文件到tomcat服务器上路径问题