一、Linux 线程 进程 协程
- 进程: 对正在运行程序的抽象, 比如一个web浏览器、一个vscode 都是进程,进程是程序运行资源的集合,是系统资源分配的最小单位。
- 线程:一个进程环境中的多个执行流,这些执行流很大程度上相对独立,在进行中,程序执行的最小单位(执行流)就是线程。可以并行运行在进程中,就像他们是单独的”线程“一样 只是它们共享相同的地址空间、代码、数据、信号处理、打开文件、全局变量等
- 线程栈1M
- 协程:简历在线程之上的抽象,它需要线程来承载运行,一个线程可以有多个协程
- 协程很小只有几kb
- 比如GO语言的goroutine,一个
go关键字就可以运行一个协程程序,go语言中协程通过go提供的runtime来控制和调度,通过context包进行上下文传输。 - 协程通过语言提供的runtime来调度,用户空间直接调度不需要在内核空间和用户空间来回切换,效率低
- 能更好地利用cpu地多核,提高程序执行能力
- 避免阻塞,如果协程所在地线程发生了阻塞,协程调度器可以把运行在阻塞线程上地协程 调度到其他线程上继续运行。
Golang中地协程与线程的关系
go中的协程相当于一个微线程,由Go Runtime调度使用。 goroutine的协程都是运行在线程上的。
它的调度模型是一个GMP模型:
- G: goroutine,表示go的一个协程,也就是”微线程“
- M: machine, 表示线程,G在M上运行
- P: processor ,它包含了运行goroutine所需资源,如果一个M想运行一个goroutine,那么要先获取 processor
二、Linux僵尸进程
ps 看到STAT为 Z 或者Zs的进程
简单来说:僵尸进程的出现时间是在子进程终止后,但是父进程尚未读取这些数据之前
当你运行一个程序时,它会产生一个父进程以及很多子进程。 所有这些子进程都会消耗内核分配给它们的内存和 CPU 资源。
这些子进程完成执行后会发送一个 Exit 信号然后死掉。这个 Exit 信号需要被父进程所读取。父进程需要随后调用 wait 命令来读取子进程的退出状态,并将子进程从进程表中移除。
若父进程正确第读取了子进程的 Exit 信号,则子进程会从进程表中删掉。
但若父进程未能读取到子进程的 Exit 信号,则这个子进程虽然完成执行处于死亡的状态,但也不会从进程表中删掉。
那么此时如果父进程是一个循环不会结束,子进程就会一直保持僵尸状态。
僵尸进程不做任何事情,不会使用任何资源也不会影响其他进程,因此也没什么坏处, 但是进程表中的退出状态以及其他一些进程信息是存在内存里的,太多也会是个问题
kill -9杀不掉子进程的, 必须杀掉父进程
三、CPU上下文切换
是指CPU从一个进程(或线程)切换到另一个进程(或线程)时,必须保存当前运行任务的上下文(状态),并恢复下一个任务的上下文的过程。
上下文切换的类型:
- 进程上下文切换:不同进程间切换,开销较大
- 线程上下文切换:同一进程的线程间切换,开销较小
- 模式切换:用户态和内核态之间的切换
上下文切换会导致性能开销,因为:
- 需要保存和恢复大量寄存器状态
- 可能导致CPU缓存失效(TLB、数据缓存等)
- 频繁切换会减少实际任务执行时间
在性能敏感的应用中,减少不必要的上下文切换是优化的重要方向。
如何诊断频繁上下文切换带来的性能问题?
确认上下文切换是否过高, 通过vmstat 1 关注cs列、通过dstat -c -y –top-cpu查看详情、pidstat -w 查看每个进程的上下文切换情况
- 正常情况上下文切换次数在每秒几千次以内
- 超过1万通常表示可能存在问题
- 超过10万基本是肯定有问题的
四、Linux CFS调度器
完全公平调度器, 2.6版本成为默认调度器取代之前的O(1)调度器
CFS的核心是”完全公平“地分配CPU时间给所有可运行地进程。与传统的基于时间片的调度器不同,CFS不是直接分配固定的时间片,而是通过虚拟运行时间(vruntime)的概念来实现公平性
如何实现的公平:
- 虚拟运行时间(vruntime),每个进程维护一个vruntime变量,表示该进程已获得的cpu时间,但经过优先级加权后的值。CFS总是选择vruntime最小的进程来运行,vruntime的计算公式:
vruntime = 实际使用的CPU时间 * (优先级为0(默认优先级)的权重 / 基于进程优先级的权重)
- 红黑树数据结构, cfs使用红黑树来组织可运行进程,以vruntime为键值。这允许:
- 高效地找到vruntime最小地进程
- 高效地插入和删除进程
- 调度周期, cfs定义了一个调度周期通常为几毫秒,目的是让所有可运行进程在这个周期内都能运行一次,每个进程分配的时间为:
time_slice = sched_latency * (weight / total_weight)
- 动态时间片,cfs不采用固定时间片,而是:
- 当进程数少时,每个进程获得更长的时间
- 当进程数多时,调度周期自动扩展,但不超过sched_latency_max
- 优先级处理,通过
nice值影响进程的权重:- 高优先级(nice值小)的进程获得更多CPU时间
- 低优先级(nice值大)的进程获得较少CPU时间
- 权重差异被严格控制(避免优先级反转问题)
转载请注明来源, 欢迎对文章中的引用来源进行考证, 欢迎指出任何有错误或不够清晰的表达, 可以邮件至 chinaops666@gmail.com