linux信号调用机制



在Linux中,信号是进程间通讯的一种方式,它采用的是异步机制。当信号发送到某个进程中时,操作系统会中断该进程的正常流程,并进入相应的信号处理函数执行操作,完成后再回到中断的地方继续执行。

需要说明的是,信号只是用于通知进程发生了某个事件,除了信号本身的信息之外,并不具备传递用户数据的功能。

1 信号的响应动作

每个信号都有自己的响应动作,当接收到信号时,进程会根据信号的响应动作执行相应的操作,信号的响应动作有以下几种:

  • 中止进程(Term)
  • 忽略信号(Ign)
  • 中止进程并保存内存信息(Core)
  • 停止进程(Stop)
  • 继续运行进程(Cont)

用户可以通过signalsigaction函数修改信号的响应动作(也就是常说的“注册信号”,在文章的后面会举例说明)。另外,在多线程中,各线程的信号响应动作都是相同的,不能对某个线程设置独立的响应动作。

2 信号类型

Linux支持的信号类型可以参考下面给出的列表。

2.1 在POSIX.1-1990标准中的信号列表

信号 动作 说明
SIGHUP 1 Term 终端控制进程结束(终端连接断开)
SIGINT 2 Term 用户发送INTR字符(Ctrl+C)触发
SIGQUIT 3 Core 用户发送QUIT字符(Ctrl+/)触发
SIGILL 4 Core 非法指令(程序错误、试图执行数据段、栈溢出等)
SIGABRT 6 Core 调用abort函数触发
SIGFPE 8 Core 算术运行错误(浮点运算错误、除数为零等)
SIGKILL 9 Term 无条件结束程序(不能被捕获、阻塞或忽略)
SIGSEGV 11 Core 无效内存引用(试图访问不属于自己的内存空间、对只读内存空间进行写操作)
SIGPIPE 13 Term 消息管道损坏(FIFO/Socket通信时,管道未打开而进行写操作)
SIGALRM 14 Term 时钟定时信号
SIGTERM 15 Term 结束程序(可以被捕获、阻塞或忽略)
SIGUSR1 30,10,16 Term 用户保留
SIGUSR2 31,12,17 Term 用户保留
SIGCHLD 20,17,18 Ign 子进程结束(由父进程接收)
SIGCONT 19,18,25 Cont 继续执行已经停止的进程(不能被阻塞)
SIGSTOP 17,19,23 Stop 停止进程(不能被捕获、阻塞或忽略)
SIGTSTP 18,20,24 Stop 停止进程(可以被捕获、阻塞或忽略)
SIGTTIN 21,21,26 Stop 后台程序从终端中读取数据时触发
SIGTTOU 22,22,27 Stop 后台程序向终端中写数据时触发

:其中SIGKILLSIGSTOP信号不能被捕获、阻塞或忽略。

2.2 在SUSv2和POSIX.1-2001标准中的信号列表

信号 动作 说明
SIGTRAP 5 Core Trap指令触发(如断点,在调试器中使用)
SIGBUS 0,7,10 Core 非法地址(内存地址对齐错误)
SIGPOLL   Term Pollable event (Sys V). Synonym for SIGIO
SIGPROF 27,27,29 Term 性能时钟信号(包含系统调用时间和进程占用CPU的时间)
SIGSYS 12,31,12 Core 无效的系统调用(SVr4)
SIGURG 16,23,21 Ign 有紧急数据到达Socket(4.2BSD)
SIGVTALRM 26,26,28 Term 虚拟时钟信号(进程占用CPU的时间)(4.2BSD)
SIGXCPU 24,24,30 Core 超过CPU时间资源限制(4.2BSD)
SIGXFSZ 25,25,31 Core 超过文件大小资源限制(4.2BSD)

:在Linux 2.2版本之前,SIGSYSSIGXCPUSIGXFSZ以及SIGBUS的默认响应动作为Term,Linux 2.4版本之后这三个信号的默认响应动作改为Core。

2.3 其它信号

信号 动作 说明
SIGIOT 6 Core IOT捕获信号(同SIGABRT信号)
SIGEMT 7,-,7 Term 实时硬件发生错误
SIGSTKFLT -,16,- Term 协同处理器栈错误(未使用)
SIGIO 23,29,22 Term 文件描述符准备就绪(可以开始进行输入/输出操作)(4.2BSD)
SIGCLD -,-,18 Ign 子进程结束(由父进程接收)(同SIGCHLD信号)
SIGPWR 29,30,19 Term 电源错误(System V)
SIGINFO 29,-,-   电源错误(同SIGPWR信号)
SIGLOST -,-,- Term 文件锁丢失(未使用)
SIGWINCH 28,28,20 Ign 窗口大小改变时触发(4.3BSD, Sun)
SIGUNUSED -,31,- Core 无效的系统调用(同SIGSYS信号)

注意:列表中有的信号有三个值,这是因为部分信号的值和CPU架构有关,这些信号的值在不同架构的CPU中是不同的,三个值的排列顺序为:1,Alpha/Sparc;2,x86/ARM/Others;3,MIPS。

