信号(signal)

一 信号的基本概念

信号机制是进程间相互传递消息的一种方法,信号全称软中断信号,也有人称作软中断,从它的命名可以看出,它的使用很像中断,所以,信号是进程控制的一部分。

(1)进程之间可以通过系统调用kill发送软中断信号

(2)内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。

注:信号指示通知给进程发生了什么事,并不给进程传递数据。

为了理解信号,我们从熟悉的场景说起

  1. 用户输入指令,在shell下启动一个前台进程。
  2. 用户按下Ctrl+C,此时硬盘驱动产生一个中断给Linux内核。
  3. 如果cpu当前正在执行这个进程的代码,则该进程的用户空间暂停执行,CPU从用户态切换到内核态处理硬件中断。
  4. 终端驱动程序将Ctrl+C解释成一个SIGINT信号,记在该进程的PCB中(也可以说发送一个SIGINT信号给该进程)
  5. 当某个信号从内核返回到该用户空间代码继续执行之前,首先处理PCB中(也可以说发送一个SIGINT信号给该进程)

    kill  -l 命令可查看系统定义的信号列表

信号产生的条件主要有:

1.用户在终端下按下某些键时,终端驱动程序会发送信号给前台进程,例如Ctrl+C产生SIGINT信号,Ctrl+\产生SIGQUIT信号,Ctrl+Z产生SIGSTOP信号(可使前台进程终止)

2.硬件异常产生信号,这些条件由硬件检测并通知内核,然后内核向当前进程发送适当的信号,例如当前进程执行了除以0的指令,CPU运算单元会产生异常,内核将这个异常解释为SIGFPE信号发送给该进程,再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给该进程。

3.一个进程调用kill(2)函数可以发送信号给另一个进程,可以用kill(1)給某个进程,kill(1)也是调用kill(2)函数实现的,如果不明确指定信号则发送SIGTERM信号,该信号默认处理动作是终止进程。当内核检测到某种软件条件发生时也可以通过信号通知进程,例如闹钟超时产生SIGALRM信号,向读端已关闭的管道写数据时产生SIGPIPE信号。如果不想按默认动作处理信号,用户进程可以调用sigaction(2)函数告诉进程应该如何处理某种信号。

可选的信号处理有一下三个动作:

  1. 忽略此信号
  2. 执行信号的默认处理动作
  3. 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉一个信号。

产生信号的方式:

  1. 通过终端键产生信号(Ctrl+C等)
  2. 调用系统函数向进程发信号
  3. 由软件条件产生信号(如闹钟alarm函数)
  4. unsigned int alarm(unsigned int seconds);

    调用alarm函数可以设定一个闹钟,也就是告诉内核在second秒之后给当前进程发送SIGALARM信号,该信号默认处理动作是终止当前进程,这个函数返回值是0或者是以前闹钟时间还余下的秒数。

二 阻塞信号

信号在内核中的表示

信号产生有各种原因,而实际信号的处理动作称为信号的递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。进程可以选择阻塞(Block)某个信号,被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞才执行递达动作,注意:阻塞和忽略是不同的,只要信号被阻塞就不会被递达,而忽略是递达之后可选的一种处理动作。

每个信号都有两个标志位分别表示阻塞和未决,还有一个函数指针表示处理动作,信号产生时,内核在进程控制块中设置信号未决标志,直到信号递达才处理这个标志。

  1. SIGHUP信号未阻塞也未产生过,当它递达是才默认处理动作。
  2. SIGINT信号产生过,但正在被阻塞,所以暂时不能递达,虽然它的处理动作是忽略的,但没有接触阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
  3. SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。

如果进程在解除对某信号的阻塞之前这种信号产生过多次,将如何处理?

允许系统递送信号一次或多次,Linux是这样实现的:常规信号在递送之前产生只计一次,而实时信号在递送之前产生多次可依次放在队列里。
从上图看来,每个信号只有一个bit的未决标志,非0即1,不记录信号产生了多少次,阻塞标志也是这样表示的。因此未决和阻塞标志可用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号“有效”或“无效”状态。信号阻塞集也叫作当前进程的信号屏蔽字(Signal Mask).

#include<stdio.h>
#include<signal.h>
void catch()
{}

int my_sleep(int timeout)
{
    signal(SIGALRM,catch);
    alarm(timeout);
    pause();
    int ret = alarm(0);
    signal(SIGALRM,SIG_DFL);
    return ret;

}
int main()
{
    while(1)
    {
        printf("testing...\n");
        my_sleep(1);
    }
    return 0;
}

设置一个闹钟,使得每一秒输出一次。

现在重新审视“mysleep”程序,设想这样的时序:
1. 注册SIGALRM信号的处理函数。
2. 调用alarm(nsecs)设定闹钟。
3. 内核调度优先级更高的进程取代当前进程执行,并且优先级更高的进程有很多个,每个
都要 执行很长时间
4. nsecs秒钟之后闹钟超时了,内核发送SIGALRM信号给这个进程,处于未决状态。
5. 优先级更高的进程执行完了,内核要调度回这个进程执行。SIGALRM信号递达,执行处
理函 数sig_alrm之后再次进入内核。
6. 返回这个进程的主控制流程,alarm(nsecs)返回,调用pause()挂起等待。

