跳转到内容

C 语言信号处理:修订间差异

来自代码酷
Admin留言 | 贡献
Page creation by admin bot
 
Admin留言 | 贡献
Page update by admin bot
 
第1行: 第1行:
{{DISPLAYTITLE:C语言信号处理}}
= C语言信号处理 =


== 简介 ==
== 介绍 ==
'''信号处理'''是C语言标准库(<code><signal.h></code>)提供的一种进程间通信机制,允许操作系统或用户进程向目标进程发送异步事件通知。信号可以用于处理异常、中断请求或实现简单的进程控制。在UNIX/Linux系统中,信号是系统级事件的基本通知方式。
'''信号(Signal)'''是操作系统用于通知进程发生某种事件的一种机制。在C语言中,信号处理允许程序捕获并响应这些事件,例如用户按下Ctrl+C(SIGINT)或进程尝试访问无效内存(SIGSEGV)。信号处理是系统编程的重要组成部分,尤其在多进程、守护进程或需要优雅退出的程序中。


信号处理的核心功能包括:
信号分为两类:
* 发送信号(如通过<code>kill()</code>系统调用)
* '''标准信号''':如SIGINT(中断)、SIGTERM(终止请求)、SIGKILL(强制终止)等。
* 捕获并自定义信号处理行为
* '''实时信号''':如SIGRTMIN到SIGRTMAX,支持排队和携带数据。
* 忽略或恢复默认信号行为


== 信号类型 ==
== 基本概念 ==
常见标准信号(定义在<code><signal.h></code>中):
=== 信号的产生 ===
{| class="wikitable"
信号可以由以下方式产生:
|+ 标准信号列表(部分)
* 用户操作(如Ctrl+C发送SIGINT)
! 信号名 !! 值 !! 描述
* 内核检测到异常(如SIGSEGV)
|-
* 其他进程通过`kill()`系统调用发送
| <code>SIGINT</code> || 2 || 终端中断(如Ctrl+C)
 
|-
=== 信号的处理方式 ===
| <code>SIGSEGV</code> || 11 || 无效内存访问
1. '''默认行为''':如SIGINT默认终止进程。
|-
2. '''忽略信号''':如`signal(SIGINT, SIG_IGN)`。
| <code>SIGTERM</code> || 15 || 终止请求
3. '''自定义处理函数''':通过`signal()`或`sigaction()`注册处理函数。
|-
 
| <code>SIGALRM</code> || 14 || 定时器超时
== 信号处理函数 ==
|}
=== signal() 函数 ===
`signal()`是传统的信号处理函数,但因其可移植性问题,推荐使用`sigaction()`。


== 基本用法 ==
=== 信号处理函数 ===
使用<code>signal()</code>函数注册信号处理器:
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
#include <stdio.h>
#include <stdio.h>
第33行: 第30行:


void handler(int sig) {
void handler(int sig) {
     printf("Received signal: %d\n", sig);
     printf("Received signal %d\n", sig);
}
}


