C 语言信号处理:修订间差异
外观
Page creation by admin bot |
Page update by admin bot |
||
第1行: | 第1行: | ||
= 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()`。 | |||
<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 | printf("Received signal %d\n", sig); | ||
} | } | ||
int main() { | int main() { | ||
signal(SIGINT, handler); | 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 | ^CReceived signal 2 | ||
Running... | Running... | ||
</pre> | </pre> | ||
=== | === sigaction() 函数 === | ||
`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> | |||
== 信号集与阻塞 == | |||
信号可以通过`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> | ||
== 实际案例 == | == 实际案例 == | ||
=== | === 守护进程的信号处理 === | ||
守护进程通常需要处理SIGHUP(重新加载配置)和SIGTERM(优雅退出)。 | |||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
void handle_sighup(int sig) { | |||
reload_config(); | |||
} | |||
void | void handle_sigterm(int sig) { | ||
cleanup(); | |||
exit(0); | |||
} | } | ||
int main() { | int main() { | ||
signal( | signal(SIGHUP, handle_sighup); | ||
signal(SIGTERM, handle_sigterm); | |||
daemonize(); | |||
// 主循环 | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
第87行: | 第114行: | ||
=== 多线程信号处理 === | === 多线程信号处理 === | ||
在多线程程序中,信号处理需特别注意: | 在多线程程序中,信号处理需特别注意: | ||
* 使用`pthread_sigmask()`控制线程的信号掩码 | |||
* 通常由一个专用线程处理所有信号 | |||
== 高级主题 == | |||
=== 信号与竞态条件 === | |||
信号处理函数可能中断主程序的执行,导致竞态条件。解决方法: | |||
* 使用`sig_atomic_t`类型声明共享变量 | |||
* 在关键代码段阻塞信号 | |||
=== 实时信号 === | |||
实时信号(SIGRTMIN-SIGRTMAX)支持排队和携带数据: | |||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
union sigval value; | |||
value.sival_int = 42; | |||
sigqueue(pid, SIGRTMIN, value); // 发送带数据的信号 | |||
</syntaxhighlight> | </syntaxhighlight> | ||
== | == 常见问题 == | ||
{| class="wikitable" | |||
|- | |||
! 问题 !! 解决方案 | |||
|- | |||
| 信号处理函数被中断 || 使用`sigaction()`并设置SA_RESTART标志 | |||
|- | |||
| 信号丢失 || 使用实时信号或信号队列 | |||
|- | |||
| 不可靠的信号行为 || 避免在信号处理函数中调用非异步安全函数 | |||
|} | |||
== 总结 == | |||
信号处理是C语言系统编程的核心概念,用于: | |||
* 处理用户中断 | |||
* 实现进程间通信 | |||
* 构建健壮的守护进程 | |||
正确使用信号需要理解其异步特性和潜在陷阱。对于新项目,推荐使用`sigaction()`而非`signal()`,并遵循异步安全编程原则。 | |||
<mermaid> | <mermaid> | ||
graph TD | |||
A[信号产生] --> B{ | A[信号产生] --> B{处理方式} | ||
B --> | B --> C[默认行为] | ||
B --> | B --> D[忽略信号] | ||
B --> | B --> E[自定义处理] | ||
E --> F[ | E --> F[signal] | ||
E --> G[sigaction] | |||
</mermaid> | </mermaid> | ||
<math> | <math> | ||
\text{信号延迟} = \frac{\text{信号处理时间}}{\text{CPU时间片}} | |||
</math> | </math> | ||
[[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()`,并遵循异步安全编程原则。