进程是操作系统的伟大发明之一,对应用程序屏蔽了CPU调度、内存管理等硬件细节,而抽象出一个进程的概念,让应用程序专心于实现自己的业务逻辑既可,而且在有限的CPU上可以“同时”进行许多个任务。但是它为用户带来方便的同时,也引入了一些额外的开销。如下图,在进程运行中间的时间里,虽然CPU也在忙于干活,但是却没有完成任何的用户工作,这就是进程机制带来的额外开销。

在进程A切换到进程B的过程中,先保存A进程的上下文,以便于等A恢复运行的时候,能够知道A进程的下一条指令是啥。然后将要运行的B进程的上下文恢复到寄存器中。这个过程被称为上下文切换。上下文切换开销在进程不多、切换不频繁的应用场景下问题不大。但是现在Linux操作系统被用到了高并发的网络程序后端服务器。在单机支持成千上万个用户请求的时候,这个开销就得拿出来说道说道了。因为用户进程在请求Redis、Mysql数据等网络IO阻塞掉的时候,或者在进程时间片到了,都会引发上下文切换。

一个简单的进程上下文切换开销测试实验

废话不多说,我们先用个实验测试一下,到底一次上下文切换需要多长的CPU时间!实验方法是创建两个进程并在它们之间传送一个令牌。其中一个进程在读取令牌时就会引起阻塞。另一个进程发送令牌后等待其返回时也处于阻塞状态。如此往返传送一定的次数,然后统计他们的平均单次切换时间开销。

# gcc main.c -o main

# ./main./main

Before Context Switch Time1565352257 s, 774767 us

After Context SWitch Time1565352257 s, 842852 us

每次执行的时间会有差异,多次运行后平均每次上下文切换耗时3.5us左右。当然了这个数字因机器而异,而且建议在实机上测试。

前面我们测试系统调用的时候,最低值是200ns。可见,上下文切换开销要比系统调用的开销要大。系统调用只是在进程内将用户态切换到内核态,然后再切回来,而上下文切换可是直接从进程A切换到了进程B。显然这个上下文切换需要完成的工作量更大。

进程上下文切换开销都有哪些

那么上下文切换的时候,CPU的开销都具体有哪些呢?开销分成两种,一种是直接开销、一种是间接开销。

直接开销就是在切换时,cpu必须做的事情,包括:

1、切换页表全局目录

2、切换内核态堆栈

3、切换硬件上下文(进程恢复前,必须装入寄存器的数据统称为硬件上下文)

ip(instruction pointer):指向当前执行指令的下一条指令

bp(base pointer): 用于存放执行中的函数对应的栈帧的栈底地址

sp(stack poinger): 用于存放执行中的函数对应的栈帧的栈顶地址

cr3:页目录基址寄存器,保存页目录表的物理地址

4、刷新TLB

5、系统调度器的代码执行

间接开销主要指的是虽然切换到一个新进程后,由于各种缓存并不热,速度运行会慢一些。如果进程始终都在一个CPU上调度还好一些,如果跨CPU的话,之前热起来的TLB、L1、L2、L3因为运行的进程已经变了,所以以局部性原理cache起来的代码、数据也都没有用了,导致新进程穿透到内存的IO会变多。 其实我们上面的实验并没有很好地测量到这种情况,所以实际的上下文切换开销可能比3.5us要大。

想了解更详细操作过程的同学请参考《深入理解Linux内核》中的第三章和第九章。

一个更为专业的测试工具-lmbench

lmbench用于评价系统综合性能的多平台开源benchmark,能够测试包括文档读写、内存操作、进程创建销毁开销、网络等性能。使用方法简单,但就是跑有点慢,感兴趣的同学可以自己试一试。

这个工具的优势是是进行了多组实验,每组2个进程、8个、16个。每个进程使用的数据大小也在变,充分模拟cache miss造成的影响。我用他测了一下结果如下:

