什么是系统调用
什么是操作系统
操作系统是资源的管理器,其管理的资源均进⾏了抽象。
磁盘抽象:⽂件夹
内存抽象:虚拟内存
CPU 抽象:时间⽚
分级保护域-protection ring
CPU 为操作系统提供了特殊的安全⽀持
操作系统内核运⾏在特殊模式下,即图中的 ring-0
应⽤运⾏在 ring-3,权限被严格限制
Intel 64 有四个特权级别,不过实际上只⽤到了其中的两个:ring-0 和 ring-3
ring-1 ring-2本来计划是为驱动程序和 OS 服务⽤,不过流⾏的 OS 们都没有接受这个⽅案。
什么是系统调⽤
系统调⽤是操作系统内核为应⽤提供的 API
是内核为应⽤提供的服务
操作系统为上层的应⽤程序提供了⼀个“标准库”
对于应⽤来说,系统调⽤可以实现超出⾃⼰能⼒以外的事情
回忆⼀下之前讲的调⽤规约,Go 语⾔调⽤规约中未使⽤寄存器
寄存器:CPU内部的特殊存储单元:
系统调⽤有⾃⼰的⼀套调⽤规约,需要使⽤寄存器和C
语⾔的调⽤规约相似
系统调⽤举例:
SYSCALL 之后发⽣了什么
怎么样找到内核⾥的代码
应⽤层的开发同学不需要继续向下研究了,基础设施同学可以看看内核的内部实现。
常见的系统调用
观察系统调用
strace on linux
dtruss on macOS
看看 nginx 这种软件阻塞的时候到底是阻塞在哪⾥:
docker 中使⽤ strace 时,要 https://jvns.ca/blog/2020/04/29/why-strace-doesnt-work-in-docker/
查看⼀个 Go 语⾔的 hello world 程序⽣命周期内的系统调⽤统计:
strace 的实现依赖了 ptrace 这个 syscall
调试器(如 delve)也是⼤量使⽤了 ptrace
Go语言中的系统调用
阻塞和⾮阻塞的系统调⽤
Syscall 相关代码的基本结构
OS 相关的基础⽂件,在 syscall package 中:
https://golang.org/src/syscall/syscall_linux.go
使⽤脚本⽣成的⽂件,在 syscall package 中:
https://golang.org/src/syscall/zsyscall_linux_386.go
不对⽤户暴露的特殊 syscall,不受调度影响,在 runtime 中:
https://golang.org/src/runtime/sys_linux_amd64.s
阻塞的系统调⽤需要修改 P 的状态:running -> syscall。这样在 sysmon 中才能发现这个 P 已经在 syscall 状态阻塞了
VDSO 优化
内核负责,⾃动映射值到⽤户地址空间,⽆需⽤户/内核态切换
系统调用发展历史
学习系统调用
这些知识到底有什么⽤?
- 了解操作系统与应⽤程序的边界
- 了解内核升级导致应⽤程序⾏为变化的原因,如:madvdontneed 修改时,导致线上应⽤ RSS ⼤幅上升
- 系统⽆响应时,观察系统是卡死在什么地⽅(也许根本没卡死,只是你的错觉)
References
Ring 0 和 Ring 3:
https://zh.wikipedia.org/wiki/%E5%88%86%E7%BA%A7%E4%BF%9D%E6%8A%A4%E5%9F%9F
https://stackoverflow.com/questions/18717016/what-are-ring-0-and-ring-3-in-the-context-of-operating-systems
https://www.futurelearn.com/info/courses/computer-systems/0/steps/53514
硬件中断、软件中断
Int 80 和 syscall 有什么区别
为什么 sysenter/syscall ⽐ int 80 开销⼩
这⼀篇把内核中的代码返回位置都说清楚了,不需要⾃⼰去翻代码了
Strace 在 docker ⾥做实验时,可能要做⼀点配置
Anatomy of a system cal:
https://lwn.net/Articles/604287/
https://lwn.net/Articles/604515/
VDSO
time.Now 的性能衰退,其中有讲到 vdso
vdso 和普通 syscall 的性能对⽐
源码分析⽐赛的⼀点提示
- spec 可以搜到类型的所有⽤法
- 编译/反编译⼯具可以找到语法的内部实现
- 调试⼯具/IDE 可以跟踪具体流程