进程通讯—信号处理

进程通讯—信号处理

signal 系统调用是 UNIX 和类 UNIX 系统(如 Linux)中用于处理信号的一个基础机制。信号是软件中断,它们可以被系统或者进程发送给另一个进程或线程,以通知它们发生了某个事件。这些事件可以是硬件中断(如外部设备请求服务)、软件异常(如除零错误)、用户请求(如用户请求停止进程)等。

1.基本用法

signal 函数的原型定义在 <signal.h> 头文件中,我们可以通过 signal 系统调用注册信号处理函数:

1
2
3
4
5
6
7
8
9
10
11
12
#include <signal.h>
// 信号处理函数声明
typedef void (*sighandler_t)(int);
/**
* signal 系统调用会注册某一信号对应的处理函数。如果注册成功,当进程收到这一
信号时,将不会调用默认的处理函数,而是调用这里的自定义函数
*
* int signum: 要处理的信号
* sighandler_t handler: 当收到对应的 signum 信号时,要调用的函数
* return: sighandler_t 返回之前的信号处理函数,如果错误会返回 SEG_ERR
*/
sighandler_t signal(int signum, sighandler_t handler);

2.性质

2.1注意事项

  1. 信号处理函数的行为:信号处理函数必须是简单的,并且必须是异步信号安全的。这意味着它只能调用异步信号安全的函数,不能执行那些可能导致死锁或数据竞争的操作,如修改全局变量(除非这些操作是原子的)。
  2. 不可重入性:如果信号处理函数在执行时被中断(例如,通过另一个信号),则原始的处理函数可能会再次被调用,这可能导致不可预测的行为。
  3. 信号掩码:在信号处理函数执行期间,会阻塞(或忽略)与当前信号相同的信号,以避免嵌套调用。但是,这不会影响其他信号。
  4. 可移植性问题:由于不同系统间对信号处理的实现细节可能存在差异,因此使用 signal 函数可能会导致可移植性问题。

2.2替代方案

**由于 **signal 函数存在上述的缺点,许多现代系统提供了更可靠的替代方案,如 sigaction 函数。sigaction 提供了更详细的控制,包括指定信号屏蔽集、获取信号处理函数以及设置信号处理选项等。

3.测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//
// Created by root on 2024/9/16.
//
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void sigint_handler(int signum){
   printf("捕获到信号%d\n",signum);
   exit(signum);
}

int main(int argc,char *argv[]){
   if (signal(SIGINT,sigint_handler)==SIG_ERR){
       perror("signal error");
       exit(EXIT_FAILURE);
  }
   while(1){
       sleep(1);
       printf("你好\n");
  }
   return 0;
}

**在这个示例中,当接收到 **SIGINT 信号(通常由用户按下 Ctrl+C 产生)时,sigint_handler 函数会被调用,程序会打印一条消息并退出。