Open arthur-zhang opened 3 years ago
[TOC]
系统调用的过程中是有上下文切换的,并且发生了两次上下文切换(保存用户的指令位置,保存寄存器的值),一次用户态切换到内核态,一次内核态切回用户态
内核中用就绪队列来维护所有处于可运行状态的进程,可运行状态不包括等待IO、休眠等状态的进程。
进程调度器负责从就绪队列中选择处于可运行状态的进程来执行。
而所有不处于可运行状态的进程,并不占用CPU资源,这些进程都等待被相关的事件比如网络IO唤醒,唤醒之后的进程更改状态为可运行状态,同时加入到就绪队列中,然后才能被调度器算法选择执行。
因此在一个进程的整个生命周期中,虽然进程看上去一直存在,但是并不是所有时候都在占用CPU,根据占用CPU与否分为
on cpu
和off cpu
。
进程因为各种原因(被其他进程抢占、自己调用了sleep系统调用主动进入睡眠状态、等待网络IO等)被剥夺了执行权的时候,首先会调用
deactivate_task
函数从就绪队列中删除,接下来调用context_switch
函数进行进程的上下文切换,这个时候旧的进程失去CPU的执行权,此时正式进入off cpu
时间中其实火焰图就是分析
on cpu
和off cpu
的时间,如果on cpu
或者off cpu
的时间过长,就要看看为什么进程一直持有cpu,或者进程一直拿不到cpu
systemap :
systemtap 自带的tapset (systemtap 提供的一个探针和函数的内置库,可以在我们编写的 systemtap 脚本中复用他们 ,类似于C语言的库函数) 中,有一个scheduler.stp文件,里面定义了与调度器相关的一些probe https://github.com/jav/systemtap/blob/master/tapset/scheduler.stp
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事件中,能获取到的参数是:
task_struct
结构体。task_struct
结构体。测试代码:cswstap.stap
global csw_count
global idle_count
global csw_total
probe scheduler.cpu_off {
csw_count[task_prev, task_next]++
csw_total+=1
idle_count+=idle
}
function fmt_task(task_prev, task_next) {
return sprintf("tid(%d)->tid(%d)", task_tid(task_prev), task_tid(task_next))
}
function print_cswtop () {
printf ("%45s %10s\n", "Context switch", "COUNT")
foreach ([task_prev, task_next] in csw_count- limit 5) {
printf("%45s %10d\n", fmt_task(task_prev, task_next), csw_count[task_prev, task_next])
}
printf("%45s %10d\n", "csw_total", csw_total)
printf("%45s %10d\n", "idle", idle_count)
delete csw_total
delete csw_count
delete idle_count
}
probe timer.s($1) {
print_cswtop ()
printf("--------------------------------------------------------------\n")
}
执行 fake_make 程序./fake_make -j4
systemtap 查看上下文切换次数 stap cswstap.stap 1
systemtap 脚本