1.引言

  • 信号是软件中断
  • 信号提供了一种处理异步事件的方法。

2. 信号概念

  • 信号的名字都是以3个字符SIG开头。
  • Linux3.2.0支持31种信号。FreeBSD、Linux和Solaris作为实时扩展都支持另外的应用程序定义的信号。
  • 在头文件signal.h(其中include的bits/signum.h)中,信号名都被定义为正整数常量,不存在编号为0的信号。kill函数对信号编号0有特殊的应用。

  • 很多条件可以产生信号:

    1. 用户按下某些终端键时:Ctrl+C、Ctrl+\、Ctrl+Z
    2. 硬件异常产生信号:除数为0、无效的内存引用
    3. 进程调用kill函数可将任意信号发送给另一个进程或进程组
    4. 当检测到某些软件条件已经发生,并应将其通知有关进程时产生信号。如:SIGURG(网络连接上传来带外数据)、SIGPIPE(在管道的读进程已经终止后,一个进程写此管道)、SIGALRM(进程所设置的定时器超时)
  • 信号是异步事件的经典实例。产生信号的事件对进程而言是随机出现的。进程不能简单地测试一个变量(如errno)来判断是否发生了一个信号,而是必须告诉内核“在此信号发生时,请执行下列操作”。

  • 当某个信号出现时,可以告诉内核按下列3种方式之一进行处理,称之为信号的处理

    1. 忽略此信号。SIG_IGN。只有两种信号不能被忽略:SIGKILL和SIGSTOP。原因是:它们向内核和超级用户提供了使进程终止或停止的可靠方法。另外,如果忽略某些由硬件异常产生的信号(如非法内存引用或除以0),则进程的运行行为是未定义的。
    2. 捕捉信号。即通知内核在某种信号发生后,调用一个用户函数。
    3. 执行系统默认动作。对大多数信号的默认动作是终止该进程。
  • 终止+core。大多数Unix系统调试程序都使用core文件检查进程终止时的状态。

  • 在下列条件下不产生core文件:

    1. 进程是设置用户ID的,而且当前用户并非程序文件的所有者
    2. 进程是设置组ID的,而且当前用户并非程序文件的组所有者
    3. 用户没有写当前工作目录的权限
    4. 文件已存在,而且用户对该文件没有写权限
  • 4个平台对各种signal的支持及默认处理方式

  • 主要信号简要说明:
    • SIGABRT。调用abort函数时产生此信号。
    • SIGALRM。当用alarm函数设置的定时器超时时,产生此信号。
    • SIGCHLD。在一个进程终止或停止时,该信号被送给其父进程。按系统默认,将忽略此信号。
    • SIGFPE。表示算术运算异常,如除以0、浮点溢出等。
    • SIGHUP。如果终端接口检测到一个连接断开,则将此信号送给与该终端相关的控制进程(会话首进程)。通常使用此信号通知守护进程再次读取它们的配置文件。选用此信号的理由是:守护进程不会有控制终端,通常决不会接收到这种信号。
    • SIGILL。表示进程执行一条非法硬件指令。
    • SIGINT。当用户按下中断键Ctrl+C时,终端驱动程序产生此信号并发送至前台进程组的每一个进程。
    • SIGIO。指示一个异步I/O事件。
    • SIGTERM。由kill命令发送的系统默认终止信号。
    • SIGKILL。不能捕获或忽略。它向管理员提供了一红杀死任一进程的可靠方法。
    • SIGPIPE。如果在管道的读进程已终止时写管道,则产生此信号。
    • SIGQUIT。当用户在终端上按下退出键Ctrl+\时,终端驱动程序产生此信号并发送给前台进程组中的所有进程。此信号除了终止前台进程组(和SIGINT一样),同时产生一个core文件。
    • SIGSEGV。指示进程进行了一次无效的内存引用。
    • SIGTSTP。交互停止信号。当用户在终端上按下挂起键Ctrl+Z时,终端驱动程序产生此信号,并发送至前台进程组的所有进程。
    • SIGSTOP。类似于交互停止信号(SIGTSTP),但它不能被捕获或忽略。
    • SIGCONT。此作业控制信号发送给需要继续运行,但当前处于停止状态的进程。
    • SIGTTIN。当一个后台进程组进程试图读其控制终端时,终端驱动程序产生此信号。下列情况例外:1. 读进程忽略或阻塞此信号;2. 读进程所属的进程组是孤儿进程组,此时读操作返回出错,errno设置为EIO
    • SIGTTOU。当一个后台进程组进程试图写其控制终端时,终端驱动程序产生此信号。
    • SIGURG。通知进程已经发生一个紧急情况。如带外数据到达。
    • SIGUSR1、SIGUSR2。用户定义的信号,可用于应用程序。

