操作系统中断与信号处理

一、中断机制原理与实践

中断是操作系统实现紧急事件处理和任务调度的关键机制,通过硬件与软件协同工作,确保系统对各类突发情况的及时响应。

1.1 中断分类

中断主要分为硬件中断与软件中断两类。硬件中断由外部硬件设备触发,如键盘、网卡等;软件中断则由程序执行特定指令引发,常见于系统调用场景。

1.2 典型硬件中断流程示例

(1)键盘输入中断处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* 按键动作
// 当检测到按键按下,键盘硬件电路生成电信号
// 键盘控制器将电信号转换为数据并发送中断请求
*/

/* 中断响应
// CPU接收中断信号(假设当前运行任务为Task A)
// 保存Task A的运行上下文(包括程序计数器PC、寄存器状态等)
// 暂停Task A的执行
*/

/* 中断处理
// CPU根据中断向量表找到键盘中断对应的服务程序
// 执行中断服务程序:
// 读取键盘硬件寄存器数据,获取按键扫描码
// 将扫描码转换为字符编码
*/

/* 事件传递
// 中断服务程序将处理后的键盘事件传递给操作系统
// 操作系统根据当前活动窗口,将事件发送给对应应用程序
// 应用程序更新界面显示输入字符
*/

/* 任务恢复
// 操作系统完成事件处理后
// 恢复Task A的运行上下文
// CPU继续执行Task A
*/

(2)网卡数据接收中断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* 数据到达检测
// 网卡接收到网络数据包
// 检查数据包MAC地址是否为本机地址
*/

/* 中断请求发送
// 若数据包目标地址为本机
// 网卡向CPU发送硬件中断请求
*/

/* 中断处理流程
// CPU暂停当前任务,保存上下文
// 执行网卡中断服务程序:
// 从网卡缓冲区读取数据包
// 校验数据包完整性
// 将数据包存储到系统内存
*/

/* 系统处理
// 操作系统解析网络协议头(如IP、TCP)
// 根据协议类型将数据转发给相应网络服务
// 若为HTTP请求,传递给Web服务器程序
*/

1.3 软件中断示例(以文件读取系统调用为例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <unistd.h>
#include <fcntl.h>

int main() {
int fd = open("test.txt", O_RDONLY);
if (fd < 0) {
// 错误处理
return -1;
}

char buffer[1024];
// 执行read系统调用,触发软件中断
ssize_t bytes_read = read(fd, buffer, sizeof(buffer));
if (bytes_read < 0) {
// 读取错误处理
}

close(fd);
return 0;
}

在上述代码中,read函数调用触发软件中断,CPU 切换到内核态执行文件读取操作,完成后将数据返回给用户态程序。

二、信号机制原理与应用

信号作为进程间通信的轻量级方式,在操作系统中承担着事件通知与进程控制的重要功能。

2.1 信号的产生与分类

在 Linux 系统中,信号产生来源广泛,主要包括:

  1. 用户交互信号:如SIGINT(Ctrl+C,中断进程)、SIGQUIT(Ctrl+\,终止进程并生成核心转储)、SIGTSTP(Ctrl+Z,暂停进程)。
  2. 异常相关信号SIGSEGV(非法内存访问)、SIGFPE(算术运算错误)、SIGBUS(硬件故障相关内存访问)。
  3. 进程状态信号SIGCHLD(子进程状态改变)、SIGHUP(终端连接断开)。
  4. 显式控制信号SIGKILL(强制终止进程,不可捕获)、SIGSTOP(暂停进程,不可捕获)。

2.2 信号处理机制

(1)默认处理

进程对未自定义处理的信号采用系统默认行为,例如SIGINT默认终止进程,SIGCHLD默认忽略。

(2)信号忽略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

int main() {
// 忽略SIGINT信号
signal(SIGINT, SIG_IGN);
printf("SIGINT信号已被忽略\n");

while (1) {
sleep(1);
printf("程序持续运行...\n");
}
return 0;
}

上述代码通过signal函数将SIGINT信号设置为忽略状态,此时按下 Ctrl+C 无法终止程序。

(3)自定义信号处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

void signal_handler(int signum) {
printf("接收到信号:%d\n", signum);
// 自定义处理逻辑
}

int main() {
// 注册SIGINT信号处理函数
signal(SIGINT, signal_handler);
printf("等待信号...\n");

while (1) {
sleep(1);
}
return 0;
}

此代码为SIGINT信号绑定自定义处理函数,当接收到该信号时,执行signal_handler函数。

2.3 信号递送与管理

信号在 Linux 系统中的完整处理流程包括:

  1. 信号生成:由事件触发,内核创建对应信号。
  2. 信号传递:内核修改目标进程task_struct中信号相关字段,将信号加入待处理队列。
  3. 信号排队:区分可排队信号(如SIGRTMIN - SIGRTMAX)与不可排队信号,处理重复信号覆盖或丢弃情况。
  4. 信号阻塞与解除:进程通过信号掩码控制信号处理时机,被阻塞信号暂不执行。
  5. 信号处理:进程从内核态返回用户态时,检查并执行未阻塞的待处理信号。

三、中断与信号的协同关系

中断与信号在操作系统中既相互独立又紧密协作。中断侧重于 CPU 对硬件事件的即时响应,实现任务切换与资源调度;信号则专注于进程间异步事件通知与控制。以键盘输入为例,硬件中断完成底层数据采集,信号机制将用户输入事件传递给目标进程进行高层处理,二者共同保障系统的高效运行与用户交互体验。