内核态与用户态

驱动跑在哪、特权级、系统调用怎么陷入内核

商用

◎学完你会

1两个世界:用户态 vs 内核态

CPU 把你的程序分在两层跑:

态特权级能做什么典型代码
用户态ring3(最低)只碰自己进程的内存、普通指令你的 main()、STL、cout
内核态ring0(最高)读写寄存器/端口/设备内存,管全部进程系统调用处理、设备驱动
一句话:你的程序是"房客",内核是"物业"——房客不能自己拆墙改水管,得通过物业。驱动就是物业派来管特定设备(硬盘/网卡/串口)的员工,它运行在 ring0。

2动手:一次系统调用怎么陷入内核

按顺序点按钮,看 read() 从用户态出发、陷入内核、再由驱动取数据、最后回来的完整旅程。右侧特权级指示灯会跟着变。

int n = read(fd, buf, 1024); // ① 用户态:你想读文件 // ② 库把调用打包,执行一条 syscall 指令 // ③ CPU 陷入内核态:去内核的 sys_read 处理 // ④ 驱动从硬盘搬数据到你的 buf(内核态) return; // ⑤ sysret:切回用户态,把结果还给你
用户态 ring3
内核态 ring0
点左边按钮开始。

3陷入内核的固定三步

步谁在做发生什么
1 发请求你的程序(用户态)调用 read(),C 库打包成 syscall
2 切特权级CPU从 ring3 切到 ring0,跳到内核入口
3 处理+返回内核 → 驱动内核调驱动读写设备,最后 sysret 回用户态
最大的坑:以为"驱动 = 自己写个类就能碰硬件"。驱动必须在内核态、由内核框架(file_operations 回调)调度,不是你想调就调;用户态想碰设备只能走系统调用。

?跨学科:一次"过安检"要花多久

系统调用 = 一次"用户态↔内核态"的过安检:要切特权级、查表、保存现场。量一下:普通函数调用约 1 ns,一次系统调用约 100~1000 ns(含上下文切换)——慢了 2~3 个数量级。

这就是为什么"每次循环里都 read() 一次"比"攒够 4 KB 一次 read()"慢几十上百倍——不是驱动慢,是来回安检的次数在花钱。类比:1 ns ≈ 光在空气里走 30 cm,1000 ns ≈ 光走 300 米——同一个量级的差距,肉眼根本看不见,但循环十万次就天差地别。

✎练一练

下面哪个代码运行在内核态?
驱动直接管设备,必须运行在内核态;你的循环、cout、vector 都是用户态。
用户程序调用 read() 时,CPU 发生了什么?
每次系统调用都要执行 syscall 指令、切特权级到 ring0,让内核去碰设备,再 sysret 回来。
关于系统调用开销,哪句对?
含特权级切换,约 100~1000 ns,比普通调用慢 2~3 个数量级——所以要把小读写攒成批量一次系统调用。