3. 函数signal

  1. #include <signal.h>
  2. void (*signal(int signo, void (*func)(int)))(int);
  3. Returns: previous disposition of signal (see following) if OK, SIG_ERR on error
  • signal函数由ISO C定义。不涉及多进程、进程组以及终端I/O等,所以它对信号的定义非常含糊,以致于对Unix系统而言几乎毫无用处。
  • 因为signal的语义与实现有关,所以最好使用sigaction函数代替signal函数。
  • 本书中的所有实例均使用图10-18中给出的signal函数,该函数使用sigaction函数是一个平台无关、语义一致的实现。
  • signo参数是上面的信号名。func参数可以是常量SIG_IGN、SIG_DFL或接收到该信号后要调用的函数的地址,即信号处理程序的地址。signal函数的返回值是指向在此之前的信号处理程序的指针。
  1. typedef void Sigfunc(int);
  2. Sigfunc* signal(int, Sigfunc*);
  3. #define SIG_ERR (void (*)())-
  4. #define SIG_DFL (void (*)())
  5. #define SIG_IGN (void (*)())
  • exec,程序启动

    当exec执行一个程序时,所有信号都被设置为它们的默认动作,除非调用exec的进程忽略该信号(则继续保持忽略)。也就是说,exec函数将原先设置为要捕获的信号都更改为默认动作,其他保持不变。因为当exec一个新程序时,信号处理程序的地址很可能在新程序中已无意义。

  • fork,进程创建

    当一个进程调用fork时,其子进程继承父进程的信号处理方式。因为信号处理程序的地址在子进程中是有意义的。

4. 不可靠的信号

  • 早期的Unix版本中,信号是不可靠的。不可靠指的是,信号可能会丢失:一个信号发生了,当进程却可能一直不知道。同时,进程对信号的控制能力很差,它能捕获或忽略它,但不能阻塞。
  • 早期版本的另一个问题是:在进程每次接到信号对其进行处理时,随即将该信号动作重置为默认值。故需要再次建立对该信号的捕获,但在此期间有一个时间窗口。
  • 早期版本的另一个问题是:在进程不希望某种信号发生时,它不能关闭该信号,只能忽略它。

5. 中断的系统调用

  • 早期Unix系统的一个特性是:如果进程在执行一个低速系统调用而阻塞期间捕捉到一个信号,则该系统调用就被中断不再继续运行。该系统调用返回出错,其errno设置为EINTR。
  • 为了支持这种特性,将系统调用分为两类:低速系统调用和其他系统调用。
  • 低速系统调用是可能会使进程永远阻塞的一类系统调用。
  • 与被中断的系统调用相关的问题是必须显式地处理出错返回。典型的代码序列如下:
  1. again:
  2. if ((n = read(fd, buf, BUFFSIZE)) < ) {
  3. if (errno == EINTR)
  4. goto again; /* just an interrupted system call */
  5. /* handle other errors */
  6. }
  • 4.2 BSD引进了某些被中断系统调用的自动重启动,包括ioctl、read、readv、write、writev、wait、waitpid。但是这种自动重启动的处理方式也会带来问题,某些应用程序并不希望这些函数被中断后重启动。为此,4.3 BSD运行进程基于每个信号禁用此功能。
  • POSIX.1要求只有中断信号的SA_RESTART标志有效时,实现才重启动系统调用。
  • 历史上,使用signal函数建立信号处理程序时,对于如何处理被中断的系统调用,各种实现的做法各不相同。

