本篇要点:


2025/12/30
①简要介绍信号是什么、如何产生信号以及信号的分类,在1.3.3有所有的信号的列表。
②介绍信号的三种处理方式,分别为默认处理 SIG_DFL、忽略信号SIG_IGN和捕获信号(自定义信号处理函数)。
③简短介绍信号从产生到处理完成的流程。
④详细说明信号的相关函数,发送信号的函数:kill、 raise 和 alarm,以及等待接收信号函数 pause,处理信号函数 signal。
⑤最后附有信号相关的3给综合示例。


目录

1  信号的概述

1.1  信号的本质

1.2  信号的来源

1.2.1  硬件方式产生的信号(Hardware-Generated Signals)

1.2.1.1  键盘操作触发

1.2.1.2  程序运行错误触发

1.2.2  软件方式产生的信号(Software-Generated Signals)

1.3  信号的种类

1.3.1  按可靠性分类

1.3.1.1   不可靠信号(Non-Reliable Signals)

1.3.1.2  可靠信号(Reliable Signals)

1.3.2  按时间特性分类

1.3.2.1  非实时信号(Non-Real-Time Signals)

1.3.2.2  实时信号(Real-Time Signals)

1.3.3  信号列表

1.4  信号的处理方式

1.4.1  默认处理(SIG_DFL)

1.4.2  忽略信号(SIG_IGN)

1.4.3  捕获信号(自定义处理函数)

1.5  信号通信的流程

1.5.1  阶段一:信号产生(Signal Generation)

1.5.2  阶段二:信号在进程中注册(Signal Registration)

1.5.3  阶段三:信号处理(Signal Handling)

1.5.4  信号通信补充说明

2  信号相关函数

2.1  发送信号

2.1.1  kill() —— 向进程发送信号

2.1.2  raise() —— 发送信号给当前进程

2.1.3  alarm() —— 设置定时器

2.2  等待接收信号

2.2.1  pause() —— 暂停进程直到接收到信号

2.3  处理信号

2.3.1  signal() —— 设置信号处理函数

3  综合示例

3.1  综合案例1

3.2  综合案例2

3.3  综合案例3


1  信号的概述

1.1  信号的本质

Linux 系统 中,信号(Signal)本质上是一种软中断机制(Software Interrupt),用于通知进程发生了某种异步事件。
它是 软件层面对硬件中断机制的模拟,在概念上,一个进程接收到信号与处理器接收到硬件中断请求非常相似。

信号是 进程间通信(IPC)的一种异步通信机制

  • 进程在执行过程中无需主动等待信号的到来;

  • 信号可能在任何时刻由内核或其他进程异步地发送过来;

  • 被信号打断后,进程会根据预设的处理方式(默认处理、忽略、自定义函数)来应对该事件。

除了基本的事件通知功能外,信号机制还具备一定的控制与管理能力,例如:

  • 可以控制进程的执行状态(终止、暂停、恢复等);

  • 可以实现进程间的事件同步与异常响应。

1.2  信号的来源

信号的产生途径多种多样,根据产生条件的不同,可以大体分为 两类:硬件方式软件方式

1.2.1  硬件方式产生的信号(Hardware-Generated Signals)

硬件事件在运行过程中触发信号,通常由 内核检测到异常或外部输入事件 所引起。常见情况包括:

1.2.1.1  键盘操作触发

用户在终端输入特定组合键时,终端驱动程序会向前台进程发送信号。

例如:

操作键

信号名称

信号编号

默认动作

是否可捕获

作用对象

默认处理行为

说明

Ctrl + C

SIGINT

2

终止进程

✅ 可捕获

前台进程组中的所有进程

终止进程

常用的中断信号,常用于终止正在运行的程序

Ctrl + Z

SIGTSTP

20

暂停进程

✅ 可捕获

前台进程组中的所有进程

挂起进程(暂停),可用 fg 或 bg 恢复

用于暂停进程,之后可恢复

Ctrl + \

SIGQUIT

3

终止并生成 core

✅ 可捕获

前台进程组中的所有进程

终止进程并生成 core 文件

常用于调试,程序异常退出时产生 core dump

Ctrl + D