例如SIGSTOP这个信号,它有三种可能的值,分别是17、19、23,其中第一个值(17)是用在Alpha和Sparc架构中,第二个值(19)用在x86、ARM等其它架构中,第三个值(23)则是用在MIPS架构中的。

3 信号机制

文章的前面提到过,信号是异步的,这就涉及信号何时接收、何时处理的问题。

我们知道,函数运行在用户态,当遇到系统调用、中断或是异常的情况时,程序会进入内核态。信号涉及到了这两种状态之间的转换,过程可以先看一下下面的示意图:

接下来围绕示意图,将信号分成接收、检测和处理三个部分,逐一讲解每一步的处理流程。

3.1 信号的接收

接收信号的任务是由内核代理的,当内核接收到信号后,会将其放到对应进程的信号队列中,同时向进程发送一个中断,使其陷入内核态。

注意,此时信号还只是在队列中,对进程来说暂时是不知道有信号到来的。

3.2 信号的检测

进程陷入内核态后,有两种场景会对信号进行检测:

  • 进程从内核态返回到用户态前进行信号检测
  • 进程在内核态中,从睡眠状态被唤醒的时候进行信号检测

当发现有新信号时,便会进入下一步,信号的处理。

3.3 信号的处理

信号处理函数是运行在用户态的,调用处理函数前,内核会将当前内核栈的内容备份拷贝到用户栈上,并且修改指令寄存器(eip)将其指向信号处理函数。

接下来进程返回到用户态中,执行相应的信号处理函数。

信号处理函数执行完成后,还需要返回内核态,检查是否还有其它信号未处理。如果所有信号都处理完成,就会将内核栈恢复(从用户栈的备份拷贝回来),同时恢复指令寄存器(eip)将其指向中断前的运行位置,最后回到用户态继续执行进程。

至此,一个完整的信号处理流程便结束了,如果同时有多个信号到达,上面的处理流程会在第2步和第3步骤间重复进行。

重点理解上图三个部分:信号检测/信号调用/信号处理程序返回。明白怎么从正常进程到信号处理程序再到正常进程的过程。

4 信号的使用

4.1 发送信号

用于发送信号的函数有raisekillkillpgpthread_killtgkillsigqueue,这几个函数的含义和用法都大同小异,这里主要介绍一下常用的raisekill函数。

raise函数:向进程本身发送信号

函数声明如下:

#include <signal.h>

int raise(int sig);

函数功能是向当前程序(自身)发送信号,其中参数sig为信号值。

kill函数:向指定进程发送信号

函数声明如下:

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

函数功能是向特定的进程发送信号,其中参数pid为进程号,sig为信号值。

在这里的参数pid,根据取值范围不同,含义也不同,具体说明如下:

  • pid > 0 :向进程号为pid的进程发送信号
  • pid = 0 :向当前进程所在的进程组发送信号
  • pid = -1 :向所有进程(除PID=1外)发送信号(权限范围内)
  • pid < -1 :向进程组号为-pid的所有进程发送信号

另外,当sig值为零时,实际不发送任何信号,但函数返回值依然有效,可以用于检查进程是否存在。

4.2 等待信号被捕获

等待信号的过程,其实就是将当前进程(线程)暂停,直到有信号发到当前进程(线程)上并被捕获,函数有pausesigsuspend

pause函数:将进程(或线程)转入睡眠状态,直到接收到信号

函数声明如下:

#include <unistd.h>

int pause(void);

该函数调用后,调用者(进程或线程)会进入睡眠(Sleep)状态,直到捕获到(任意)信号为止。该函数的返回值始终为-1,并且调用结束后,错误代码(errno)会被置为EINTR。

sigsuspend函数:将进程(或线程)转入睡眠状态,直到接收到特定信号

函数声明如下:

#include <signal.h>

int sigsuspend(const sigset_t *mask);

该函数调用后,会将进程的信号掩码临时修改(参数mask),然后暂停进程,直到收到符合条件的信号为止,函数返回前会将调用前的信号掩码恢复。该函数的返回值始终为-1,并且调用结束后,错误代码(errno)会被置为EINTR。

4.3 修改信号的响应动作

用户可以自己重新定义某个信号的处理方式,即前面提到的修改信号的默认响应动作,也可以理解为对信号的注册,可以通过signalsigaction函数进行,这里以signal函数举例说明。

首先看一下函数声明:

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

第一个参数signum是信号值,可以从前面的信号列表中查到,第二个参数handler为处理函数,通过回调方式在信号触发时调用。

下面为示例代码:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

/* 信号处理函数 */
void sig_callback(int signum) {
    switch (signum) {
        case SIGINT:
            /* SIGINT: Ctrl+C 按下时触发 */
            printf("Get signal SIGINT. \r\n");
            break;
        /* 多个信号可以放到同一个函数中进行 通过信号值来区分 */
        default:
            /* 其它信号 */
            printf("Unknown signal %d. \r\n", signum);
            break;
    }

    return;
}