6. 可重入函数

  • 进程捕捉到信号并对其进行处理时,进程正在执行的正常指令序列就被信号处理程序临时中断,它首先执行该信号处理程序中的指令。但是,在信号处理程序中,不能判断捕捉到信号时进程执行到何处:
    1. 如果进程正在执行malloc,而在信号处理程序中又再次调用malloc,这时会?
    2. 如果进程正在执行getpwnam,这是将其结果存放在静态存储单元中的函数,而在信号处理程序中又再次调用getpwnam,这时会?
  • SUS说明了在信号处理程序中保证调用安全的函数。这些函数是可重入的,并被称为异步信号安全的。
  • 没有列入上图的大多数函数是不可重入的,因为:
    1. 它们使用静态数据结构
    2. 它们调用malloc或free
    3. 它们是标准的I/O函数。标准I/O库的很多实现都以不可重入方式使用全局数据结构。
  • 应当了解,即使信号处理程序调用的是上图中的函数,但是由于每个线程只有一个errno变量,所以信号处理程序可能会修改其原先值。故作为一个通用的规则,先保存,后恢复。

7. SIGCLD语义

8. 可靠信号术语和语义

  • 首先,当造成信号的事件发生时,向进程发送一个信号。
  • 当对信号采取了某种动作时,我们说向进程递送了一个信号。在信号产生和递送之间的时间间隔内,称信号是未决的。
  • 进程可以选用“阻塞信号递送”。内核在递送一个原来被阻塞的信号给进程时(而不是在产生该信号时),才决定对它的处理方式。因此,进程在信号递送给它之前仍可改变对该信号的动作。
  • 每个进程都有一个信号屏蔽字,它规定了当前要阻塞递送到该进程的信号集。进程可以调用sigprocmask函数来检测和更改其当前信号屏蔽字。
  • 进程调用sigpending函数来判定哪些信号是设置为阻塞并处于未决状态的。
  • 如果在进程解除对某个信号的阻塞之前,该信号发生了多次,那么?如果递送该信号多次,则称这些信号进行了排队。除非支持POSIX.1实时扩展,否则大多数Unix并不对信号排队,而只递送一次。

9. 函数kill和raise

  • kill函数将信号发送给进程或进程组
  • raise函数则允许进程向自身发送信号。
  1. #include <signal.h>
  2. int kill(pid_t pid, int signo);
  3. int raise(int signo);
  4. Both return: if OK, − on error
  • raise(signo); 等价于 kill(getpid(), signo);
  • kill的pid参数有以下4种情况:
    • pid > 0,发送给进程ID为pid的进程
    • pid == 0,发送给与发送进程属于同一进程组的所有进程
    • pid < 0,发送给其进程组ID等于pid绝对值,而且发送进程具有权限向其发送信号的所有进程
    • pid == -1,发送给发送进程具有权限向它们发送信号的所有进程
  • 关于发送信号的权限

    1. 超级用户可将信号发送给任一进程。
    2. 非超级用户,其基本规则是发送者的实际用户ID或有效用户ID必须等于接收者的实际用户ID或有效用户ID。

      特例:如果被发送的信号是SIGCONT,则进程可以将它发送给属于同一会话的任一其他进程。

  • POSIX.1 将信号编号为0定义为空信号。如果signo参数为0,则kill仍执行正常的错误检查,当不发送信号。这常被用来确定一个特定进程是否仍然存在。但是,在返回测试结果时,原来存在的被测试进程可能已经终止,所以这种测试并无多大意义。