7. 可是SIGALRM信号已经处理完了,还等待什么呢?
出现这个问题的根本原因是系统运行的时序(Timing)并不像我们写程序时所设想的那样。
虽然alarm(nsecs)紧接着的下一行就是pause(),但是无法保证pause()一定会在调用
alarm(nsecs)之 后的nsecs秒之内被调用。由于异步事件在任何时候都有可能发生(这里
的异步事件指出现更高优 先级的进程),如果我们写程序时考虑不周密,就可能由于时序问题
而导致错误,这叫做竞态条件 (Race Condition)。
如何解决上述问题呢?读者可能会想到,在调用pause之前屏蔽SIGALRM信号使它不能提前递
达就可 以了。看看以下方法可行吗?

  1. 屏蔽SIGALRM信号;
    2. alarm(nsecs);
    3. 解除对SIGALRM信号的屏蔽;
    4. pause();
  2. 从解除信号屏蔽到调用pause之间存在间隙,SIGALRM仍有可能在这个间隙递达。要消除这
    个间隙, 我们把解除屏蔽移到pause后面可以吗?
    1. 屏蔽SIGALRM信号;
    2. alarm(nsecs);
    3. pause();
    4. 解除对SIGALRM信号的屏蔽;
    这样更不行了,还没有解除屏蔽就调用pause,pause根本不可能等到SIGALRM信号。要是
    “解除信号屏蔽”和“挂起等待信号”这两步能合并成一个原子操作就好了,这正是sigsuspend
    函数的功 能。sigsuspend包含了pause的挂起等待功能,同时解决了竞态条件的问题,在对
    时序要求严格的场合下都应该调用sigsuspend而不是pause。

三 信号捕捉

测试31种信号哪种可以捕捉?哪种不能捕捉?

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下:

1.

时间: 2024-11-17 07:55:29

信号(signal)的相关文章

Linux 信号signal处理机制(ZZ)

http://www.cnblogs.com/taobataoma/archive/2007/08/30/875743.html 信号是Linux编程中非常重要的部分,本文将详细介绍信号机制的基本概念.Linux对信号机制的大致实现方法.如何使用信号,以及有关信号的几个系统调用. 信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称作软中断.从它的命名可以看出,它的实质和使用很象中断.所以,信号可以说是进程控制的一部分. 一.信号的基本概念 本节先介绍信号的一些基本概念,然后

Linux 信号signal处理机制

信号是Linux编程中非常重要的部分,本文将详细介绍信号机制的基本概念.Linux对信号机制的大致实现方法.如何使用信号,以及有关信号的几个系统调用. 信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称作软中断.从它的命名可以看出,它的实质和使用很象中断.所以,信号可以说是进程控制的一部分. 一.信号的基本概念 本节先介绍信号的一些基本概念,然后给出一些基本的信号类型和信号对应的事件.基本概念对于理解和使用信号,对于理解信号机制都特别重要.下面就来看看什么是信号. 1.基本

Python标准库07 信号 (signal包,部分os包)

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 在了解了Linux的信号基础之后,Python标准库中的signal包就很容易学习和理解.signal包负责在Python程序内部处理信号,典型的操作包括预设信号处理函数,暂停并等待信号,以及定时发出SIGALRM等.要注意,signal包主要是针对UNIX平台(比如Linux, MAC OS),而Windows内核中由于对信号机制的支持不充分,所以在Windows上的Pytho

Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)

整理自网络 Unix IPC包括:管道(pipe).命名管道(FIFO)与信号(Signal) 管道(pipe) 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信: 实现机制: 管道是由内核管理的一个缓冲区,相当于我们放入内存中的一个纸条.管道的一端连接一个进程的输出.这个进程会向管道中放入信息.管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息.一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管

Linux信号signal用法详解及注意事项

信号是软件中断,是一种异步通信方式,处理异步的事件.例如我们在终端中运行程序,通过按下键盘"Ctrl+c",可以发出一个SIGINT中断信号去停止程序运行.信号的处理有3种方法:1.  忽略该信号,大多数信号都可以如此处理.但是SIGKILL和SIGSTOP除外,决不能被忽略.2.  捕获信号,用户自定义一个信号处理函数,当信号发生时,就会触发调用该自定义信号函数.信号SIGKILL和SIGSTOP不能被捕获.3.  系统默认动作, 大多数信号的默认动作是终止该进程.有些信号会&quo

信号 signal sigaction补充

目前linux中的signal()是通过sigation()函数实现的. 由signal()安装的实时信号支持排队,同样不会丢失. 先看signal 和 sigaction 的区别: 关键是 struct sigaction act; 里面有三个部分,除了 signal函数会关注的 sa_handler 之外, 还有 sa_mask,这里面可以提供阻塞功能(类似于sigprocmask) sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask, 

UNIX环境高级编程之-----信号signal

参考书籍:unxi环境高级编程 信号函数: <span style="font-family:Microsoft YaHei;font-size:18px;">typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); </span> 其原型为: <span style="font-family:Microsoft YaHei;

Linux信号signal

Linux 信号表 Linux支持POSIX标准信号和实时信号.下面给出Linux Signal的简表,详细细节可以查看man 7 signal. 信号 取值 默认动作 含义(发出信号的原因) SIGHUP 1 Term 终端的挂断或进程死亡 SIGINT 2 Term 来自键盘的中断信号 SIGQUIT 3 Core 来自键盘的离开信号 SIGILL 4 Core 非法指令 SIGABRT 6 Core 来自abort的异常信号 SIGFPE 8 Core 浮点例外 SIGKILL 9 Ter

linux 信号signal和sigaction理解

今天看到unp时发现之前对signal到理解实在浅显,今天拿来单独学习讨论下. signal,此函数相对简单一些,给定一个信号,给出信号处理函数则可,当然,函数简单,其功能也相对简单许多,简单给出个函数例子如下: [cpp] view plaincopy 1 #include <signal.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 5 void ouch(int sig) 6 { 7     printf(&quo

apue 第10章 信号signal

每种信号都有名字,都是以SIG开头 信号机制最简单的接口是signal函数 #include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 返回值:成功以前的信号处理配置,出错SIG_ERR kill函数将信号发送给进程或进程组.raise函数则允许进程向自身发送信号 #include <sys/types.h> #includ