首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

揭秘进程调度:让你的程序有序跑起来

近日见闻

Electron v28.2.0 发布,一款跨平台的桌面应用开发工具 --electron社区

Linus 发飙,批评谷歌内核贡献者的代码是垃圾,“你复制了一段不理解为何执行此操作的函数,这就是垃圾代码!”

过年前的时间过的很快,想起来一句话,幸福在于等幸福到来的那段日子。

摘抄:

为最坏的情况做好准备,

为最好的情况敞开心扉。

——斯鲁日特尔《莫斯科小猫》

小记

这两天和同事讨论起linux进程调度的问题,比如进程统计、那些进程优先运行、怎么调度等,对此在这里和大家一同复习一下。先来说说怎么查看进程。在使用Linux操作系统的过程中,掌握如何查看和管理进程是系统管理的重要技能之一。进程管理不仅有助于监控系统资源的使用情况,还能帮助排查问题和优化系统性能。

查看进程的常见命令

ps (Process Status)

ps命令是最基本同时也是最常用的进程查看命令,它能够列出系统中当前运行的所有进程信息。

基本用法

ps aux:显示所有进程信息

ps -ef:以全格式列出所有进程

ps -u [username]:列出指定用户的所有进程

如图所示

top

top命令提供了一个实时更新的进程状态动态视图。默认情况下,它以CPU使用率降序排列进程。

基本用法

执行top即可进入top界面,按q退出。

htop

htop是top命令的一个增强版本,提供了更友好的界面和更多的信息展示。

基本用法

安装htop(如果系统未预装):sudo apt-get install htop(Debian/Ubuntu)或者sudo yum install htop(CentOS/RedHat)

执行htop即可进入htop界面,按F10退出。

pstree

pstree命令展示的是进程之间的层级关系,以树状图的方式显示。

基本用法

pstree:展示系统中的所有进程及其关系

pstree [username]:展示指定用户的进程树

进程信息解读

无论是ps、top还是htop,它们都会显示以下几个重要的进程信息:

PID:进程ID

USER:运行该进程的用户

PR:进程优先级

NI:进程的“nice”值,即优先级数值

VIRT:虚拟内存使用量

RES:常驻内存量

SHR:共享内存量

S:进程状态(S睡眠,R运行,T停止,Z僵尸)

%CPU:占用的CPU百分比

%MEM:占用的内存百分比

TIME+:总的CPU时间

COMMAND:启动命令/命令行

掌握了如何查看进程,我们再来讨论一下进程是如何调度的。在Linux操作系统中,进程调度是操作系统最为核心的功能之一。它负责合理分配处理器资源,决定哪个进程何时运行,以及运行多长时间。本文将深入探讨Linux中的进程调度机制。

什么是进程调度?

进程调度是指操作系统按照一定的策略,动态地将处理器分配给等待运行的进程。调度的目的是为了优化系统性能,保证各类进程合理、公平地获取CPU使用权。

Linux进程调度的类型

Linux系统中的进程调度主要有两种类型:

完全公平调度器(Completely Fair Scheduler,CFS):从Linux内核版本2.6.23开始,CFS成为默认的进程调度器。CFS的设计目标是最大化利用CPU,并在进程间提供公平的CPU时间分配。

实时调度器(Real-Time Scheduler):用于实时任务,确保任务在规定时间内完成。包含两种策略:

FIFO(先进先出)

RR(轮转)

Linux进程调度策略

完全公平调度器(CFS)

CFS的核心思想是所有进程都应该获得相等的CPU时间。CFS将CPU资源分成时间片,用红黑树来管理所有可运行的进程,保证每个进程都能获得公平的时间片。

红黑树

红黑树是一种自平衡的二叉查找树,一种特殊的数据结构。在这棵树中,每个节点都有颜色属性,要么是红色,要么是黑色。因为它涉及多个操作的细节,比如插入、删除、旋转(左旋和右旋)、重新着色等,每个操作都必须维护红黑树的性质。感兴趣的可以自行查阅,这里贴一个简单的算法示例。这个实现并不完整,主要用于展示红黑树操作的基本概念。

class?Node:

def?__init__(self,?data,?color="red"):

self.data?=?data

self.color?=?color

self.parent?=?None

self.left?=?None

self.right?=?None

class?RedBlackTree:

def?__init__(self):

self.TNULL?=?Node(0,?color="black")??#?创建一个空节点作为叶子节点

self.root?=?self.TNULL

def?left_rotate(self,?x):

#?左旋示意图:

#?????x???????????y

#????/?\?????????/?\

#???a???y??=>???x???c

#??????/?\?????/?\

