《UNIX环境高级编程》笔记 第十章-信号

1. 信号概念

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

Unix早期版本提供的是不可靠信号,信号可能丢失。之后增加了可靠信号机制

每个信号都有一个名字,以3个字符SIG开头。如SIGABRT是终止信号,进程调用abort函数产生这种信号。

  • SIGABRT信号:

    void abort(void);
    

    abort()首先解除了对SIGABRT(6)信号的阻止和忽略,然后为调用进程发送该信号(就像调用了 raise(SIGABRT)一样)。 导致进程的非正常终止,除非SIGABRT 信号被捕获,并且信号处理函数没有返回(调用exit、_exit、 _Exit、longjmp或siglongjmp使信号处理函数没有返回)。

    如果abort()函数导致进程终止,则关闭和刷新所有打开的文件流。

Linux支持63种信号,不存在编号为0的信号(即为空信号),kill函数对信号编号0有特殊应用。1 ~ 31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号为32 ~ 63的信号是后来扩充的,称做可靠信号(实时信号)。不可靠信号和可靠信号的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。

1.1 产生信号条件
  • 用户按某些终端键时,引发终端产生的信号。如在终端上按delete或Ctrl+C键,通常产生中断信号SIGINT。
  • 硬件产生异常信号:除0、无效的内存引用等。因为这些条件通常由硬件检测到,并且通知内核。然后内核为该进程产生适当信号。
  • 进程调用kill(2)函数将任意信号发送给另一个进程或进程组:接收信号进程和发送信号进程的所有者必须相同,或发送方必须是超级用户root
  • 用kill(1)命令将信号(默认是SIGTERM)发送给进程
  • 当检测到某种软件条件发生时将产生信号通知相关进程。如SIGURG、SIGPIPE、SIGALRM(进程设置的定时器已经超时)等
1.2 对信号的处理方式
  • 忽略此信号

    除了两种信号SIGKILL和SIGSTOP绝对不能忽略以外,大多数信号都可使用这种方式处理。

    SIGKILL和SIGSTOP不能被忽略的原因是,它们向内核和超级用户提供了使进程终止或停止的可靠方法(可以理解为终止、停止进程的终极方法)。

    并且如果忽略某些由硬件异常(如除0、非法内存引用)产生的信号,会发生未定义行为

  • 捕捉该信号

    通知内核在某种信号发生时,调用一个用户函数。同样SIGKILL和SIGSTOP不能被捕捉

  • 执行系统默认动作

    对大多数信号的系统默认动作是终止该进程

    Linux对各种信号的默认动作:

    注意“终止+core”表示在进程当前工作目录的core文件中复制了该进程内存映像(默认文件名为core,这种行为即coredump),大多数Unix系统调试程序(如gdb)都使用core文件检查进程终止时的状态。

    注意"硬件故障"对应于具体定义的硬件故障,需要通过对应操作系统手册查看这些信号对应于哪些错误

在这里插入图片描述

Core Dump

当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在一个文件中,这种行为就叫做Core Dump(中文有的翻译成“核心转储”)。我们可以认为 core dump 是“内存快照”,但实际上,除了内存信息之外,还有些关键的程序运行状态也会同时 dump 下来,例如寄存器信息(包括程序指针、栈指针等)、内存管理信息、其他处理器和操作系统状态和信息。core dump 对于编程人员诊断和调试程序是非常有帮助的,因为对于有些程序错误是很难重现的,例如指针异常,而 core dump 文件可以再现程序出错时的情景。

如果没有进行core dump 的相关设置,默认是不开启的。可以通过ulimit -c查看是否开启。如果输出为0,则没有开启,需要执行ulimit -c unlimited开启core dump功能。

不产生core文件的条件:

  • ulimit -c查看core文件有没有限制大小。如果是0则说明禁止了core文件产生
  • 程序设置了用户id(即调用setuid),但当前用户并非该程序文件的所有者
  • 程序设置了组id(即调用setgid),但当前用户并非该程序文件的组所有者
  • 用户没有当前目录或指定core文件产生目录的写权限
  • 文件已存在,用户对该文件没有写权限
  • core文件太大,磁盘空间不足

