这里写目录标题
-
-
- 核心主题:中断处理流程
-
- [1. 硬件触发阶段](#1. 硬件触发阶段)
- [2. 信号传递与响应](#2. 信号传递与响应)
- [3. CPU 保护现场(关键步骤!)](#3. CPU 保护现场(关键步骤!))
- [4. 执行中断服务程序](#4. 执行中断服务程序)
- [5. 恢复现场与继续](#5. 恢复现场与继续)
- [💡 重点概念解析](#💡 重点概念解析)
- 核心主题:从硬件中断到软件信号
-
- [1. 硬件中断回顾](#1. 硬件中断回顾)
- [2. 信号的诞生:为什么要发明它?](#2. 信号的诞生:为什么要发明它?)
- [3. 什么是信号?](#3. 什么是信号?)
- [4. 关键对比:信号 vs 硬件中断](#4. 关键对比:信号 vs 硬件中断)
- 结论
-
- [📝 操作系统进程调度:从硬件中断到多任务并发](#📝 操作系统进程调度:从硬件中断到多任务并发)
-
- [💡 核心破局:OS 也是软件,谁来运行它?](#💡 核心破局:OS 也是软件,谁来运行它?)
- [❓ 四大关键问题解析](#❓ 四大关键问题解析)
- [⚙️ 进程调度的完整逻辑链](#⚙️ 进程调度的完整逻辑链)
- [🎯 总结:操作系统的"调度权"](#🎯 总结:操作系统的“调度权”)
-
- 操作系统核心:从软中断到系统调用 (0x80)
-
- [1. 核心观点:操作系统的本质](#1. 核心观点:操作系统的本质)
- [2. 三大中断机制辨析](#2. 三大中断机制辨析)
-
- [🔴 硬件中断](#🔴 硬件中断)
- [🟡 异常](#🟡 异常)
- [🟢 陷阱 ------ 重点!](#🟢 陷阱 —— 重点!)
- [3. 系统调用的实现原理 (以 Linux 0x80 为例)](#3. 系统调用的实现原理 (以 Linux 0x80 为例))
- [4. 总结图谱](#4. 总结图谱)
- [虚拟地址空间:用户态 vs 内核态](#虚拟地址空间:用户态 vs 内核态)
-
-
- 一、核心概念:进程页表的"同与不同"
- 二、"内核级页表相同"的本质
- 三、内核级页表的"不同点"(进程隔离的关键)
- 四、用户态与内核态的访问限制
- 五、物理内存的"页划分"逻辑
- 六、总结:"同"与"不同"的核心作用
- [`sigaction` 函数笔记](#
sigaction函数笔记) -
- [1. 函数功能](#1. 函数功能)
- [2. 函数原型](#2. 函数原型)
- [3. 参数详解](#3. 参数详解)
- [4. 核心结构体 `struct sigaction` 与 `sa_mask`](#4. 核心结构体
struct sigaction与sa_mask)
-


运算器和控制器加起来,叫做cpu,又叫做中央处理器。其中,CPU的"底层物理规则"是出厂时被写死的,它只能处理这些特定的基础电信号。储存器,主要包含了内存条和硬盘,内存条储存着是临时数据,硬盘储存着是永久数据,这两个本身是硬件,里面储存着的内容叫做软件,里面储存着点内容可以记录一系列动作,然后这些动作通过加载,从储存器加载到cpu,然后cpu识别之后,就可以通过进程为载体,执行一系列动作。

好,在看这个图,我们上篇说到,如果写个scanf,让os轮询访问,会影响效率,所以建安会自己中断,当有数据写入时,硬盘(或外设)会向中断控制器发出电信号,这样 CPU 就知道了。随后,CPU 会去内存中查找由操作系统提前写好的'中断向量表',找到对应的处理程序地址,然后跳转过去执行具体的命令
这张图非常清晰地展示了**中断处理(Interrupt Handling)**的完整生命周期,这是理解操作系统如何管理硬件和CPU的核心机制。
核心主题:中断处理流程
这张图描述了当一个外部设备需要CPU关注时,从发起请求到处理完毕并恢复原状的全过程。
1. 硬件触发阶段
- 外设就绪:某个外部设备(如键盘、鼠标、网卡等)完成了它的工作或有了新数据(图中"外部设备1"高亮)。
- 发起中断 :该设备向中断控制器发送一个电信号,请求CPU暂停手头的工作来处理它。
2. 信号传递与响应
- 通知CPU:中断控制器汇总信号后,向CPU发送中断请求。
- 获取中断号 :CPU接收到信号,得知发生了中断,并从控制器那里获取一个中断号(用于区分是哪个设备发出的请求)。
3. CPU 保护现场(关键步骤!)
- 暂停当前工作:CPU必须立刻停下正在执行的"当前进程"的代码。
- 保存寄存器状态 :为了防止回来后忘记刚才算到哪了,CPU会将当前所有寄存器 中的数值(即"硬件上下文")保存起来。这就是图中红框标注的 "5. CPU保护现场"。
4. 执行中断服务程序
- 查表定位 :CPU拿着刚才拿到的"中断号",去查询内存中的中断向量表。
- 跳转执行 :根据向量表的指引,CPU跳转到对应的中断服务程序(例如:如果是键盘中断,就去执行"处理键盘"的代码;如果是硬盘中断,就去执行"处理磁盘"的代码)。这些代码通常属于操作系统内核的一部分。
5. 恢复现场与继续
- 执行完毕:中断服务程序处理完设备的请求后,发出结束信号。
- 恢复现场 :CPU将之前保存的寄存器数值重新加载回寄存器中(图中红框标注的 "7. ...恢复现场")。
- 继续工作:CPU回到刚才被打断的地方,继续执行"当前进程"的代码,仿佛什么都没发生过一样。
💡 重点概念解析
- 中断控制器:相当于一个"前台接待员"。因为外部设备很多,CPU不可能直接连每一根线,所以由它来统一接收请求,排队并告诉CPU谁在呼叫。
- 中断向量表:相当于一个"通讯录"或"索引目录"。它存储在内存中,记录了每一个中断号对应的处理程序在内存中的具体地址。
- 保护现场/恢复现场:这是实现多任务并发和执行中断的基础。没有这一步,一旦被打断,原来的程序就会崩溃或数据出错。
核心主题:从硬件中断到软件信号
1. 硬件中断回顾
- 定义:暂停 CPU 正在执行的任务,去处理硬件发生的突发事件(如键盘输入、网卡数据到达)。
- 机制 :利用中断号 查询中断向量表来定位处理程序。
- 归属 :这是由操作系统内核直接提供的底层能力。
2. 信号的诞生:为什么要发明它?
- 背景:计算机世界里已经有了硬件中断来处理物理设备的问题。
- 痛点 :后来发现,进程之间 也需要一种类似的机制来互相通知或处理异常(比如用户按
Ctrl+C想杀掉一个死循环程序,或者父进程想告诉子进程"你该退出了")。 - 解决方案 :为了不让每个进程都去写复杂的硬件交互代码,操作系统发明了信号机制。
3. 什么是信号?
- 本质 :信号是一种纯软件的方式。
- 功能:它模拟了中断的行为,用来完成特定的任务处理(如终止进程、暂停进程、忽略事件等)。
- 比喻:如果说硬件中断是有人用力拍了一下你的肩膀(物理强制),那么信号就像是给你发了一条微信消息(逻辑通知,你可以选择看或不看,或者看到后决定怎么做)。
4. 关键对比:信号 vs 硬件中断
图中特意强调了这一点(红框部分):
- 相似点:原理类似。都是打断当前流程 -> 查表/查找处理方式 -> 执行特定代码 -> 恢复流程。
- 不同点 :本质完全不同!
- 硬件中断 :发生在内核态,由硬件触发,优先级极高,进程无法屏蔽(除了关中断指令)。
- 信号 :发生在用户态 (虽然由内核投递),由软件触发(其他进程或内核发给进程的),进程可以选择忽略 、捕获 (自定义处理函数)或默认处理。
结论
• 中断向量表就是操作系统的⼀部分,启动就加载到内存中了
• 通过外部硬件中断,操作系统就不需要对外设进行任何周期性的检测或者轮询
• 由外部设备触发的,中断系统运行流程,叫做硬件中断
这是一份结合了底层原理、核心疑问与宏观结论的完整笔记。
📝 操作系统进程调度:从硬件中断到多任务并发
💡 核心破局:OS 也是软件,谁来运行它?
结论: 硬件中断 是唯一的"发令枪"。OS 不会凭空运行,而是依赖硬件定时器 定期发送电信号(时钟中断),强制打断当前进程,将 CPU 控制权夺回给 OS。
OS通过时钟源+时钟中断运行的
❓ 四大关键问题解析
- 什么叫做时间片?
- 定义: 分配给每个进程运行的固定时长配额。
- 代码体现:
task_struct结构体中的long counter字段,即进程的剩余时间计数器。
- 什么叫做时间片耗尽?
- 现象: 当前 PCB(进程控制块)中的
counter计数器被减为 0。 - 后果: 触发调度逻辑,准备切换任务。
- 现象: 当前 PCB(进程控制块)中的
- 为什么 OS 能计算时间?
- 原理: 依靠硬件产生的固定时间间隔的时钟中断(如每 10ms 一次)。
- 当代操作系统已经将这个时钟原放置到cpu内容
- 累计: 内核通过全局变量
ticks累计中断次数,从而感知物理时间的流逝。
- OS 凭什么执行它的调度算法?
- 机制: 通过 IDT(中断描述符表) 建立硬软连接。
- 流程: 硬件中断 → \rightarrow → 查 IDT 找到入口 → \rightarrow → 执行
timer_interrupt→ \rightarrow → 调用do_timer。正是do_timer函数在每次中断时检查并更新counter,一旦耗尽便调用schedule()进行进程切换。
⚙️ 进程调度的完整逻辑链
-
用户程序与 OS 的交互(资源层面)
用户编写代码时,申请空间、打开文件等操作,本质是通过系统调用 请求服务。OS 作为资源管理者,维护进程结构(
task_struct),实现对进程的管控。 -
调度的基础:时钟中断与
do_timer(驱动层面)- 触发: 硬件定时器周期性产生中断(如
set_intr_gate(0x20, &timer_interrupt)),每次中断对应一个时间片(如 10ms)。 - 执行: 中断触发后执行
do_timer(),负责累计系统滴答数(ticks)、更新counter,并判断时间片是否耗尽。
- 触发: 硬件定时器周期性产生中断(如
-
时间片调度与切换(执行层面)
- 机制:
counter限制单次运行时长,保证多进程"轮流"占用 CPU。 - 决策: 当
do_timer检测到counter减为 0,触发调度器(schedule()),选择下一个就绪进程,通过上下文切换实现进程切换。
- 机制:
🎯 总结:操作系统的"调度权"
- 硬件是监工: 硬件时钟中断定期"叫醒"OS。
- OS 是执行者: 通过
do_timer掌握调度权,让进程按时间片轮流运行。 - 本质: 用户程序的运行依赖 OS 支撑,而 OS 的并发能力源于硬件中断驱动的强制性时间片轮转 。
操作系统通过时钟中断和 do_timer 掌握进程调度权,让进程按时间片轮流运行,这是多任务并发的核心机制。
好的,根据你提供的笔记图片,我为你整理了一篇简洁、重点突出的技术博客笔记。这篇笔记将核心概念(软中断、陷阱、异常)与系统调用的具体实现(0x80)串联起来。
操作系统核心:从软中断到系统调用 (0x80)
1. 核心观点:操作系统的本质
操作系统本质上就是一个基于中断处理的软件集合。
如果把 CPU 比作硬件引擎,那么操作系统就是躺在"中断处理例程"上的代码块。没有中断机制,OS 就无法接管硬件或响应程序请求。
2. 三大中断机制辨析
在深入系统调用之前,必须区分三种不同的"打断"CPU 执行流的方式:
🔴 硬件中断
- 来源: 外部硬件设备。
- 例子: 时钟中断、外设中断(键盘、鼠标、网卡)。
- 特点: 异步发生,与当前执行的指令无关。
🟡 异常
- 来源: CPU 内部检测到的错误。
- 例子: 除零错误、野指针访问(缺页异常/段错误)。
- 特点: 被动触发,通常意味着程序出错了。
🟢 陷阱 ------ 重点!
- 来源: CPU 内部的软中断。
- 例子:
int 0x80或syscall指令。 - 特点: 主动触发 。这是用户态程序进入内核态的"大门"。我们常说的系统调用,本质上就是一种特殊的陷阱。
💡 为什么要有软中断?
为了实现系统调用。用户程序无法直接操作硬件,必须通过软中断"陷入"内核,由内核代为执行特权操作。
3. 系统调用的实现原理 (以 Linux 0x80 为例)
当我们在 C 语言中调用 read() 或 fork() 时,底层发生了什么?
步骤一:用户态发起请求
C 标准库函数(如 ssize_t read(int fd, ...))不仅仅是个函数,它内部封装了汇编代码:
- 将参数放入寄存器(如
ebx,ecx)。 - 关键动作: 将系统调用号(例如 3 代表 read)放入
eax寄存器。 - 触发中断: 执行
int 0x80(或者现代一点的syscall指令)。
步骤二:陷入内核 (Trap)
CPU 执行到 int 0x80 后,会立即暂停当前用户程序,保存现场(压栈),并根据中断向量表跳转到内核预设的处理入口。
步骤三:内核分发与执行
内核通过读取 eax 中的值,查表找到对应的内核函数并执行:
c
// 伪代码逻辑
int sysnumber = eax; // 获取系统调用号
sys_call_table[sysnumber](XXXXX); // 查表并执行对应的内核函数
- 系统调用表: 内核维护着一个巨大的数组
sys_call_table[],里面存满了sys_read,sys_write,sys_fork等函数的地址。 - 设置入口: 内核启动时通过
set_system_gate(0x80, &system_call)将 0x80 号中断与系统调用处理程序绑定。
步骤四:返回用户态
内核函数执行完毕后,恢复现场,将结果返回值放入 eax,CPU 切换回用户态继续执行之前的代码。
4. 总结图谱
| 概念 | 触发源 | 典型场景 | 备注 |
|---|---|---|---|
| 硬件中断 | 外部设备 | 键盘输入、时钟滴答 | 异步,不可预测 |
| 异常 | CPU 内部 | 除以零、非法内存访问 | 错误处理,被动 |
| 陷阱 | 程序指令 | int 0x80, syscall |
系统调用的基础,主动 |
一句话总结:
程序员写代码调用库函数 -> 库函数触发 软中断(陷阱) -> CPU 执行 int 0x80 -> 内核查表执行 系统调用 -> 完成任务返回。这就是操作系统控制硬件的核心路径。
"只要产生中断,就会先查表找到对应的内核函数去执行。
- 如果是干活 (IO、系统调用),函数干完活就退出了,不发信号。
- 如果是报错 (异常)或被杀 ,函数会在内部给进程打上信号标记(发信号),等回到用户态时再处理。"
虚拟地址空间:用户态 vs 内核态
一、核心概念:进程页表的"同与不同"
每个进程拥有独立的页表 ,但页表中内核级映射部分高度共享,用户级映射完全私有。
二、"内核级页表相同"的本质
所有进程的页表中,内核虚拟地址对应的页表项(从 PGD 到 PTE)完全一致,指向同一组物理页框。这些"共享的内核虚拟地址"包括:
- 内核自身的代码段、数据段;
- 全局变量;
- 通过
vmalloc动态映射的内核数据结构(如内核模块、大内存分配等)。
→ 内核代码/数据是系统级公共资源,无需为每个进程重复映射,因此所有进程的内核页表项"指向同一物理页"。
三、内核级页表的"不同点"(进程隔离的关键)
内核级页表并非完全相同,差异体现在进程独有的内核数据上:
- 每个进程有专属的内核栈(用于系统调用、中断处理时的内核态执行);
- 进程控制块
task_struct(记录进程状态、资源等信息); - 其他进程私有的内核数据结构(如打开文件表、信号处理结构等)。
这些"进程独有内核数据"在内核虚拟地址空间中划分了不同的地址范围 ,并通过页表映射到不同的物理页框,实现进程间的内核数据隔离。
四、用户态与内核态的访问限制
- 用户态不能直接访问内核态 :CPU 通过特权级(DPL)和页表权限位 双重机制限制。
- 页表项中,内核虚拟地址对应的页标记为"仅内核可访问"(如 x86 的 U/S 位设为 Supervisor),用户态代码若尝试访问会触发页错误异常。
- 用户程序需通过系统调用 (如
int 0x80/syscall)陷入内核态后,才能间接访问内核资源。
五、物理内存的"页划分"逻辑
物理内存被划分为固定大小的物理页(如 4KB),作为内存管理的基本单元:
- 多个进程的虚拟页(无论用户态还是内核态)可映射到同一物理页(如内核代码的物理页被所有进程共享);
- 进程独有的虚拟页(如用户堆栈、进程内核栈)则映射到不同的物理页,确保内存隔离。
六、总结:"同"与"不同"的核心作用
| 维度 | 相同部分 | 不同部分 |
|---|---|---|
| 映射对象 | 内核公共代码/数据 | 进程独有的内核数据(栈、task_struct 等) |
| 物理页关联 | 指向同一组物理页 | 指向各自独立的物理页 |
| 功能意义 | 减少内存冗余,共享内核资源 | 隔离进程内核数据,保障安全 |
通过"内核页表的共享 + 私有化区分",操作系统既实现了内核资源的高效复用 ,又保证了进程间的内存隔离与安全。
sigaction 函数笔记
1. 函数功能
检查并修改信号的处理动作 。它是 Linux/Unix 下最健壮、推荐的信号处理接口(比老旧的 signal() 更安全)。
2. 函数原型
c
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
3. 参数详解
signum(int) :要操作的信号编号 (如SIGINT,SIGSEGV)。注意不能是SIGKILL和SIGSTOP。act(const struct sigaction *) :新动作 。指向一个结构体,定义收到信号后该怎么办(传入NULL表示不修改)。oldact(struct sigaction *) :旧动作 。用于保存修改前的配置,方便以后恢复(传入NULL表示不关心旧值)。
4. 核心结构体 struct sigaction 与 sa_mask
这是控制信号行为的关键结构体:
c
struct sigaction {
void (*sa_handler)(int); // 简单的信号处理函数指针
sigset_t sa_mask; // 【重点】信号屏蔽集
int sa_flags; // 标志位(如 SA_RESTART)
// ... 其他字段
};
关于 sa_mask (信号屏蔽集):
- 是什么?
它是一个信号集合(Set)。定义了当正在执行当前的信号处理函数时,**额外需要被阻塞(屏蔽)**的其他信号。 - 为什么需要它?
防止在处理某个信号的过程中,被同类型或其他信号打断(重入问题),保证处理过程的原子性。 - 工作机制:
- 当信号处理函数开始执行时,内核会自动将
sa_mask中的信号加入进程的屏蔽字。 - 当信号处理函数执行完毕返回时,内核自动恢复原来的屏蔽字。
- 注意: 当前正在处理的这个信号本身(
signum),默认在执行期间也是被阻塞的(除非设置了SA_NODEFER标志)。
- 当信号处理函数开始执行时,内核会自动将