10. 函数alarm、pause

  • 当定时器超时时,产生SIGALRM信号。信号由内核产生。
  1. #include <unistd.h>
  2. unsigned int alarm(unsigned int seconds);
  3. Returns: or number of seconds until previously set alarm
  • 每个进程只能有一个闹钟时间。多次调用alarm以新值代替旧值,并返回旧值的余留值。参数为0,则取消以前的闹钟。

  • pause函数使调用进程挂起直到捕捉到一个信号

  1. #include <unistd.h>
  2. int pause(void);
  3. Returns: − with errno set to EINTR
  • 只有执行了一个信号处理程序并从其中返回时,pause才返回。返回-1,errno设置为EINTR。

11. 信号集

  • POSIX.1定义数据类型sigset_t包含一个信号集,并定义以下5个处理信号集的函数
  1. #include <signal.h>
  2. int sigemptyset(sigset_t *set);
  3. int sigfillset(sigset_t *set);
  4. int sigaddset(sigset_t *set, int signo);
  5. int sigdelset(sigset_t *set, int signo);
  6. All four return: if OK, − on error
  7. int sigismember(const sigset_t *set, int signo);
  8. Returns: if true, if false, − on error

12. 函数sigprocmask

  • 调用函数sigprocmask可以检测或更改,或同时检测和更改进程的信号屏蔽字
  1. #include <signal.h>
  2. int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
  3. Returns: if OK, − on error
  • how参数:SIG_BLOCK、SIG_UNBLOCK、SIG_SETMASK
  • 在调用sigprocmask后如果有任何未决的、不再阻塞的信号,则在其返回之前,至少将其中之一递送给该进程。

13. 函数sigpending

  • sigpending函数返回一信号集,它对于调用进程而言,其中的各信号是阻塞不能递送的
  1. #include <signal.h>
  2. int sigpending(sigset_t *set);
  3. Returns: if OK, − on error

14. 函数sigaction

  • sigaction函数的功能是检查或修改与指定信号相关联的处理动作
  1. #include <signal.h>
  2. int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);
  3. Returns: if OK, − on error
  1. struct sigaction {
  2. void (*sa_handler)(int); /* addr of signal handler, or SIG_IGN, or SIG_DFL */
  3. sigset_t sa_mask; /* additional signals to block */
  4. int sa_flags; /* signal options, Figure 10.16 */
  5. void (*sa_sigaction)(int, siginfo_t *, void *); /* alternate handler */
  6. };

原创文章,转载请声明出处:http://www.cnblogs.com/DayByDay/p/3948397.html