EOF(非信号)

输入结束

输入流

关闭标准输入流

表示输入流结束,并非信号

1.2.1.2  程序运行错误触发
  • 当程序在运行中出现异常或非法操作时,内核会自动向该进程发送信号:

    • 空指针访问、非法内存访问 → SIGSEGV(段错误)

      int *p = NULL;
      *p = 12456;   // 触发段错误,进程被内核终止
      
    • 除以 0 → SIGFPE(算术错误信号)

    • 执行非法指令 → SIGILL


1.2.2  软件方式产生的信号(Software-Generated Signals)

软件层面可以通过系统调用或命令来产生信号,这些信号通常由进程主动触发系统函数自动触发

1.2.2.1  命令触发

  • 通过命令行工具向进程发送信号,例如:

    kill -9 1234     # 向 PID 为 1234 的进程发送 SIGKILL 信号
    

    (内部实际上是调用了 kill() 系统调用。)

1.2.2.2  函数触发

  • 程序中可以通过以下函数主动发送信号:

    kill(pid_t pid, int sig);       // 向指定进程发送信号
    raise(int sig);                 // 向当前进程发送信号
    sigqueue(pid_t pid, int sig, const union sigval value); // 带参数发送信号
    
  • 示例:

    kill(getpid(), SIGINT);   // 向自己发送中断信号
    

1.3  信号的种类

Linux 系统中的信号可以从不同角度进行分类,主要包括 按可靠性分类按时间特性分类


1.3.1  按可靠性分类

1.3.1.1   不可靠信号(Non-Reliable Signals)
  • 早期 UNIX 系统 设计较为简单,信号机制存在缺陷:
    当同一种信号在尚未处理完时再次到达,后续信号可能被丢失。

  • 因此,这类可能丢失的信号被称为 “不可靠信号”

  • 在 Linux 中,信号编号 1~31 的传统信号属于不可靠信号。

特点:

  • 不支持信号排队(同类型信号可能被覆盖)

  • 信号值固定,语义预定义(例如 SIGINT、SIGKILL 等)


1.3.1.2  可靠信号(Reliable Signals)
  • 为解决信号丢失问题,Linux 在原有信号机制基础上扩展了一组新的信号,称为 可靠信号

  • 这类信号支持 信号排队机制
    当一个信号正在被处理时,如果再次收到同类信号,会进入信号队列中,等待前一个信号处理完成后依次处理。

  • 因此,不会发生信号丢失的情况。

范围:

  • 可靠信号编号从 SIGRTMIN ~ SIGRTMAX(一般为 34~64)。

特点:

  • 支持排队(不会丢失)

  • 可附带参数(通过 sigqueue() 发送)

  • 信号含义可由用户自定义


1.3.2  按时间特性分类

1.3.2.1  非实时信号(Non-Real-Time Signals)
  • 非实时信号即早期的 不可靠信号

  • 每个信号都有固定含义,系统预定义行为,例如:

    • SIGINT:中断进程

    • SIGKILL:强制结束进程

  • 不支持排队机制,如果在信号处理期间收到相同信号,后者会被忽略。

总结:
非实时信号的特点是:

“固定含义,不支持排队,可能丢失”


1.3.2.2  实时信号(Real-Time Signals)
  • 实时信号是 POSIX 标准 新增的信号类型,属于可靠信号的一种

  • 允许用户自定义信号编号及其处理逻辑。

  • 具有 排队优先级 特性:

    • 多个相同信号可以依次排队执行;

    • 编号越小的实时信号优先级越高;

    • 可以使用 sigqueue() 附带数据发送。

总结:
实时信号的特点是:

“可自定义,支持排队,不丢失,具备优先级”

1.3.3  信号列表