#?????b???c???a???b

y?=?x.right

x.right?=?y.left

if?y.left?!=?self.TNULL:

y.left.parent?=?x

y.parent?=?x.parent

if?x.parent?==?None:??#?x是根节点

self.root?=?y

elif?x?==?x.parent.left:??#?x是其父节点的左子节点

x.parent.left?=?y

else:??#?x是其父节点的右子节点

x.parent.right?=?y

y.left?=?x

x.parent?=?y

def?right_rotate(self,?y):

#?右旋示意图:

#??????y???????????x

#?????/?\?????????/?\

#????x???c??=>???a???y

#???/?\?????????????/?\

#??a???b???????????b???c

x?=?y.left

y.left?=?x.right

if?x.right?!=?self.TNULL:

x.right.parent?=?y

x.parent?=?y.parent

if?y.parent?==?None:??#?y是根节点

self.root?=?x

elif?y?==?y.parent.right:??#?y是其父节点的右子节点

y.parent.right?=?x

else:??#?y是其父节点的左子节点

y.parent.left?=?x

x.right?=?y

y.parent?=?x

def?insert(self,?key):

#?插入操作

node?=?Node(key)

node.parent?=?None

node.data?=?key

node.left?=?self.TNULL

node.right?=?self.TNULL

node.color?=?"red"??#?新节点必须是红色

y?=?None

x?=?self.root

while?x?!=?self.TNULL:

y?=?x

if?node.data?<?x.data:

x?=?x.left

else:

x?=?x.right

node.parent?=?y

if?y?==?None:

self.root?=?node

elif?node.data?<?y.data:

y.left?=?node

else:

y.right?=?node

if?node.parent?==?None:

node.color?=?"black"

return

if?node.parent.parent?==?None:

return

self.fix_insert(node)??#?调用插入修复函数来维护红黑树的性质

def?fix_insert(self,?k):

#?插入节点后修复红黑树性质的函数

while?k.parent.color?==?"red":

if?k.parent?==?k.parent.parent.right:

u?=?k.parent.parent.left??#?叔叔节点

if?u.color?==?"red":

u.color?=?"black"

k.parent.color?=?"black"

k.parent.parent.color?=?"red"

k?=?k.parent.parent

else:

if?k?==?k.parent.left:

k?=?k.parent

self.right_rotate(k)

k.parent.color?=?"black"

k.parent.parent.color?=?"red"

self.left_rotate(k.parent.parent)

else:

#?与上面的代码一模一样,只不过是方向相反

u?=?k.parent.parent.right

if?u.color?==?"red":

u.color?=?"black"

k.parent.color?=?"black"

k.parent.parent.color?=?"red"

k?=?k.parent.parent

else:

if?k?==?k.parent.right:

k?=?k.parent

self.left_rotate(k)

k.parent.color?=?"black"

k.parent.parent.color?=?"red"

self.right_rotate(k.parent.parent)

if?k?==?self.root:

break

self.root.color?=?"black"

def?print_tree(self,?node,?indent,?last):

#?用于打印红黑树的辅助函数

if?node?!=?self.TNULL:

print(indent,?end='?')

if?last:

print("R----",?end='?')

indent?+=?"?????"

else:

print("L----",?end='?')

indent?+=?"|????"

s_color?=?"RED"?if?node.color?==?"red"?else?"BLACK"

print(str(node.data)?+?"("?+?s_color?+?")")

self.print_tree(node.left,?indent,?False)

self.print_tree(node.right,?indent,?True)

特点

不基于固定时间片,而是动态分配

使用虚拟运行时间来进行比较,避免优先级倒置问题

当进程创建或唤醒时,它会被插入到红黑树中

当进程消耗时间片后,它会被重新插入到树中,保证调度的公平性

实时调度器

实时调度器为需要快速响应的实时任务提供服务,分为两种策略:

SCHED_FIFO(先进先出):没有时间片的概念,一旦获得CPU,除非自己放弃,或者有更高优先级的进程需要运行,否则会一直运行。

SCHED_RR(轮转):类似于SCHED_FIFO,但是每个进程运行一定的时间片后,如果还有其他同优先级的进程等待,则自动放弃CPU。

进程调度过程

当前运行进程的时间片用完或主动放弃CPU时,进程调度器被唤醒。

进程调度器选择下一个运行的进程。

如果有实时进程等待运行,根据实时调度策略选择进程。

如果没有实时进程,按照CFS策略从红黑树中选择下一个运行的进程。

结语

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OcUHNc9P60VwYm7mQlYmJggg0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

相关快讯

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券
http://www.vxiaotou.com