从 CPU 时间说起...下面这个是 top
命令的界面,相信大家应该都不陌生 。
top - 19:01:38 up 91 days, 23:06,1 user,load average: 0.00, 0.01, 0.05Tasks: 151 total,1 running, 149 sleeping,1 stopped,0 zombie%Cpu(s):0.0 us,0.1 sy,0.0 ni, 99.8 id,0.0 wa,0.0 hi,0.0 si,0.0 stKiB Mem :8010420 total,5803596 free,341300 used,1865524 buff/cacheKiB Swap:0 total,0 free,0 used.6954384 avail MemPID USERPRNIVIRTRESSHR S%CPU %MEMTIME+ COMMAND13436 root200 1382776280405728 S0.30.4 251:21.06 n9e-collector1 root2004318433842212 S0.00.05:15.64 systemd2 root200000 S0.00.00:00.28 kthreadd3 root200000 S0.00.00:00.58 ksoftirqd/05 root0 -20000 S0.00.00:00.00 kworker/0:0H7 rootrt0000 S0.00.00:35.48 migration/0
%Cpu(s):
这一行表示的是 CPU 不同时间的占比,其中大家比较熟悉的应该是 system time
与 user time
:
- 正常情况下
user time
占比应该最高,这是进程运行应用代码的的时间占比(CPU 密集) - 而
system time
占用率高,则意味着存在频繁的系统调用(IO 密集)或者一些潜在的性能问题
文章插图
接下来我们将探究隐藏在这些时间背后的操作原理 。
内核态与用户态操作系统的核心功能就是管理硬件资源,因此不可避免会使用到一些直接操作硬件的CPU指令,这类指令我们称之为特权指令 。特权指令如果使用不当,将会导致整个系统的崩溃,因此操作系统提供了一组特殊的资源访问代码 —— 内核
kernel
来负责执行这些指令 。操作系统将虚拟地址空间划分为两部分:
- 内核空间
kernel memotry
:存放内核代码和数据(进程间共享) - 用户空间
user memotry
:存放用户程序的代码和数据(相互隔离)
文章插图
通过区分内核空间和用户空间的设计,隔离了操作系统代码与应用程序代码 。即便是单个应用程序出现错误也不会影响到操作系统的稳定性,这样其它的程序还可以正常的运行 。
应用程序通过内核提供的接口,访问 CPU、内存、I/O 等硬件资源,我们将该过程称为系统调用
system call
。系统调用是操作系统的最小功能单位 。每个进程处于活动状态时,可能处于以下两种状态之一:
- 执行用户空间的代码时,处于用户态
- 执行内核空间的代码时(系统调用),处于内核态
- CPU 保存用户态指令,切换为内核态
- 在内核态下访问系统资源
- CPU 恢复用户态指令,切换回用户态
user time
与 system time
分别就是对应 CPU 在用户态与内核态的运行时间 。上下文切换当发生以下状况时,线程会被挂起,并由系统调度其他线程运行:
- 等待系统资源分配
- 调用
sleep
主动挂起 - 被优先级更高的线程抢占
- 发生硬件中断,跳转执行内核的中断服务程序
- CPU 保存线程 A 用户态指令,切换为内核态
- 保存线程 A 私有资源(栈、寄存器...)/li>
- 加载线程 B 私有资源(栈、寄存器...)
- CPU 恢复线程 B 用户态指令,切换回用户态
经验总结扩展阅读
- 工厂方法在Spring源码中的运用
- 啤酒度数怎么看
- OnionArch - 如何实现更新指定字段的通用Handler
- 关于三伏天出伏的诗有哪些 三伏天宜静还是宜动
- windows C++ 异常调用栈简析
- 宋嫂鱼羹的来历
- .NET平台下一个你不知道的框架,我只想说两个字:“牛逼”
- 胡辣汤是河南的吗
- 四伏天是几月 伏天晒背的最佳时间
- 不属于夏季节气的是