UNIX 高级环境编程 第10章 信号

浏览: 113 发布日期: 2016-11-18 分类: unix

10.1 引言

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

10.2 信号概念

信号处理方式

  1. 忽略信号

  2. 捕捉信号

  3. 执行默认系统动作,对于大多数系统默认动作是终止该进程
    注意:SIGKILL和SIGSTOP 信号不能被忽略,也不能被捕捉。

10.3 函数signal

#include <signal>
void (signal(int sig, void(*handler)(int)))(int);

函数说明:

  1. sig: 信号类型

  2. handler: 处理sig信号的函数的指针

  3. signal的返回值: 原先处理sig信号的函数的指针

信号的状态
1.进程启动
  当执行一个程序时,所有信号的状态都是系统默认的。 除非调用exec的进程忽略该信号。确切地讲,exec函数将原先设置为要捕捉的信号都更改为默认动作,其他信号的状态则不变。(课本举了一个例子: 在bash下,执行"./a.out &", 产生的后台进程自动忽略SIGINT与SIGSTOP)
2.进程创建
  当一个进程调用fork时,其子进程继承父进程的信号处理方式

10.5 中断的系统调用

对于Linux操作系统而言,如果进程在执行一个低速系统调用而阻塞期间捕捉到一个信号,则该系统调用:

  1. 在信号处理程序完成后,自动重启.
    (SA_RESTART置位后,如 write, read,open,wait, waitpid, fcntl等)

  2. 系统调用失败,返回EINTR(无论SA_RESTART是否置位,如pause, sigsuspend,nanosleep等)
    注意:sleep 永远不会被重启,返回一个成功值:剩余的休眠时间

10.6 可重入函数

问题:
如果进程正在执行malloc,在其堆中分配另外的存储空间,而此时由于捕捉到信号而插入执行该信号处理程序,其中又调用malloc,那么可能就会出现严重错误。所以,对于malloc而言,它就不是可重入代码

不可重入函数的特征:

  1. 使用了静态数据结构,或全局数据结构

  2. 调用malloc或free函数

  3. 它们是标准I/O(很多标准I/O实现以不可重入方式使用了全局数据结构)

举例:在信号处理程序中调用prinf函数,当main函数也使用了printf时,则不保证出现期望的结果
注意:由于一个线程只有一个errno值,而信号处理程序可能改变errno值。 因此,在信号处理程序中首先应该保存原先的errno。待处理完信号后,恢复原有的errno。比如:SIGCHLD的信号处理程序 往往会调用waitpid函数,显然waitpid可能会产生ECHILD,从而覆盖了原有的errno。

void handler(int sig){
    int errno_saved = errno;
    // proccess sig
    errno = errno_saved; 
}

理解longjmp与siglongjmp为什么不是可重入的。

10.7 SIGCLD 语义

在 linux 3.10.0 中语义与SIGCHLD 相同

10.8 可靠信号术语和语义

  1. 信号的产生(generation): 造成信号的发生(硬件异常,软件条件)

  2. 信号的传递(delivery):对信号调用信号处理程序或采用系统默认处理方式

  3. 信号的未决(pending):信号已经产生,当还没有传递的中间状态

多数unix操作系统并不对信号进行排队,而是只传递一次信号

10.9 函数kill和raise

int kill(pid_t pid,int signo)

int raise(int signo); 等价于 kill(getpid(), signo)

理解不同pid的值代表的含义。对于非超级用户,发送者的实际用户ID或有效用户ID必须等于接受者的实际用户ID或有效用户ID

10.10 函数alarm和pause

unsigned int alarm(unsigned int sec);

理解第二次设置alarm时,alarm函数的返回值。

int pause(void);

只有执行了一个信号处理程序并从其返回时,pause才返回,pause返回-1,errno设置为EINTR

返回顶部