《Unix环境高级编程》读书笔记 第10章-信号的更多相关文章

  1. [置顶] 文件和目录(二)--unix环境高级编程读书笔记

    在linux中,文件的相关信息都记录在stat这个结构体中,文件长度是记录在stat的st_size成员中.对于普通文件,其长度可以为0,目录的长度一般为1024的倍数,这与linux文件系统中blo ...

  2. unix环境高级编程-读书笔记与习题解答-第一篇

    从这周开始逐渐的进入学习状态,每天晚上都会坚持写c程序,并且伴随对这本书的深入,希望能写出更高质量的读书笔记和程序. 本书的第一章,介绍了一些关于unix的基础知识,在这里我不想去讨论linux到底是 ...

  3. unix 环境高级编程 读书笔记与习题解答第四篇

    第一章 第六节 第一小节 这一章没有程序设计和API方面的深入学习,而是注重介绍了unix操作系统中的原始数据类型和系统原型函数,错误处理方面的知识. ____unistd.h____ 该文件包含了u ...

  4. unix 环境高级编程-读书笔记与习题解答-第二篇

    第四节 输入与输出 上次的笔记中写到的 open, read, write, lseek 以及close ,都是不带缓存的IO函数,这些函数都使用文件描述符进行工作. 上一篇笔记用到的 read(ST ...

  5. [置顶] 文件io(一)--unix环境高级编程读书笔记

    unix-like(后面以linux为例)系统中的文件操作只需要五个函数就足够了,open.close.read.write以及lseek.这些操作被称为不带缓存的io,这里有必要说一下带缓存和不带缓 ...

  6. unix进程的环境--unix环境高级编程读书笔记

    http://blog.csdn.net/xiaocainiaoshangxiao/article/category/1800937

  7. unix环境高级编程 读书笔记

    1.上班业余时间把书下载下来,第一章读完了,但是程序只能回家运行啦!Fighting!

  8. Unix环境高级编程学习笔记——fcntl

    写这篇文正主要是为了介绍下fcntl,并将我自己在学习过程中的一些理解写下来,不一定那么官方,也有错误,希望指正,共同进步- fcntl: 一个修改一打开文件的性质的函数.基本的格式是 int fcn ...

  9. 20191105 《Spring5高级编程》笔记-第10章

    第10章 使用类型转换和格式化进行验证 在应用程序开发中,数据验证通常与转换和格式化一起被提及.因为数据源的格式很可能与应用程序中所使用的格式不同. 名词缩写: SPI(Service Provide ...

随机推荐

  1. Excel基础视频教程在线观看

    也许你已经在Excel中完成过上百张财务报表,也许你已利用Excel函数实现过上千次的复杂运算,也许你认为Excel也不过如此,甚至了无新意.但我们平日里无数次重复的得心应手的使用方法只不过是Exce ...

  2. WebApi笔记

    WebApi有一段时间没用了,这几天用webapi做了一个接口供第三方调用,又重新折腾了下,做个笔记记录下心得,防止遗忘. 1.webapi使用的RESTful风格的传参方式,其实就是充分利用HTTP ...

  3. Python数据分析------例子1(信用卡欺诈)

    1.读取数据 data=read_csv(path) data.head() #画图(查看class即分类的数据条形图),函数sort_index()是将dataframe按照行索引来排序输出值 co ...

  4. 一些css布局

    # css布局 #---bootstrap   详情请看官方文档---首先要按照相应的官方规范引入相应的css js fonts等 container相当于一个容器 一般设置一个 接下来设置行 用ro ...

  5. [luogu4195 Spoj3105] Mod (大步小步)

    传送门 题目描述 已知数a,p,b,求满足a^x≡b(mod p)的最小自然数x. 输入输出格式 输入格式: 每个测试文件中最多包含100组测试数据. 每组数据中,每行包含3个正整数a,p,b. 当a ...

  6. python生成器,递归调用

    生成器 什么是生成器:只要在函数体内出现yield关键字,那么再执行函数就不会执行函数代码,会得到一个结果,该结果就是生成器 生成器就是迭代器 yield的功能 yield为我们提供了一种自定义迭代器 ...

  7. u-boot for tiny210 ver1.0(by liukun321咕唧咕唧)

     新版本下载: 下面的链接提供了较新版本的源码 ver4.0源码下载:u-boot for tiny210 ver4.0 ver3.1源码下载: u-boot for tiny210 ver3.1 v ...

  8. [剑指offer] 5. 用两个栈实现队列+[剑指offer]30. 包含min函数的栈(等同于leetcode155) +[剑指offer]31.栈的压入、弹出序列 (队列 栈)

    c++里面stack,queue的pop都是没有返回值的, vector的pop_back()也没有返回值. 思路: 队列是先进先出 , 在stack2里逆序放置stack1的元素,然后stack2. ...

  9. shell 的变量

    一.自定义变量 1.字母或者下划线开头,由字母.数字.下划线组成,大小写敏感,在使用变量时,要在变量前加上前缀 $,一般变量由大写字母表示,并且英文开头,"=" 两边应没有空格.如 ...

  10. 2015 Multi-University Training Contest 1 hdu 5296 Annoying problem

    Annoying problem Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others ...