信号名 数值 英文名称 默认行为 说明
SIGHUP 1 Hang Up 终止进程 控制终端挂起或控制进程终止(终端关闭)
SIGINT 2 Interrupt 终止进程 键盘中断(Ctrl + C)
SIGQUIT 3 Quit 终止并产生 core dump 键盘退出(Ctrl + \)
SIGILL 4 Illegal Instruction 终止并产生 core dump 非法指令(执行了错误的机器码)
SIGTRAP 5 Trace Trap 终止并产生 core dump 调试断点或陷阱
SIGABRT 6 Abort 终止并产生 core dump 调用 abort() 函数触发
SIGBUS 7 Bus Error 终止并产生 core dump 总线错误(内存访问未对齐)
SIGFPE 8 Floating Point Exception 终止并产生 core dump 浮点异常(除以零等)
SIGKILL 9 Kill 立即终止 强制杀死进程,不能被捕获或忽略(此信号不可更改处理方式)
SIGUSR1 10 User-defined Signal 1 终止进程 用户自定义信号1
SIGSEGV 11 Segmentation Violation 终止并产生 core dump 非法内存访问(段错误)
SIGUSR2 12 User-defined Signal 2 终止进程 用户自定义信号2
SIGPIPE 13 Broken Pipe 终止进程 向无读端的管道写数据
SIGALRM 14 Alarm Clock 终止进程 定时器信号(来自 alarm()
SIGTERM 15 Terminate 终止进程 请求进程正常终止(可捕获)
SIGSTKFLT 16 Stack Fault 终止进程 协处理器栈错误(很少使用)
SIGCHLD 17 Child Status Changed 忽略 子进程结束或状态改变时发送给父进程
SIGCONT 18 Continue 继续运行 恢复被暂停的进程(不能被阻塞)
SIGSTOP 19 Stop 暂停进程 暂停进程(不可捕获或忽略)
SIGTSTP 20 Terminal Stop 暂停进程 键盘暂停(Ctrl + Z)
SIGTTIN 21 Background Read 暂停进程 后台进程尝试从终端读输入
SIGTTOU 22 Background Write 暂停进程 后台进程尝试往终端写输出
SIGURG 23 Urgent Condition 忽略 套接字紧急(out-of-band)数据到达
SIGXCPU 24 CPU Time Limit Exceeded 终止并产生 core dump 超出 CPU 时间限制
SIGXFSZ 25 File Size Limit Exceeded 终止并产生 core dump 超出文件大小限制
SIGVTALRM 26 Virtual Alarm Clock 终止进程 虚拟时间定时器信号
SIGPROF 27 Profiling Timer Expired 终止进程 统计定时器信号
SIGWINCH 28 Window Change 忽略 终端窗口大小改变
SIGIO 29 I/O Possible 终止进程 异步 I/O 事件(可读可写)
SIGPWR 30 Power Failure 终止进程 电源故障(UPS事件)
SIGSYS 31 Bad System Call 终止并产生 core dump 错误的系统调用
SIGRTMIN ~ SIGRTMAX 34~64 Real-Time Signals 默认终止 实时信号(可自定义,用于线程或实时通信)

1.4  信号的处理方式

在 Linux 系统中,每个信号都可以被进程以不同方式处理,主要分为 三类


1.4.1  默认处理(SIG_DFL)

  • 含义:使用系统为该信号预定义的默认动作。

  • 特点

    • 对大多数信号,默认动作是 终止进程

    • 对少数信号,默认动作可能是 忽略信号停止进程

  • 示例

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

int main() {
    signal(SIGINT, SIG_DFL); // Ctrl+C 将按默认方式终止进程
    while(1);                // 无限循环等待信号
    return 0;
}

1.4.2  忽略信号(SIG_IGN)

  • 含义:进程 忽略该信号,收到信号时不做任何处理,相当于屏蔽信号。

  • 特点

    • 对于无法忽略的信号(如 SIGKILL, SIGSTOP),此方式无效;

    • 常用于忽略临时无关紧要的信号。

  • 示例

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

int main() {
    signal(SIGINT, SIG_IGN); // 忽略 Ctrl+C 信号
    while(1);                // 无限循环,按 Ctrl+C 不会终止程序
    return 0;
}

1.4.3  捕获信号(自定义处理函数)

  • 含义:进程为信号注册一个 自定义处理函数(类似中断服务函数),信号到来时执行该函数。

  • 特点

    • 可以执行用户定义的逻辑,例如打印消息、清理资源、计数等;

    • 捕获信号的函数原型:

      void handler(int signum);
      
  • 示例

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

void sig_handler(int signum) {
    printf("收到信号 %d\n", signum);
}

int main() {
    signal(SIGINT, sig_handler); // 注册自定义处理函数
    while(1);                    // 无限循环,按 Ctrl+C 时不会终止
    return 0;
}

1.5  信号通信的流程

一个完整的信号生命周期可以分为 三个阶段,涉及 四个关键事件


1.5.1  阶段一:信号产生(Signal Generation)

  • 发生位置:内核(Kernel)

  • 触发方式

    • 硬件触发:如空指针访问、非法指令、键盘组合键(Ctrl+C)

    • 软件触发:如 kill()raise()sigqueue()

  • 说明
    内核负责生成信号,并将其标记到目标进程的信号位图中,等待进程处理。


1.5.2  阶段二:信号在进程中注册(Signal Registration)

  • 发生位置:用户进程(User Process)

  • 操作

    • 进程通过 signal()sigaction() 注册信号处理函数

    • 也可以选择忽略信号 (SIG_IGN) 或使用默认动作 (SIG_DFL)

  • 说明

    • 用户进程不能直接给其他用户进程发送信号,必须通过 内核

    • 用户空间只负责告诉内核如何处理信号


1.5.3  阶段三:信号处理(Signal Handling)

  • 发生位置:用户进程

  • 操作

    • 当进程在用户态运行时,内核检查是否有待处理信号

    • 若有,则暂停当前执行,调用注册的处理函数执行信号响应逻辑

    • 信号处理完成后,恢复原来的程序执行


1.5.4  信号通信补充说明

  • 用户进程不能直接向其他用户进程发送信号

    • 必须通过内核转发

    • 例子:

      进程A --> 内核 --> 发送信号 --> 进程B
  • 用户空间不具备发送信号的能力

    • 进程只能将信号请求提交给内核,由内核完成实际发送

2  信号相关函数

2.1  发送信号

2.1.1  kill() —— 向进程发送信号

头文件

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

② 函数原型

int kill(pid_t pid, int sig);

③ 函数参数

函数参数 参数类型 参数功能
pid pid_t

指定要发送信号的目标进程或进程组:

pid > 0:向指定 PID 的进程发送信号。

pid = 0:向调用进程所在的进程组的所有进程发送信号。

pid = -1:向系统上所有有权限的进程发送信号。

pid < -1:向进程组 ID = -pid 的所有进程发送信号。

sig int

要发送的信号编号,例如:

SIGKILL(9) 强制终止进程。

SIGTERM(15) 请求终止(可被捕获)。

SIGSTOP 暂停进程。

SIGCONT 继续被暂停的进程。

④ 返回值

返回值 返回值类型 返回值功能
-1 int 失败时返回 -1,并设置 errno 以指示错误原因。
0 int 成功时返回 0

常见错误号:

  • ESRCH:指定的进程不存在;

  • EPERM:没有权限向该进程发送信号。

⑤ 函数功能

kill() 函数用于向指定的进程发送信号。这个信号可以用来通知进程进行特定操作,比如终止进程(SIGTERM)或强制杀死进程(SIGKILL)。即使发送的信号是 0kill() 也会检查进程是否存在,而不发送实际的信号。

  • 如果 pid 为正数,kill() 向指定的进程发送信号。

  • 如果 pid0kill() 向当前进程组的所有进程发送信号。

  • 如果 pid-1,则发送信号给所有进程,除了进程1(init)。

如果目标进程拒绝某个信号(如 SIGKILL),则 kill() 会失败。

kill() 函数与 kill 命令的区别

对比项 kill() 函数 kill 命令
使用位置 C 语言程序中调用 在终端命令行中使用
参数顺序 kill(pid, sig)先进程,后信号 kill -sig pid先信号,后进程
示例 kill(1234, 9); kill -9 1234

⑥ 示例代码

利用 kill() 函数结束进程

思路

  1. 第一个终端运行目标程序。

  2. 第二个终端运行本程序,通过命令行参数发送信号结束目标进程。

kill.c

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

int main(int argc,char *argv[]) {

    if(argc != 3) {
        printf("用法:%s -sig pid", argv[0]);
        printf("示例:%s -9 1234",argv[0]);
        return -1;
    }

    int sig = atoi(argv[1]) * (-1);

    pid_t pid = (pid_t)atoi(argv[2]);

    int ret = kill(pid, sig);
    if(ret == -1) {
        printf("用法:%s -sig pid", argv[0]);
        printf("示例:%s -9 1234",argv[0]);
        perror("kill failed");
        return -1;
    }
    else {
        printf("成功%s pid为%d 的进程", argv[0], pid);
    }

    return 0;
}


count.c

#include <unistd.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main() {

    printf("PID:%d\n", getpid());
    int count = 1;
    while (count++) {
        printf("count:%d\n", count);
        sleep(1);
    }
    return 0;
}