2. 各种信号详细信息

  • SIGABRT

    调用abort函数产生此信号,进程异常终止。

  • SIGALRM

    调用alarm、setitimer函数设置的定时器、间隔时间超时产生此信号。

  • SIGBUS

    表示发生了一个具体定义的硬件故障。当出现某种类型内存故障时,常产生此种信号

  • SIGCHLD

    子进程终止或停止时,SIGCHLD信号发送给父进程。系统默认会忽略此信号。如果父进程希望得知子进程的这种状态改变,那么通常在信号捕捉函数中调用wait等函数获取子进程pid及终止状态。

  • SIGCONT

    用于作业控制。如果收到此信号的进程处于停止状态,则系统默认动作是该进程继续运行;否则忽略此信号

  • SIGEMT

    表示发生了一个具体定义的硬件故障。

  • SIGFPE

    算术运算异常,如除0、浮点溢出等

  • SIGHUP:挂断信号

    • 终端接口检测到一个连接断开,则将此信号送给与该终端相关的控制进程(会话首进程)。

    • session首进程退出时,该信号被发送到该session中的前台进程组和后台进程组中的每一个进程

    • 若进程的退出,导致一个进程组变成了孤儿进程组,新的孤儿进程组中处于停止(stopped)状态的每一个进程都会收到挂断(SIGHUP)信号,接着又收到继续(SIGCONT)信号。

    系统对SIGHUP信号的默认处理是终止收到该信号的进程。所以若程序中没有捕捉该信号,当收到该信号时,进程就会退出。

  • SIGILL

    进程执行一条非法硬件指令

  • SIGINT:中断信号

    用户按中断键(delete或Ctrl+C),产生此信号发送至前台进程组中的每一个进程。当一个进程失控或者在终端产生大量不需要输出时,常通过此命令终止它。

  • SIGIO

    指示发生一个异步I/O事件。Linux中定义SIGIO与SIGPOLL有相同的值,默认行为是终止该进程。

    #define	SIGIO		SIGPOLL
    
  • SIGIOT

    表示发生了一个具体定义的硬件故障。Linux中定义SIGIOT与SIGABRT有相同值

    #define	SIGIOT		SIGABRT
    
  • SIGKILL:

    这是两个不能被阻塞、捕捉或忽略的信号中的一个,向内核和超级用户提供了使进程终止的可靠方法

  • SIGPIPE

    管道读进程已经终止后,写进程写入管道产生此信号

    当类型为SOCK_STREAM的socket已不再连接时,进程写入该套接字也产生此信号。

  • SIGPOLL

    当在一个可轮询设备上发生一个特定事件时产生此信号(如当系统发现有东西需要你读的时候,就发个SIGPOLL信号来通知)。在未来可能将此信号移除。

  • SIGPROF

    setitimer函数设置的梗概统计间隔定时器超时产生此信号。在未来可能将此信号移除

  • SIGPWR

    电源故障信号。主要用于具有不间断电源的系统。如果电源失效,系统依靠蓄电池继续运行。但是如果蓄电池也将不能工作,此时发送SIGPWR信号。通常是接到蓄电池电压过低信息的进程将SIGPWR发送给init进程,然后由init处理停机操作。

    Linux对SIGPWR的默认动作是终止相关进程。

  • SIGQUIT:退出信号

    用户在终端上按退出键(Ctrl+\)产生此信号,并发送给前台进程组的所有进程。磁信号不仅终止前台进程组,还产生一个core文件。

  • SIGSEGV

    进程进行了一次无效的内存引用(比如访问了一个未经初始化的指针),或发生段错误

  • SIGSTOP

    作业控制信号,它停止一个进程。同SIGKILL,该信号不能被阻塞、忽略和捕获

  • SIGSYS

    指示一个无效的系统调用

  • SIGTERM

    是kill命令发出的默认信号(系统默认终止信号)。相比于SIGKILL,SIGTERM可以被捕获或忽略,因此允许让程序有机会在退出之前做好清理工作,从而优雅地终止。

  • SIGTRAP

    表示发生了一个具体定义的硬件故障。通常使用此信号将控制转移至调试程序

  • SIGTSTP

    交互停止信号。当用户在终端按挂起键(Ctrl+Z)时,将该信号发送到前台进程组中的所有进程。 SIGTSTP与SIGSTOP都是使进程暂停(都使用SIGCONT让进程重新激活)。唯一的区别是SIGSTOP不可以捕获和忽略,而SIGTSTP可以。

  • SIGTTIN

    当一个后台进程组中的进程试图读控制终端时收到此信号,并使该进程暂停。注意,如果读进程属于孤儿进程组,那么read控制终端操作返回出错,不产生此信号,errno设置为EIO。

  • SIGTTOU

    如果禁止后台作业向控制终端写,此时当一个后台进程组进程试图写控制终端时收到此信号,并使该进程暂停。注意,如果写进程属于孤儿进程组,则写操作返回出错,不产生此信号,errno设置为EIO。

    除此之外,tcsetattr、tcsendbreak、tcdrain、tcflush、tcflow以及tcsetpgrp也能产生SIGTTOU信号。

  • SIGURG

    通知进程发生一个紧急情况(用于socket编程)。在网络连接上接到带外的数据时,可选择地产生此信号。

    带外数据:
    带外数据用于迅速告知对方本端发生的重要的事件。它比普通的数据(带内数据)拥有更高的优先级,不论发送缓冲区中是否有排队等待发送的数据,它总是被立即发送。带外数据的传输可以使用一条独立的传输层连接,也可以映射到传输普通数据的连接中。

  • SIGUSR1

    用户定义的信号

  • SIGUSR2

    另一个用户定义的信号,与SIGUSR类似

  • SIGVTALRM

    当一个由setitimer函数设置的虚拟间隔时间超时产生此信号

  • SIGWINCH

    内核维护与每个终端或伪终端相关联窗口的大小。进程可以用ioctl得到或设置窗口大小。如果用ioctl设置窗口大小命令更改了窗口大小,则内核将该信号发送至前台进程组

  • SIGXCPU

    如果进程超过其软CPU时间限制,产生此信号。

  • SIGXFSZ

    如果进程超过其软文件长度限制,产生此信号。

3. signal函数

设置调用进程收到指定信号时的动作(忽略、使用系统默认动作或捕获)

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
  • signum参数:信号名,如SIGABRT

  • handler参数:

    • SIG_IGN:忽略此信号(不能用于SIGKILL和SIGSTOP)

      #define	SIG_IGN	 ((__sighandler_t
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值