linux程序性能分析工具stap,《面向应用开发者的系统指南》CPU篇之使用systemtap分析进程的行为...

本文是《面向应用开发者的系统指南》文档其中的一篇,完整的目录见《面向应用开发者的系统指南》导论。概述以上描述进程的创建、执行、调度器的工作原理,有了这些准备之后,可以使用systemtap在系统中埋点进行一些跟踪,以便理解进程的行为。分析进程对CPU的占用简单回顾一下前面进程调度相关的内容:内核中使用就绪队列来维护当前所有处于可运行状态的进程,可运行状态不包括等待IO、休眠等状态的进程。进程调度器...
摘要由CSDN通过智能技术生成

本文是《面向应用开发者的系统指南》文档其中的一篇,完整的目录见《面向应用开发者的系统指南》导论。

概述

以上描述进程的创建、执行、调度器的工作原理,有了这些准备之后,可以使用systemtap在系统中埋点进行一些跟踪,以便理解进程的行为。

分析进程对CPU的占用

简单回顾一下前面进程调度相关的内容:

内核中使用就绪队列来维护当前所有处于可运行状态的进程,可运行状态不包括等待IO、休眠等状态的进程。

进程调度器负责从就绪队列中选择处于可运行状态的进程来执行。

而所有不处于可运行状态的进程,并不占用CPU资源,这些进程都等待被相关的事件比如网络IO唤醒,唤醒之后的进程更改状态为可运行状态,同时加入到就绪队列中,然后才能被调度器算法选择执行。

因此,一个进程的整个生命周期中,虽然看上去进程一直存在,但是并不是所有时候都占用CPU资源。根据CPU占用资源与否,或者说当前是否在运行,分为on cpu和off cpu状态:

598b70aecd9630b77b234d541776be38.png

上图中就一个进程执行的时间线做了简单的阶段划分,其中省略掉了进程被创建出来和最后退出时的情况,仅列出占用CPU资源状态的切换。

进程占用CPU获得执行权的时候,称为on cpu时间。

进程因为各种原因(被其他进程抢占、自己调用了sleep系统调用主动进入睡眠状态、等待网络IO等)被剥夺了执行权的时候,首先会调用deactivate_task函数从就绪队列中删除,接下来调用context_switch函数进行进程的上下文切换,这个时候旧的进程失去CPU的执行权,此时正式进入off cpu时间中。

在此之后,进程由于各种原因被唤醒,唤醒之后首先会被再次调用activate_task函数加入到就绪队列中,进入就绪队列的进程也并不是马上就能够获得执行权的,是由进程调度算法来决定哪一个在就绪队列中的进程来执行。这段时间又可以分为两个部分:

进程被切换出去直到重新进入就绪队列,这部分时间内进程等待被唤醒。

进入就绪队列到被调度器选中执行,这部分时间内进程等待被调度执行。

以上两部分时间的总和,加起来就是进程休眠的时间,即处于off cpu状态的时间。

从这里看出来,一个进程虽然看上去一直存在,但并不是所有时间都在执行,跟进一个程序的运行时间时,需要区分其on和off cpu的时间,如果off的时间过长,那需要看看是什么原因导致了进程一直没有被唤醒执行。

另外需要注意的是,进程处于就绪状态,并不一定就是在运行,有可能还在就绪队列中等待被调度执行;但是反之则不然,一个占用CPU在执行的进程,其状态一定是就绪状态。即:

进程处于就绪状态的时间 = 进程在就绪队列的时间 + 进程在执行的时间

关于off cpu这一概念,Off-CPU Analysis一文中有更多的讲述。

有了上面对on cpu和off cpu的介绍,下面来看看使用systemtap如何跟踪这些状态以及所处的时间。

off CPU

systemtap中自带的tapset中,有一个scheduler.stp文件,里面定义了与调度器相关的一些probe。

其中跟踪off cpu的probe是scheduler.cpu_off :

probe scheduler.cpu_off =

kernel.trace("sched_switch") !,

kernel.function("context_switch")

{

name = "cpu_off"

task_prev = $prev

task_next = $next

idle = __is_idle()

}

结合代码和最开始的示意图,可以知道该probe事件是针对内核trace事件sched_switch以及内核函数context_switch的封装,这两个事件都在进程上下文切换时触发。

在该probe事件中,能获取到的参数是:

task_prev:保存切换之前的进程task_struct结构体。

task_next:保存切换之后的进程task_struct结构体。

idle:表示当前CPU是否空闲。

因为这个probe事件记录了进程切换前后的信息,因此可以用来完成类似记录系统切换最多的进程跟踪的功能:

global csw_count

global idle_count

probe scheduler.cpu_off {

csw_count[task_prev, task_next]++

idle_count+=idle

}

function fmt_task(task_prev, task_next)

{

return sprintf("%s(%d)->%s(%d)",

task_execname(task_prev),

task_pid(task_prev),

task_execname(task_next),

task_pid(task_next))

}

function print_cswtop () {

printf ("%45s %10s\n", "Context switch", "COUNT")

foreach ([task_prev, task_next] in csw_count- limit 20) {

printf("%45s %10d\n", fmt_task(task_prev, task_next), csw_count[task_prev, task_next])

}

printf("%45s %10d\n", "idle", idle_count)

delete csw_count

delete idle_count

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值