-------------------------------------------------------------------------

Host OS 2p/0K 2p/16K 2p/64K 8p/16K 8p/64K 16p/16K 16p/64K

ctxsw ctxsw ctxsw ctxsw ctxsw ctxsw ctxsw

--------- ------------- ------ ------ ------ ------ ------ ------- -------

bjzw_46_7 Linux 2.6.32- 2.7800 2.7800 2.7000 4.3800 4.0400 4.75000 5.48000

lmbench显示的进程上下文切换耗时从2.7us到5.48之间。

线程上下文切换耗时

前面我们测试了进程上下文切换的开销,我们再继续在Linux测试一下线程。看看究竟比进程能不能快一些,快的话能快多少。

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

> 同一进程下的线程之所有getpid()看到的pid是一样的,其实task_struct里还有一个tgid字段。 对于多线程程序来说,getpid()系统调用获取的实际上是这个tgid,因此隶属同一进程的多线程看起来PID相同。

我们用一个实验来测试一下,其原理和进程测试差不多,创建了20个线程,在线程之间通过管道来传递信号。接到信号就唤醒,然后再传递信号给下一个线程,自己睡眠。 这个实验里单独考虑了给管道传递信号的额外开销,并在第一步就统计了出来。

# gcc -lpthread main.c -o main

0.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 st

2 0 0 595504 5724 190884 0 0 295 297 0 0 14 6 75 0 4

5 0 0 593016 5732 193288 0 0 0 92 19889 29104 20 6 67 0 7

3 0 0 591292 5732 195476 0 0 0 0 20151 28487 20 6 66 0 8

4 0 0 589296 5732 196800 0 0 116 384 19326 27693 20 7 67 0 7

4 0 0 586956 5740 199496 0 0 216 24 18321 24018 22 8 62 0 8

或者是

# sar -w 1

proc/s

Total number of tasks created per second.

cswch/s

Total 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 Command

11: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导致的额外开销。

> 扩展:平时大家在操作系统理论学习的时候都知道CPU时间片的概念,时间片到了会将进程从CPU上赶下来,换另一个进程上。但其实在我们互联网的网络IO密集型的应用里,真正因为时间片到了而发生的非自愿切换很少,绝大部分都是因为等待网络IO而进行的自愿切换。 上面的例子你也可以看出,我的一个fpm进程主动切换有57W次,而被动切换只有不到9W次。所以,在同步阻塞的开发模式里,网络IO是导致上下文切换频繁的元凶

