信号的概念
Linux操作系统中,
信号是一种进程间通信(Inter-Process Communication, IPC)机制,用于向其他进程发送通知或指示
,通常是为了通知特定事件的发生,如程序终止、用户按下特定按键等。信号提供了一种异步的、非阻塞的通信方式,因此在某些情况下非常有用。
使用信号主要的两个目的是:
- 让进程知道已经发生了一个特定的事情。
- 强迫进程执行它自己代码中的信号处理程序
信号的特点:
- 简单
- 不能携带大量信息
- 满足某个特定条件才发送
- 优先级比较高
常用的一些信号
我们可以使用Linux命令:
kill -l
去查看所有的信号列表。
以下是一些常见和常用的信号及其简要说明:
-
SIGHUP(1):挂起信号。通常用于通知进程在终端连接关闭时重新读取其配置文件。这个信号可以让一个守护进程(daemon)重新初始化自身,以便在不终止进程的情况下应用新的配置设置。
-
SIGINT(2):中断信号。当用户在终端中按下Ctrl+C时,通常会发送此信号给前台进程。默认情况下,此信号会导致进程终止并生成核心转储文件(core dump)。
-
SIGQUIT(3):退出信号。当用户在终端中按下Ctrl+\时,通常会发送此信号给前台进程。默认情况下,此信号会导致进程终止并生成核心转储文件。
-
SIGILL(4):非法指令信号。当进程尝试执行非法的或未定义的指令时,系统会发送此信号给进程。默认情况下,此信号会导致进程终止并生成核心转储文件。
-
SIGTRAP(5):跟踪陷阱信号。通常由调试器(如gdb)使用,以便在断点处中断进程的执行。默认情况下,此信号会导致进程终止并生成核心转储文件。
-
SIGABRT(6):异常终止信号。通常在进程因内部错误而需要立即终止时发送此信号。默认情况下,此信号会导致进程终止并生成核心转储文件。
-
SIGBUS(7):总线错误信号。当进程执行时发生硬件相关的错误(如内存访问错误)时,系统会发送此信号给进程。默认情况下,此信号会导致进程终止并生成核心转储文件。
-
SIGFPE(8):浮点异常信号。当进程执行时发生与浮点运算相关的错误(如除以零)时,系统会发送此信号给进程。默认情况下,此信号会导致进程终止并生成核心转储文件。
-
SIGKILL
(9):强制终止信号
。此信号用于立即终止进程,进程无法捕获、忽略或阻塞此信号
。这是一个强制手段,可能导致数据丢失或系统处于不稳定状态。 -
SIGUSR1(10)和SIGUSR2(12):用户自定义信号。这两个信号保留给用户自定义使用,用于实现自定义的进程间通信。
-
SIGSEGV(11):
段错误信号
。当进程执行时发生与内存访问相关的错误(如访问无效的内存地址)时,系统会发送此信号给进程
。默认情况下,此信号会导致进程终止并生成核心转储文件。 -
SIGPIPE(13):管道破裂信号。当进程尝试向一个没有读端的管道中写入数据时,系统会发送此信号给进程。默认情况下,此信号会导致进程终止。
-
SIGALRM(14):闹钟信号。通常由alarm()或setitimer()系统调用设置,用于在指定时间后发送信号给进程。此信号可以用于实现定时器功能。
-
SIGTERM(15):终止信号。这是一个通用的、友好的终止信号,通常用于要求进程正常结束。进程可以捕获此信号并执行清理工作,然后自行结束。
-
SIGCHLD(17):子进程状态改变信号。当子进程终止、停止或恢复运行时,系统会发送此信号给父进程。此信号通常用于实现父进程对子进程的监控。
-
SIGCONT(18):继续信号。用于恢复之前被SIGSTOP或SIGTSTP信号停止的进程。
-
SIGSTOP
(19):停止信号。此信号用于暂停进程的执行。与SIGKILL类似,进程无法捕获、忽略或阻塞此信号
。 -
SIGTSTP(20):终端停止信号。当用户在终端中按下Ctrl+Z时,通常会发送此信号给前台进程。此信号可以被进程捕获并执行特定操作,如保存状态或清理资源。
-
SIGTTIN(21)和SIGTTOU(22):终端输入/输出信号。当后台进程尝试从控制终端读取输入或向控制终端写入输出时,系统会发送这些信号给进程。默认情况下,这些信号会导致进程停止。
-
SIGURG(23):紧急信号。当套接字上有紧急数据可读时,系统会发送此信号给进程。此信号通常用于网络编程中,用于处理异常情况。
以上信号仅为Linux系统中常见和常用的信号,实际上还有更多其他信号。不过,了解这些信号已经足够应对大部分场景。对于进程来说,可以根据实际需求捕获和处理这些信号,实现进程间的通信和协同工作。
信号的基本原理
信号的基本原理是,一个进程或内核可以向另一个进程发送信号,而接收信号的进程可以对该信号做出相应的响应。接受信号的进程做出的五种默认响应为:
- Term: 终止进程
- Ign: 当前进程忽略掉这个信号
- Core: 终止进程,并生成一个Core文件
- Stop: 暂停当前进程
- Cont: 继续执行当前被暂停的进程
信号的重要特性
以下是Linux信号机制的一些重要特性:
-
信号的发送:进程可以使用系统调用kill()、raise()等函数向其他进程发送信号。同时,用户也可以通过终端使用kill命令来发送信号。
-
信号的接收:当进程接收到信号时,它可以选择忽略信号、执行默认操作或者捕获信号并执行自定义的信号处理函数。
-
信号的处理:进程可以使用系统调用sigaction()或signal()为特定信号设置自定义的信号处理函数。信号处理函数通常用于在接收到信号时执行某些任务,如清理资源或修改程序的执行状态。
-
信号的阻塞与解除阻塞:进程可以使用sigprocmask()等函数来阻塞或解除阻塞特定信号。阻塞的信号不会立即被处理,而是在信号解除阻塞后才会被传递给进程。
-
信号的排队与合并:Linux信号支持排队,但是对于同一信号类型,只会保留一个实例。这意味着,在信号尚未处理时,如果再次发送相同类型的信号,该信号不会被排队,而是被合并到一个实例中。
-
实时信号:Linux还支持实时信号(RT signals),它们可以排队并携带额外的数据。实时信号的优先级较高,因此在多个信号同时传递给进程时,实时信号会优先处理。
信号相关的函数
- Linux系统调用之kill函数(进程相关函数)
- 标准C库之raise,abort函数(进程通信函数,发送信号类函数)
- Linux系统调用之alarm,setitimer函数(定时器,向当前进程发送一个信号、循环/间隔定时器,周期性的向当前进程发送信号)
信号捕捉函数
- Linux系统调用之signal,sigaction函数 (捕捉指定的信号,自定义/默认处理该信号)
信号集的概念
- 许多信号相关的系统调用都需要能表示一组不同的信号,多个信号可使用一个称之为信号集的数据结构来表示,其系统数据类型为 sigset_t。
- 在 PCB 中有两个非常重要的信号集。一个称之为 “阻塞信号集” ,另一个称之为“未决信号集” 。这两个信号集都是内核使用位图机制来实现的。但操作系统不允许我们直接对这两个信号集进行位操作。而需自定义另外一个集合,借助信号集操作函数来对 PCB 中的这两个信号集进行修改。
- 信号的 “未决” 是一种状态,指的是从信号的产生到信号被处理前的这一段时间。
- 信号的 “阻塞” 是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。
- 信号的阻塞就是让系统暂时保留信号留待以后发送。由于另外有办法让系统忽略信号,所以一般情况下信号的阻塞只是暂时的,只是为了防止信号打断敏感的操作。
阻塞信号集和未决信号集
与信号集相关的函数
- 标准C库sigemptyset、sigfillset函数(清空自定义信号集,阻塞信号
集所有信号) - 标准C库之sigismember、sigaddset、sigdelset函数(检查某个信号是否被阻塞,阻塞自定义信号集中的某一个信号,不阻塞自定义信号集中的某一个信号)
- Linux系统调用之sigprocmask、sigpending函数(设置内核阻塞信号集,查看内核未决信号集)
内核实现信号捕捉的过程
SIGCHLD信号
- SIGCHLD信号产生的条件
- 子进程终止时
- 子进程接收到 SIGSTOP 信号停止时
- 子进程处在停止态,接受到SIGCONT后唤醒时
以上三种条件都会给父进程发送 SIGCHLD 信号,父进程默认会忽略该信号
总结
总之,信号是Linux中一种重要的进程间通信机制,它提供了一种简单、灵活的方式来处理进程间的通知和事件。然而,信号机制的异步性和非阻塞性也导致了一些困难和问题,例如竞态条件和信号处理的复杂性。因此,在某些场景下,其他进程间通信机制,如管道、消息队列和共享内存等,可能会更加适合。
最后的最后,如果你觉得我的这篇文章写的不错的话,请给我一个赞与收藏,关注我,我会继续给大家带来更多更优质的干货内容
。