2.1.2  raise() —— 发送信号给当前进程

① 头文件

#include <signal.h>

② 函数原型

int raise(int sig);

③ 函数参数

函数参数 参数类型 参数功能
sig int

要发送的信号,通常是信号常量,表示将该信号发送给调用本函数的进程本身

常用信号:

信号宏 数值 含义
SIGINT 2 中断信号(如 Ctrl + C)
SIGTERM 15 终止信号(可捕获、可处理)
SIGKILL 9 强制杀死(不可捕获、不可忽略)
SIGSEGV 11 段错误
SIGABRT 6 调用 abort() 产生的信号

④ 返回值

返回值 返回值类型 返回值功能
0 int 成功时返回 0
-1 int 失败时返回 -1,并设置 errno 以指示错误原因。

⑤ 函数功能

raise() 函数用于向当前进程发送指定的信号。它等效于调用 kill(getpid(), sig),即向当前进程发送信号 sig

  • 常见的信号包括:SIGINT(中断信号,通常由 Ctrl+C 触发)、SIGTERM(终止信号)、SIGSEGV(段错误信号)等。

  • raise() 是一个同步函数,发送信号后会根据信号类型触发相应的处理动作,如信号处理函数的调用或者进程终止。

raise() 适用于向当前进程发送信号,用来触发进程的信号处理行为。

