Linux进程与CPU相关基础

  1. 一、Linux 线程 进程 协程
  2. 二、Linux僵尸进程
  3. 三、CPU上下文切换
  4. 四、Linux CFS调度器

一、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)的概念来实现公平性

如何实现的公平:

  1. 虚拟运行时间(vruntime),每个进程维护一个vruntime变量,表示该进程已获得的cpu时间,但经过优先级加权后的值。CFS总是选择vruntime最小的进程来运行,vruntime的计算公式:
    • vruntime = 实际使用的CPU时间 * (优先级为0(默认优先级)的权重 / 基于进程优先级的权重)
  2. 红黑树数据结构, cfs使用红黑树来组织可运行进程,以vruntime为键值。这允许:
    • 高效地找到vruntime最小地进程
    • 高效地插入和删除进程
  3. 调度周期, cfs定义了一个调度周期通常为几毫秒,目的是让所有可运行进程在这个周期内都能运行一次,每个进程分配的时间为:
    • time_slice = sched_latency * (weight / total_weight)
  4. 动态时间片,cfs不采用固定时间片,而是:
    • 当进程数少时,每个进程获得更长的时间
    • 当进程数多时,调度周期自动扩展,但不超过sched_latency_max
  5. 优先级处理,通过nice值影响进程的权重:
    • 高优先级(nice值小)的进程获得更多CPU时间
    • 低优先级(nice值大)的进程获得较少CPU时间
    • 权重差异被严格控制(避免优先级反转问题)

转载请注明来源, 欢迎对文章中的引用来源进行考证, 欢迎指出任何有错误或不够清晰的表达, 可以邮件至 chinaops666@gmail.com
相册