/* 主函数 */
int main(int argc, char *argv[]) {
    printf("Register SIGINT(%u) Signal Action. \r\n", SIGINT);

    /* 注册SIGINT信号的处理函数 */
    signal(SIGINT, sig_callback);

    printf("Waitting for Signal ... \r\n");

    /* 等待信号触发 */
    pause();

    printf("Process Continue. \r\n");

    return 0;
}

源文件下载:链接

例子中,将SIGINT信号(Ctrl+C触发)的动作接管(打印提示信息),程序运行后,按下Ctrl+C,命令行输出如下:

./linux_signal_example
Register SIGINT(2) Signal Action.
Waitting for Signal ...
^CGet signal SIGINT.
Process Continue.

进程收到SIGINT信号后,触发响应动作,将提示信息打印出来,然后从暂停的地方继续运行。这里需要注意的是,因为我们修改了SIGINT信号的响应动作(只打印信息,不做进程退出处理),所以我们按下Ctrl+C后,程序并没有直接退出,而是继续运行并将"Process Continue."打印出来,直至程序正常结束。

转载:http://hutaow.com/blog/2013/10/19/linux-signal/

linux信号调用机制

时间: 2024-08-06 11:24:36

linux信号调用机制的相关文章

Linux 信号signal处理机制(ZZ)

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

Linux信号(signal) 机制分析

[摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核对于信号的处理流程包括信号的触发/注册/执行及注销等.最后介绍了应用层的相关处理,主要包括信号处理函数的安装.信号的发送.屏蔽阻塞等,最后给了几个简单的应用实例. [关键字]软中断信号,signal,sigaction,kill,sigqueue,settimer,sigmask,sigprocmask,sigset_t 1       信

Linux 信号signal处理机制

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

利用linux信号机制调试段错误(Segment fault)

在实际开发过程中,大家可能会遇到段错误的问题,虽然是个老问题,但是其带来的隐患是极大的,只要出现一次,程序立即崩溃中止.如果程序运行在PC中,segment fault的调试相对比较方便,因为可以通过串口.显示器可以查看消息,只要程序运行,通过GDB调试工具即可捕捉产生segment fault的具体原因.但是不知大家有没有想法,当程序运行在嵌入式设备上时,你所面临资源的缺乏,你没有串口打印信息,没有显示器可查看,你不知道程序运行的状态,如果程序的产生segment falut这种bug发生的周

Linux信号机制

信号分类 不可靠信号 VS. 可靠信号 Linux信号机制基本上是从UNIX系统中继承过来的.早期UNIX系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,它的主要问题是: 1.进程每次处理信号后,就将对信号的响应设置为默认动作.在某些情况下,将导致对信号的错误处理:因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用signal(),重新安装该信号. 2.早期UNIX下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失. Linux支持不可靠信号,但是

linux信号机制分析

[摘要]本文分析了Linux内核对于信号的实现机制和应用层 的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核对于信号的处理流程包括信号的触发/注册/执行 及注销等.最后介绍了应用层的相关处理,主要包括信号处理函数的安装.信号的发送.屏蔽阻塞等,最后给了几个简单的应用实例. [关键字]软中断信号,signal,sigaction,kill,sigqueue,settimer,sigmask,sigprocmask,sigset_t 1      

[内核同步]浅析Linux内核同步机制

转自:http://blog.csdn.net/fzubbsc/article/details/37736683?utm_source=tuicool&utm_medium=referral 很早之前就接触过同步这个概念了,但是一直都很模糊,没有深入地学习了解过,近期有时间了,就花时间研习了一下<linux内核标准教程>和<深入linux设备驱动程序内核机制>这两本书的相关章节.趁刚看完,就把相关的内容总结一下.为了弄清楚什么事同步机制,必须要弄明白以下三个问题: 什么是互

Linux信号详解

Linux信号详解 一 信号的种类 可靠信号与不可靠信号, 实时信号与非实时信号 可靠信号就是实时信号, 那些从UNIX系统继承过来的信号都是非可靠信号, 表现在信号 不支持排队,信号可能会丢失, 比如发送多次相同的信号, 进程只能收到一次. 信号值小于 SIGRTMIN的都是非可靠信号. 非可靠信号就是非实时信号, 后来, Linux改进了信号机制, 增加了32种新的信号, 这些信 号都是可靠信号, 表现在信号支持排队, 不会丢失, 发多少次, 就可以收到多少次. 信号值 位于 [SIGRTM

Linux内核同步机制

http://blog.csdn.net/bullbat/article/details/7376424 Linux内核同步控制方法有很多,信号量.锁.原子量.RCU等等,不同的实现方法应用于不同的环境来提高操作系统效率.首先,看看我们最熟悉的两种机制——信号量.锁. 一.信号量 首先还是看看内核中是怎么实现的,内核中用struct semaphore数据结构表示信号量(<linux/semphone.h>中): [cpp] view plaincopyprint? struct semaph