⑥ 示例代码

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

void handler(int sig) {
    printf("捕获到信号 %d\n", sig);
}

int main(void) {
    // 注册信号处理函数
    signal(SIGINT, handler);

    printf("程序开始运行...\n");

    // 发送信号给自己
    raise(SIGINT);

    printf("程序正常结束。\n");
    return 0;
}

2.1.3  alarm() —— 设置定时器

① 头文件

#include <unistd.h>

② 函数原型

unsigned int alarm(unsigned int seconds);

③ 函数参数

函数参数 参数类型 参数功能
seconds unsigned int 定时器倒计时的秒数,表示多少秒后发送 SIGALRM 信号给当前进程。如果设置为 0,则取消定时器。

④ 返回值

返回值 返回值类型 返回值功能
0 unsigned int 如果定时器之前没有设置,返回 0。
x unsigned int 如果定时器之前已设置,返回之前设置的秒数,即剩余的时间。

⑤ 函数功能

alarm() 函数用于设置一个定时器,经过指定的时间(以秒为单位)后,向当前进程发送 SIGALRM 信号。SIGALRM 信号可以被捕获并通过相应的信号处理函数进行处理。alarm() 返回值是之前设置的定时器的剩余时间(如果有的话)。

  • 如果定时器已经设置并且还有剩余时间,alarm() 会返回剩余的秒数。

  • 如果设置为 0,则会取消定时器,并且不会发送 SIGALRM 信号。

常见用法是设置超时机制,或者定时执行某些操作。

⑥ 示例代码

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

void sig_handler(int sig) {
    printf("收到信号 %d:定时器时间到!\n", sig);
}

int main(void) {
    // 注册 SIGALRM 信号处理函数
    signal(SIGALRM, sig_handler);

    printf("设置定时器 5 秒...\n");
    alarm(5);  // 5秒后产生SIGALRM信号

    // 主进程持续运行,等待信号
    for (int i = 1; i <= 10; i++) {
        printf("第 %d 秒\n", i);
        sleep(1);
    }

    return 0;
}

2.2  等待接收信号

2.2.1  pause() —— 暂停进程直到接收到信号

① 头文件