mysql 上下文切换高_进程/线程上下文切换会用掉你多少CPU?相关推荐

  1. cpu线程_进程/线程上下文切换会用掉你多少CPU?

    进程是操作系统的伟大发明之一,对应用程序屏蔽了CPU调度.内存管理等硬件细节,而抽象出一个进程的概念,让应用程序专心于实现自己的业务逻辑既可,而且在有限的CPU上可以"同时"进行许 ...

  2. CPU分析系列--vmstat/pidstat -wt分析进程/线程上下文切换造成的性能瓶颈

    目录 1.从系统层面看:vmstat 1 3 2.从进程层面看:pidstat -w 3.从线程层面看上下文切换:pidstat -wt 4.案例: 1.使用sysbench模拟多线程切换. yum ...

  3. Java并发篇_进程线程

    一个进程包括由操作系统分配的内存空间,包含一个或多个线程.一个线程不能独立的存在,它必须是进程的一部分.一个进程一直运行,直到所有的非守护线程都结束运行后才能结束. 多线程能满足程序员编写高效率的程序 ...

  4. 进程 线程 协程_进程 线程 协程 管程 纤程 概念对比理解

    不知道是不是我自己本身就有那么一丝丝的密集恐惧,把这么一大堆看起来很相似很相关的概念放在一起,看起来是有点麻,捋一捋感觉舒服多了. 相关概念 任务.作业(Job,Task,Schedule) 在进程的 ...

  5. 什么数据库比mysql效率高_牛x!一款比传统数据库快 100-1000 倍的数据库,来认识一下?...

    一.ClickHouse 是什么? 二.业务问题 三.ClickHouse实践 四.遇到的坑 五.总结 一.ClickHouse 是什么?ClickHouse:是一个用于联机分析(OLAP)的列式数据 ...

  6. sqlsugar对mysql效率高_基于.Net Core 2.0 + SqlSugar ORM + MySql快速实现网站开发

    .Net Core 2.0 (以下简称Core)正式版已经发布有一段时间了, 博主也第一时间尝鲜了, 相比Core 1.0和1.1类库支持方面提高了不少, 开发起来也方便快捷很多了. 废话不多说了, ...

  7. 二级mysql通过率高_计算机二级各科通过率是多少 好过吗

    为了更快的得到计算机二级证书,考生们都很关心计算机二级各个科目的通过率,想要寻找通过率最高的.相对来说较容易通过的.下面是小编为大家准备的计算机二级各科具体通过率. 计算机二级通过率 之前一直有人在说 ...

  8. matlab 设置最大并行数_浅析线程池参数设置

    背景 首先先明确一下线程池的主要作用是什么 线程池解决的核心问题就是资源管理问题.在并发环境下,系统不能够确定在任意时刻中,有多少任务需要执行,有多少资源需要投入.这种不确定性将带来以下若干问题: 频 ...

  9. mysql 读写引擎_揭秘MySQL存储引擎spider

    转自:兴趣部落​buluo.qq.com 导读: Spider是为MySQL/MariaDB开发的一个特殊引擎,具有内嵌分片功能.现在它已经被集成到MariaDB10.0及以上版本中,作为MariaD ...

最新文章

  1. [转载]SSH框架搭建详细图文教程
  2. 这5种动态炫酷图,用Python就可以画!
  3. 工作中linux定时任务的设置及相关配置
  4. (转载)封装bilibili播放器,自定义边下边播和缓存功能
  5. OpenGL multiviewport多个视口的实例
  6. Android之Universal-Image-loader
  7. 1.9 编程基础之顺序查找 05 最大值和最小值的差 python
  8. powerdesigner辅助导入导出excel文件
  9. c++11 多线程 2c++ concurrency in action
  10. SVN的使用及MyEclipse的集成
  11. jmeter5.4.1插件管理器下载安装-Jmeter-plugins-manager
  12. 作为音乐创作人,你可知道?有可以自己写歌词的软件,专门写歌词的软件,创作歌词的软件,帮忙写歌词的软件
  13. 软件测试VS软件开发哪个好?怎么选择
  14. 组成计算机硬件系统的基本部分为,组成计算机硬件系统的基本部分有哪些
  15. Qt中使用httpServer框架
  16. 自学测试入门—用户注册功能的测试
  17. Batch Normation
  18. JavaScript中classList属性和className的区别
  19. android mp3 lrc歌词文件utf-8歌词显示为乱码,百度歌词显示乱码 LRC歌词批量转换 UTF-8编码批量转换为GB或ANSI 文本编码批量转换...
  20. IKBC-DC87无线连接方法

热门文章

  1. AR地图可视化,原来应用这么广!
  2. C语言为什么能够恒久不衰,来看看吧!
  3. 三星GT-N8010刷机教程
  4. 中国慈展会谱写“扶贫三部曲”
  5. electorn更换窗口图标
  6. linux退出热键_linux 用户退出怎么命令
  7. 开源代码难阅读?几位研发的“妙招”帮你解决
  8. 直播技术:如何实现1080P延迟低于500ms的实时超清直播传输技术
  9. 达人评测 i5 13500h和i7 12700h选哪个好 酷睿i513500h和i712700h差距
  10. [附安卓获取方式]微信大更8.0,可爱特效,走心功能!