信号(Signals)是操作系统中用于通知进程发生特定事件的一种机制。信号可以由软件或硬件触发,并且可以被进程捕获和处理。以下是信号的相关概念、常见信号列表、信号处理以及相关API的汇总整理。
信号概述
信号是操作系统向进程发出的通知,用于响应各种事件,如用户输入、定时器到期、异常情况等。信号可以被忽略、捕获或默认处理。
常见信号列表
以下是一些常见的信号及其意义:
- SIGINT (2):终端中断信号,通常由Ctrl+C触发。
- SIGTERM (15):终止信号,通常由
kill
命令发送。 - SIGKILL (9):强制终止信号,无法被捕捉或忽略。
- SIGQUIT (3):退出信号,通常由Ctrl+\触发,产生核心转储文件。
- SIGABRT (6):程序异常终止信号,通常由
abort()
函数触发。 - SIGSEGV (11):段错误信号,通常由无效内存访问触发。
- SIGCHLD (17):子进程结束信号,用于通知父进程子进程已经结束。
- SIGPIPE (13):写入断开的管道或套接字信号。
- SIGALRM (14):定时器信号,由
alarm()
函数触发。 - SIGUSR1 (10) 和 SIGUSR2 (12):用户定义信号,通常用于进程间通信。
- SIGSTOP (17):停止信号,无法被捕捉或忽略。
- SIGCONT (18):继续信号,用于恢复被停止的进程。
- SIGTSTP (20):终端停止信号,通常由Ctrl+Z触发。
- SIGTTIN (21) 和 SIGTTOU (22):后台进程试图读取或写入终端信号。
信号处理
信号处理包括三种主要方式:
- 默认动作:操作系统默认的动作,如终止进程。
- 忽略:进程忽略该信号。
- 捕获:进程捕获信号,并通过信号处理函数进行处理。
信号处理API
定义信号处理函数
- signal():
sighandler_t signal(int signum, sighandler_t handler)
: 设置信号处理函数。- 参数
signum
指定信号编号,handler
指定信号处理函数。
捕获信号
- sigaction():
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
: 设置信号处理函数和信号行为。- 参数
signum
指定信号编号,act
指定新的信号处理行为,oldact
用于返回旧的行为。
忽略信号
- sigignore():
void sigignore(int signum)
: 忽略指定的信号。
检查信号集
- sigemptyset():
void sigemptyset(sigset_t *set)
: 清空信号集。
- sigfillset():
void sigfillset(sigset_t *set)
: 将信号集设置为包含所有信号。
- sigaddset():
int sigaddset(sigset_t *set, int signo)
: 添加信号到信号集。
- sigdelset():
int sigdelset(sigset_t *set, int signo)
: 从信号集中删除信号。
- sigismember():
int sigismember(const sigset_t *set, int signo)
: 检查信号是否在信号集中。
等待信号
- sigwait():
int sigwait(const sigset_t *set, int *sig)
: 等待信号集中的任意信号。
- sigwaitinfo():
int sigwaitinfo(const sigset_t *set, siginfo_t *info)
: 等待信号集中的任意信号,并返回信号信息。
- sigtimedwait():
siginfo_t *sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeout)
: 在指定时间内等待信号集中的任意信号。
发送信号
- kill():
int kill(pid_t pid, int sig)
: 发送信号给指定的进程。
- raise():
int raise(int sig)
: 发送信号给当前进程。
- syscall():
int syscall(int number, ...)
: 用于调用系统调用,可以用来发送信号。
示例代码
以下是一个简单的示例,展示如何在 Linux 系统上使用 C 语言捕获SIGINT
信号,并打印出相应的信息:
1#include <stdio.h>
2#include <signal.h>
3#include <unistd.h>
4
5void sigint_handler(int sig) {
6 printf("捕获到 SIGINT 信号\n");
7}
8
9int main() {
10 // 设置信号处理函数
11 signal(SIGINT, sigint_handler);
12
13 // 无限循环,等待信号
14 while (1) {
15 sleep(1); // 休眠一秒,以便捕获信号
16 }
17
18 return 0;
19}
注意事项
- 信号处理函数不能执行耗时的操作,因为这会导致进程长时间处于不可中断的状态。
- 信号处理函数不能直接调用可能导致阻塞的函数,如
read()
、write()
等,而应该使用sigprocmask()
来管理信号屏蔽。 - 信号处理函数应当尽可能简短,避免使用全局变量以减少竞态条件的风险。
- 信号处理函数必须是异步安全的。
- 有些信号是不可捕获或不可忽略的,如
SIGKILL
和SIGSTOP
。
信号是操作系统中一种非常强大的机制,用于处理各种异常和事件。理解信号及其处理方式对于编写健壮的多进程或多线程程序非常重要。