#include <unistd.h>

② 函数原型

int pause(void);

③ 函数参数

pause() 不接受任何参数。

④ 返回值

返回值 返回值类型 返回值功能
-1 int 如果调用失败,返回 -1,并设置 errno。常见的错误是 EINTR(由于信号中断)。
无返回值 pause() 会一直阻塞直到接收到信号,因此如果成功调用时,它没有返回值,除非因信号中断而返回。

⑤ 函数功能

pause() 函数会使当前进程挂起,直到接收到一个信号为止。信号会被送到进程并触发相应的信号处理函数。pause() 会阻塞当前进程,直到信号处理函数被调用,或者信号导致进程终止。

  • pause() 是一个阻塞调用,它会让进程停止执行,直到收到信号。

  • 如果信号处理函数返回时,pause() 会返回 -1,并且 errno 会设置为 EINTR,表示调用被信号中断。

pause() 常用于在信号驱动的程序中,保持进程等待信号的到来,通常与信号处理程序配合使用。

⑥ 示例代码

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

void sig_handler(int sig) {
    printf("收到信号 %d:唤醒进程!\n", sig);
}

int main(void) {
    // 注册信号处理函数
    signal(SIGALRM, sig_handler);

    printf("设置定时器为 5 秒...\n");
    alarm(5);  // 5秒后触发SIGALRM信号

    printf("进程进入休眠,等待信号...\n");
    pause();   // 暂停执行,直到收到信号

    printf("pause() 返回,程序继续运行。\n");

    return 0;
}

2.3  处理信号

2.3.1  signal() —— 设置信号处理函数

① 头文件

#include <signal.h>

② 函数原型

typedef void (*__sighandler_t)(int); 
__sighandler_t signal(int __sig, __sighandler_t __handler);

等价于

void (*signal(int __sig, void (*__handler)(int)))(int);

虽然原型比较复杂,但可以理解为:

signal(信号编号, 信号处理函数);

这个函数的难点在返回值第二个参数,其实返回值和第二个参数的类型是一样的,都是函数指针__sighandler_t,也就是说这个函数的第二参数的参数类型是函数指针,返回值也是函数指针。

这个函数容易会被看成一个函数指针,但实际上它是指针函数,只是它的返回值特殊一点,是函数指针。

关于函数指针和指针函数的区别,可以参考C与指针

③ 函数参数

函数参数 参数类型 参数功能
__sig int 要捕获的信号编号,例如 SIGINTSIGTERMSIGALRM 等。
__handler

__sighandler_t

void (*)(int)

信号处理函数指针,用于处理捕获的信号。该函数应接受一个 int 类型的参数,通常是信号编号。三种处理方式如下:

处理方式

含义

SIG_DFL

对该信号采取 默认的处理方式

SIG_IGN

忽略 该信号(不做任何反应)

自定义函数名(如 sig_handler

指定自定义的信号处理函数(函数声明格式如下)

⚠️ 信号处理函数声明格式:

void sigX_handler(int arg);

当捕获到信号时,系统会自动调用此函数,并将信号编号作为参数传入。

④ 返回值

返回值 返回值类型 返回值功能
旧处理函数 __sighandler_t 返回之前指定的信号处理函数指针。如果设置失败,返回 SIG_ERR

⑤ 函数功能

signal() 函数用于为指定的信号设置一个信号处理函数,该函数会在信号发生时被调用。该函数提供了一个简单的机制来捕获并处理特定的信号。信号处理函数是由程序员提供的,用于定义接收到信号时应该执行的行为。

  • signal() 的第一个参数是信号编号,指定要捕获的信号。

  • 第二个参数是信号处理函数的指针。当该信号发生时,操作系统会暂停当前进程的执行,并调用该信号处理函数。

  • 处理信号的函数通常会接受一个整数类型的参数,该参数是信号的编号。

signal() 的返回值是先前设置的信号处理函数,可以通过返回的函数指针来恢复或替换信号处理程序。如果设置信号处理程序失败,signal() 返回 SIG_ERR

常见信号处理方法:

  • SIG_IGN:忽略信号。

  • SIG_DFL:恢复默认行为(如进程终止、核心转储等)。

  • 自定义函数 → 调用该处理函数。

⑥ 示例代码

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

int count = 0;
void signal_handler(int sig) {
    count++;
    printf("\ncount :%d\n", count);
    fflush(stdout);
}
int main() {
    signal(SIGINT, signal_handler);

    while (1) {
        printf("程序运行中\n");
        printf("count = 5 时程序退出\n");
        sleep(1);
        if(count == 5) {
            printf("count = 5 程序退出\n");
            exit(2);
        }
    }
}

程序运行过程:

  1. 程序启动,进入无限循环。

  2. 每秒打印 "程序运行中""count = 5 时程序退出"

  3. 每次收到 Ctrl + CSIGINT 信号)时,count 会增加并打印出当前值。

  4. 一旦 count 达到 5,程序会打印 "count = 5 程序退出",然后退出。