int main() {
int main() {
     signal(SIGINT, handler); // 注册SIGINT处理器
     signal(SIGINT, handler); // 捕获SIGINT(Ctrl+C)
     while(1) {
     while (1) {
         printf("Running...\n");
         printf("Running...\n");
         sleep(1);
         sleep(1);
第45行: 第42行:
}
}
</syntaxhighlight>
</syntaxhighlight>
'''输出示例'''
 
'''输出示例:'''
<pre>
<pre>
Running...
Running...
Running...
Running...
^CReceived signal: 2 # 用户按下Ctrl+C
^CReceived signal 2
Running...
Running...
...
</pre>
</pre>


=== 高级信号处理 ===
=== sigaction() 函数 ===
更安全的<code>sigaction()</code>函数:
`sigaction()`提供了更精细的控制,是更现代的替代方案。
 
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
struct sigaction sa;
#include <stdio.h>
sa.sa_handler = handler;
#include <signal.h>
sigemptyset(&sa.sa_mask);
#include <unistd.h>
sa.sa_flags = 0;
 
void handler(int sig) {
    printf("Received signal %d\n", sig);
}
 
int main() {
    struct sigaction sa;
    sa.sa_handler = handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
 
    sigaction(SIGINT, &sa, NULL);
    while (1) {
        printf("Running...\n");
        sleep(1);
    }
    return 0;
}
</syntaxhighlight>


sigaction(SIGINT, &sa, NULL);
== 信号集与阻塞 ==
信号可以通过`sigprocmask()`临时阻塞,防止在处理关键代码时被中断。
 
<syntaxhighlight lang="c">
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigprocmask(SIG_BLOCK, &set, NULL); // 阻塞SIGINT
// 关键代码
sigprocmask(SIG_UNBLOCK, &set, NULL); // 解除阻塞
</syntaxhighlight>
</syntaxhighlight>


== 实际案例 ==
== 实际案例 ==
=== 定时器实现 ===
=== 守护进程的信号处理 ===
使用<code>SIGALRM</code>创建简单定时器:
守护进程通常需要处理SIGHUP(重新加载配置)和SIGTERM(优雅退出)。
 
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
#include <signal.h>
void handle_sighup(int sig) {
#include <unistd.h>
    reload_config();
#include <stdio.h>
}


void alarm_handler(int sig) {
void handle_sigterm(int sig) {
     printf("Timer expired!\n");
     cleanup();
    exit(0);
}
}


int main() {
int main() {
     signal(SIGALRM, alarm_handler);
     signal(SIGHUP, handle_sighup);
     alarm(5); // 设置5秒定时器
     signal(SIGTERM, handle_sigterm);
     pause();   // 等待信号
     daemonize();
    return 0;
    // 主循环
}
}
</syntaxhighlight>
</syntaxhighlight>
第87行: 第114行:
=== 多线程信号处理 ===
=== 多线程信号处理 ===
在多线程程序中,信号处理需特别注意:
在多线程程序中,信号处理需特别注意:
* 使用`pthread_sigmask()`控制线程的信号掩码
* 通常由一个专用线程处理所有信号
== 高级主题 ==
=== 信号与竞态条件 ===
信号处理函数可能中断主程序的执行,导致竞态条件。解决方法:
* 使用`sig_atomic_t`类型声明共享变量
* 在关键代码段阻塞信号
=== 实时信号 ===
实时信号(SIGRTMIN-SIGRTMAX)支持排队和携带数据:
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
pthread_sigmask(SIG_BLOCK, &mask, NULL); // 阻塞信号
union sigval value;
// 创建线程...
value.sival_int = 42;
pthread_sigmask(SIG_UNBLOCK, &mask, NULL); // 在主线程处理信号
sigqueue(pid, SIGRTMIN, value); // 发送带数据的信号
</syntaxhighlight>
</syntaxhighlight>


== 信号处理流程 ==
== 常见问题 ==
{| class="wikitable"
|-
! 问题 !! 解决方案
|-
| 信号处理函数被中断 || 使用`sigaction()`并设置SA_RESTART标志
|-
| 信号丢失 || 使用实时信号或信号队列
|-
| 不可靠的信号行为 || 避免在信号处理函数中调用非异步安全函数
|}
 
== 总结 ==
信号处理是C语言系统编程的核心概念,用于:
* 处理用户中断
* 实现进程间通信
* 构建健壮的守护进程
 
正确使用信号需要理解其异步特性和潜在陷阱。对于新项目,推荐使用`sigaction()`而非`signal()`,并遵循异步安全编程原则。
 
<mermaid>
<mermaid>
flowchart TD
graph TD
     A[信号产生] --> B{信号处理方式}
     A[信号产生] --> B{处理方式}
     B -->|默认| C[执行系统默认操作]
     B --> C[默认行为]
     B -->|忽略| D[丢弃信号]
     B --> D[忽略信号]
     B -->|自定义| E[调用用户处理函数]
     B --> E[自定义处理]
     E --> F[处理函数返回后恢复执行]
     E --> F[signal]
    E --> G[sigaction]
</mermaid>
</mermaid>


== 数学基础 ==
信号处理涉及中断概率计算。假设信号到达服从泊松分布:
<math>
<math>
P(k) = \frac{\lambda^k e^{-\lambda}}{k!}
\text{信号延迟} = \frac{\text{信号处理时间}}{\text{CPU时间片}}
</math>
</math>
其中:
* <math>\lambda</math>为单位时间平均信号数
* <math>k</math>为实际到达信号数
== 注意事项 ==
1. 信号处理函数应是可重入的(避免使用非异步安全函数如<code>printf()</code>)
2. 某些信号(如<code>SIGKILL</code>)不能被捕获或忽略
3. 信号可能中断系统调用(需检查<code>errno == EINTR</code>)
== 扩展阅读 ==
* UNIX信号机制发展历史
* 实时信号(<code>SIGRTMIN</code>到<code>SIGRTMAX</code>)
* 信号与进程状态的关系
[[Category:C语言标准库]]
[[Category:系统编程]]


[[Category:编程语言]]
[[Category:编程语言]]
[[Category:C]]
[[Category:C]]
[[Category:C 语言标准库]]
[[Category:C 语言系统编程]]

2025年4月29日 (二) 04:48的最新版本

C语言信号处理[编辑 | 编辑源代码]

介绍[编辑 | 编辑源代码]

信号(Signal)是操作系统用于通知进程发生某种事件的一种机制。在C语言中,信号处理允许程序捕获并响应这些事件,例如用户按下Ctrl+C(SIGINT)或进程尝试访问无效内存(SIGSEGV)。信号处理是系统编程的重要组成部分,尤其在多进程、守护进程或需要优雅退出的程序中。

信号分为两类:

  • 标准信号:如SIGINT(中断)、SIGTERM(终止请求)、SIGKILL(强制终止)等。
  • 实时信号:如SIGRTMIN到SIGRTMAX,支持排队和携带数据。

基本概念[编辑 | 编辑源代码]

信号的产生[编辑 | 编辑源代码]

信号可以由以下方式产生:

  • 用户操作(如Ctrl+C发送SIGINT)
  • 内核检测到异常(如SIGSEGV)
  • 其他进程通过`kill()`系统调用发送

信号的处理方式[编辑 | 编辑源代码]

1. 默认行为:如SIGINT默认终止进程。 2. 忽略信号:如`signal(SIGINT, SIG_IGN)`。 3. 自定义处理函数:通过`signal()`或`sigaction()`注册处理函数。

信号处理函数[编辑 | 编辑源代码]

signal() 函数[编辑 | 编辑源代码]

`signal()`是传统的信号处理函数,但因其可移植性问题,推荐使用`sigaction()`。

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

void handler(int sig) {
    printf("Received signal %d\n", sig);
}

int main() {
    signal(SIGINT, handler); // 捕获SIGINT(Ctrl+C)
    while (1) {
        printf("Running...\n");
        sleep(1);
    }
    return 0;
}

输出示例:

Running...
Running...
^CReceived signal 2
Running...

sigaction() 函数[编辑 | 编辑源代码]

`sigaction()`提供了更精细的控制,是更现代的替代方案。

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

void handler(int sig) {
    printf("Received signal %d\n", sig);
}

int main() {
    struct sigaction sa;
    sa.sa_handler = handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;

    sigaction(SIGINT, &sa, NULL);
    while (1) {
        printf("Running...\n");
        sleep(1);
    }
    return 0;
}

信号集与阻塞[编辑 | 编辑源代码]

信号可以通过`sigprocmask()`临时阻塞,防止在处理关键代码时被中断。

sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigprocmask(SIG_BLOCK, &set, NULL); // 阻塞SIGINT
// 关键代码
sigprocmask(SIG_UNBLOCK, &set, NULL); // 解除阻塞

实际案例[编辑 | 编辑源代码]

守护进程的信号处理[编辑 | 编辑源代码]

守护进程通常需要处理SIGHUP(重新加载配置)和SIGTERM(优雅退出)。

void handle_sighup(int sig) {
    reload_config();
}

void handle_sigterm(int sig) {
    cleanup();
    exit(0);
}

int main() {
    signal(SIGHUP, handle_sighup);
    signal(SIGTERM, handle_sigterm);
    daemonize();
    // 主循环
}

多线程信号处理[编辑 | 编辑源代码]

在多线程程序中,信号处理需特别注意:

  • 使用`pthread_sigmask()`控制线程的信号掩码
  • 通常由一个专用线程处理所有信号

高级主题[编辑 | 编辑源代码]

信号与竞态条件[编辑 | 编辑源代码]

信号处理函数可能中断主程序的执行,导致竞态条件。解决方法:

  • 使用`sig_atomic_t`类型声明共享变量
  • 在关键代码段阻塞信号

实时信号[编辑 | 编辑源代码]

实时信号(SIGRTMIN-SIGRTMAX)支持排队和携带数据:

union sigval value;
value.sival_int = 42;
sigqueue(pid, SIGRTMIN, value); // 发送带数据的信号

常见问题[编辑 | 编辑源代码]

问题 解决方案
信号处理函数被中断 使用`sigaction()`并设置SA_RESTART标志
信号丢失 使用实时信号或信号队列
不可靠的信号行为 避免在信号处理函数中调用非异步安全函数

总结[编辑 | 编辑源代码]

信号处理是C语言系统编程的核心概念,用于:

  • 处理用户中断
  • 实现进程间通信
  • 构建健壮的守护进程

正确使用信号需要理解其异步特性和潜在陷阱。对于新项目,推荐使用`sigaction()`而非`signal()`,并遵循异步安全编程原则。

graph TD A[信号产生] --> B{处理方式} B --> C[默认行为] B --> D[忽略信号] B --> E[自定义处理] E --> F[signal] E --> G[sigaction]

信号延迟=信号处理时间CPU时间片