进程/线程上下文切换会用掉你多少CPU?( 二 )


线程上下文切换耗时
前面我们测试了进程上下文切换的开销,我们再继续在Linux测试一下线程 。看看究竟比进程能不能快一些,快的话能快多少 。
在Linux下其实本并没有线程,只是为了迎合开发者口味,搞了个轻量级进程出来就叫做了线程 。轻量级进程和进程一样,都有自己独立的task_struct进程描述符,也都有自己独立的pid 。从操作系统视角看,调度上和进程没有什么区别,都是在等待队列的双向链表里选择一个task_struct切到运行态而已 。只不过轻量级进程和普通进程的区别是可以共享同一内存地址空间、代码段、全局变量、同一打开文件集合而已 。

同一进程下的线程之所有getpid()看到的pid是一样的,其实task_struct里还有一个tgid字段 。对于多线程程序来说,getpid()系统调用获取的实际上是这个tgid,因此隶属同一进程的多线程看起来PID相同 。
【进程/线程上下文切换会用掉你多少CPU?】我们用一个实验来测试一下test06 。其原理和进程测试差不多,创建了20个线程,在线程之间通过管道来传递信号 。接到信号就唤醒,然后再传递信号给下一个线程,自己睡眠 。这个实验里单独考虑了给管道传递信号的额外开销,并在第一步就统计了出来 。
# gcc -lpthread main.c -o main0.508250 4.363495 每次实验结果会有一些差异,上面的结果是取了多次的结果之后然后平均的,大约每次线程切换开销大约是3.8us左右 。从上下文切换的耗时上来看,Linux线程(轻量级进程)其实和进程差别不太大 。
Linux相关命令
既然我们知道了上下文切换比较的消耗CPU时间,那么我们通过什么工具可以查看一下Linux里究竟在发生多少切换呢?如果上下文切换已经影响到了系统整体性能,我们有没有办法把有问题的进程揪出来,并把它优化掉呢?
# vmstat 1 procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----r b swpd free buff cache si so bi bo in cs us sy id wa st2 0 0 595504 5724 190884 0 0 295 297 0 0 14 6 75 0 45 0 0 593016 5732 193288 0 0 0 92 19889 29104 20 6 67 0 73 0 0 591292 5732 195476 0 0 0 0 20151 28487 20 6 66 0 84 0 0 589296 5732 196800 0 0 116 384 19326 27693 20 7 67 0 74 0 0 586956 5740 199496 0 0 216 24 18321 24018 22 8 62 0 8 或者是
# sar -w 1 proc/sTotal number of tasks created per second. cswch/sTotal number of context switches per second. 11:19:20 AM proc/s cswch/s 11:19:21 AM 110.28 23468.22 11:19:22 AM 128.85 33910.58 11:19:23 AM 47.52 40733.66 11:19:24 AM 35.85 30972.64 11:19:25 AM 47.62 24951.43 11:19:26 AM 47.52 42950.50 ...... 上图的环境是一台生产环境机器,配置是8核8G的KVM虚机,环境是在Nginx+fpm的,fpm数量为1000,平均每秒处理的用户接口请求大约100左右 。其中cs列表示的就是在1s内系统发生的上下文切换次数,大约1s切换次数都达到4W次了 。粗略估算一下,每核大约每秒需要切换5K次,则1s内需要花将近20ms在上下文切换上 。要知道这是虚机,本身在虚拟化上还会有一些额外开销,而且还要真正消耗CPU在用户接口逻辑处理、系统调用内核逻辑处理、以及网络连接的处理以及软中断,所以20ms的开销实际上不低了 。
那么进一步,我们看下到底是哪些进程导致了频繁的上下文切换?
# pidstat -w 1 11:07:56 AM PID cswch/s nvcswch/s Command11:07:56 AM 32316 4.00 0.00 php-fpm 11:07:56 AM 32508 160.00 34.00 php-fpm 11:07:56 AM 32726 131.00 8.00 php-fpm ...... 由于fpm是同步阻塞的模式,每当请求Redis、Memcache、Mysql的时候就会阻塞导致cswch/s自愿上下文切换,而只有时间片到了之后才会触发nvcswch/s非自愿切换 。可见fpm进程大部分的切换都是自愿的、非自愿的比较少 。
如果想查看具体某个进程的上下文切换总情况,可以在/proc接口下直接看,不过这个是总值 。
grep ctxt /proc/32583/status voluntary_ctxt_switches: 573066 nonvoluntary_ctxt_switches: 89260 本节结论
上下文切换具体做哪些事情我们没有必要记,只需要记住一个结论既可,测得作者开发机上下文切换的开销大约是2.7-5.48us左右,你自己的机器可以用我提供的代码或工具进行一番测试 。
lmbench相对更准确一些,因为考虑了切换后Cache miss导致的额外开销 。
个人公众号“开发内功管理”,打通理论与实践的任督二脉 。
参考文献