3  综合示例

3.1  综合案例1

0~5s发送系统信号2 忽略  5~10秒发送信号2 执行处理函数的内容  10秒后执行信号默认功能

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

void SIGINT_Handler(int sig) {
    printf("捕获到信号 %d SIGINT\n", sig);
}

int main() {
    int second = 1;
    //0 ~ 5s 忽略 SIGINT 信号
    signal(SIGINT, SIG_IGN);

    printf("0~5s 忽略系统信号2 (ctrl+c)\n");
    while(second <= 5) {
        printf("%d s\n", second++);
        sleep(1);
    }

    //5 ~ 10s 发送信号2 执行处理函数的内容
    signal(SIGINT, SIGINT_Handler);

    while(second > 5 && second <= 10) {
        printf("%d s\n", second++);
        sleep(1);
    }

    //10 ~ 15s 执行系统默认的功能
    signal(SIGINT, SIG_DFL);
    while(second > 10) {
        printf("%d s\n", second++);
        sleep(1);
    }

    return 0;
}

3.2  综合案例2

可以调用对应的信号处理函数,可以触发多个信号,例如:2信号 3信号 4信号 调用同一个信号处理函数。

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

// 通用信号处理函数
void signal_handler(int signo)
{
    switch (signo)
    {
        case SIGINT:   
            printf("捕获到信号 2 (SIGINT):键盘中断信号 Ctrl+C\n");
            break;
        case SIGQUIT:  
            printf("捕获到信号 3 (SIGQUIT):键盘退出信号 Ctrl+\\\n");
            break;
        case SIGILL:  
            printf("捕获到信号 4 (SIGILL):非法指令信号\n");
            break;
        default:
            printf("捕获到其他信号:%d\n", signo);
            break;
    }
}

int main(void)
{
    printf("综合案例2:多个信号共用同一个处理函数\n");
    printf("请尝试按 Ctrl+C、Ctrl+\\ 或使用 kill 命令发送信号\n");

    // 注册信号(2、3、4 都调用同一个处理函数)
    signal(SIGINT,  signal_handler);   // 信号 2
    signal(SIGQUIT, signal_handler);   // 信号 3
    signal(SIGILL,  signal_handler);   // 信号 4

    // 主循环
    while (1)
    {
        printf("程序正在运行中... PID = %d\n", getpid());
        sleep(3);
    }

    return 0;
}

触发信号的方式

信号编号 信号名称 触发方式
2 SIGINT 终端按 Ctrl+C
3 SIGQUIT 终端按 Ctrl+\
4 SIGILL kill -4 <pid> 命令发送(模拟非法指令)

现象:

3.3  综合案例3

每隔5秒钟,输出一下当前的时间。

思路:需要使用定时器alarm + signal对应的信号处理函数

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

// 通用信号处理函数
void signal_handler(int signo)
{
    // 每次触发时输出系统时间
    system("date");

    // 再次启动定时器,实现周期触发
    alarm(5);
}

int main(void)

{
    printf("程序PID:%d\n", getpid());
    int i=1;

    // 注册SIGALRM信号
    signal(SIGALRM, signal_handler);

    // 启动第一次定时
    alarm(5);

    // 主进程持续运行,等待信号触发
    while (1)
    {
        printf("%ds\r\n",i++);
        sleep(1);
    }
    return 0;

}

现象:

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