进程间通信方式之一——信号

本文详细介绍了Linux信号的产生、处理和相关函数,包括硬件和软件触发信号、信号的默认动作及自定义处理、信号安装、信号集操作和阻塞。重点讨论了信号在进程通信中的作用,如SIGHUP、SIGPIPE和SIGURG等,以及其在网络编程中的应用。同时,指出了Unix信号处理的挑战与Posix标准的sigaction函数作为改进方案。
摘要由CSDN通过智能技术生成

信号

信号是由用户、系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常。Linux信号可由如下条件产生:

(1)对于前台进程,用户可以通过输入特殊的终端字符来给它发送信号;

(2)系统异常,比如浮点异常和非法内存段访问;

(3)系统状态变化,比如alarm定时器到期将引起SIGALARM信号;

(4)运行kill命令或调用kill函数。

注意:服务器必须处理(或忽略)一些常见的信号,以免异常终止。

Linux信号表

取值名称解释默认动作
1SIGHUP挂起
2SIGINT中断
3SIGQUIT退出
4SIGILL非法指令
5SIGTRAP断点或陷阱指令
6SIGABRTabort发出的信号
7SIGBUS非法内存访问
8SIGFPE浮点异常
9SIGKILLkill信号不能被忽略、处理和阻塞
10SIGUSR1用户信号1
11SIGSEGV无效内存访问
12SIGUSR2用户信号2
13SIGPIPE管道破损,没有读端的管道写数据
14SIGALRMalarm发出的信号
15SIGTERM终止信号
16SIGSTKFLT栈溢出
17SIGCHLD子进程退出默认忽略
18SIGCONT进程继续
19SIGSTOP进程停止不能被忽略、处理和阻塞
20SIGTSTP进程停止
21SIGTTIN进程停止,后台进程从终端读数据时
22SIGTTOU进程停止,后台进程想终端写数据时
23SIGURGI/O有紧急数据到达当前进程默认忽略
24SIGXCPU进程的CPU时间片到期
25SIGXFSZ文件大小的超出上限
26SIGVTALRM虚拟时钟超时
27SIGPROFprofile时钟超时
28SIGWINCH窗口大小改变默认忽略
29SIGIOI/O相关
30SIGPWR关机默认忽略
31SIGSYS系统调用异常

对于signal信号,绝大部分的默认处理都是终止进程或停止进程,或dump内核映像转储。 上述的31的信号为非实时信号,其他的信号32-64 都是实时信号。

信号的产生

信号来源分为硬件类和软件类。

硬件方式

  • 用户输入:比如在终端上按下组合键ctrl+C,产生SIGINT信号;
  • 硬件异常:CPU检测到内存非法访问等异常,通知内核生成相应信号,并发送给发生事件的进程;

软件方式

通过系统调用,发送signal信号。

  • kill():用于向进程或进程组发送信号;
  • sigqueue():只能向一个进程发送信号,不能像进程组发送信号;主要针对实时信号提出,与sigaction()组合使用,当然也支持非实时信号的发送;
  • alarm():用于调用进程指定时间后发出SIGALARM信号;
  • setitimer():设置定时器,计时达到后给进程发送SIGALRM信号,功能比alarm更强大;
  • abort():向进程发送SIGABORT信号,默认进程会异常退出。
  • raise():用于向进程自身发送信号;

信号的处理

信号由内核进行统一管理。可以由内核产生,也可以由其他进程产生并发送给内核,再由内核传递给目标进程。
在这里插入图片描述
(1)内核中针对每一个进程都有一个表(未决pending信号集合)来保存信号,当内核需要将信号传递给某个进程时,就在该进程对应的表中写入信号,这样就生成了信号。

(2)进程可以选择是否阻塞某信号,若阻塞,则该信号进入阻塞信号列表(blocked集合),只有解除阻塞后进程才接收该信号。

(3)当该进程由用户态陷入内核态,再次切换到用户态之前,会检查该进程的未被阻塞的待处理信号的集合,也就是存在于pending集合中同时又不存在于blocked集合中(pending & ~blocked)。

(4)如果集合为空,则控制就传递回该进程中的下一条指令。如果非空,那么内核会选择集合中的某个信号(通常是序号最小的信号),并且强制该进程接收该信号。

(5)进程接收信号后,进程对信号有以下3种处理方式:

  • 默认,即接收到信号后按默认的行为处理该信号(多数应用采取的处理方式)。
  • 自定义,即执行用户编写的信号处理函数。
  • 忽略,即接收到信号后不做任何反应。

(6)一旦进程完成了这个行为,那么控制就传递回该进程中的下一条指令。

信号安装

进程处理某个信号前,需要先在进程中安装此信号。安装过程主要是设置信号的处理函数。

信号安装函数signal():

#include <signal.h>
_sighandler_t signal(int sig, _sighandler_t _handler);
//sig:要捕获的信号类型,_handler:函数指针,用于指定信号sig的处理函数

Unix信号处理的缺陷(难点):

(1)处理程序与主程序并发运行,共享同样的全局变量,可能存在相互干扰。

(2)如何以及何时接收信号的规则常常有违人的直觉。

(3)不同的系统有不同的信号处理语义。

Posix标准定义的信号安装函数sigaction():

#include <signal.h>
int sigaction(int sig, const struct sigaction* act, struct sigaction* oact);
//sig:要捕获的信号类型,act:新的信号处理方式,oact:输出信号先前的处理方式

与signal()函数相比,该函数具备可移植性。但它用得并不广泛,一个更简洁的方式是使用包装函数Signal()。

信号集

Linux使用数据结构sigset_t来表示一组信号,它实际上是一个长整型数组,数组的每个元素的每个位表示一个信号。

信号集操作函数:

int sigemptyset(sigset_t *set);//信号集全部清0;
int sigfillset(sigset_t *set); //信号集全部置1,则信号集包含linux支持的64种信号;
int sigaddset(sigset_t *set, int signum);//向信号集中加入signum信号;
int sigdelset(sigset_t *set, int signum);//向信号集中删除signum信号;
int sigismember(const sigset_t *set, int signum);//判定信号signum是否存在信号集中。

信号阻塞函数:

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset))//不同how参数,实现不同功能
//SIG_BLOCK:将set指向信号集中的信号,添加到进程阻塞信号集;
//SIG_UNBLOCK:将set指向信号集中的信号,从进程阻塞信号集删除;
//SIG_SETMASK:将set指向信号集中的信号,设置成进程阻塞信号集;
int sigpending(sigset_t *set)); //获取已发送到进程,却被阻塞的所有信号即,即处于未决状态的信号;
int sigsuspend(const sigset_t *mask)); //用mask代替进程的原有掩码,并暂停进程执行,直到收到信号再恢复原有掩码并继续执行进程。

网络编程相关信号

SIGHUP:在用户终端连接(正常或非正常)结束时发出,通常是在终端的控制进程结束时,通知同一session内的各个作业,这时它们与控制终端不再关联。一般来说,控制进程就是登入系统的shell进程。对于没有控制终端的网络后台程序而言,它们通常利用SIGHUP信号来强制服务器重读配置文件。

系统对SIGHUP信号的默认处理是终止收到该信号的进程。

SIGPIPE:默认情况下,往一个读端关闭的管道或socket连接中写数据将引发该信号。

默认行为也是结束进程。

SIGURG:Linux内核用于通知应用程序带外数据到达。(另一种方式是使用I/O复用技术)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值