最近在学《操作系统导论》(Operating Systems: Three Easy Pieces)。在第六章有个测量上下文切换开销的作业,原作业的提示是利用 UNIX 读写管道测量,我采用一个期刊上说的 sched_yield()
测量。这里用的大部分是 macos 的库函数, sched_yield()
位于 sched.h 中。
首先要确保上下文切换进程在同一个 CPU 上,于是用了位于 mach/thread_act.h 下的 thread_policy_set()
方法,让进程共享 L2 缓存,从而使其在同一个 CPU 上。然后要保证该 CPU 上没有其他进程的干扰,也就是只有两个进行上下文切换的进程,这里用了 pthread.h 下的 pthread_setschedparam()
函数,采用 SHED_FIFO 调度策略,同时将优先级设成最高 99。
在 main() 函数中,fork 出父子进程,父进程负责测量统计。当父进程运行到 sched_yield()
时会切换到子进程运行,子进程运行到 sched_yield()
时切回父进程运行,此时总共进行了 2 次上下文切换,在这里保存开销时间,并继续进行多次循环测量。最终测量出的结果中除了上下文切换的开销外,还有 sched_yield()
系统调用和运算等开销,相比于用管道读写,开销更小。
在不去掉系统调用开销的情况下, 采用sched_yield () 测试上下文切换的结果相对更精确(根据期刊描述)。
我在父子进程的shed_yield()调用附近使用 println!()打印,其结果是父进程里的先打印多次,然后父进程与子进程交叉打印(并不是严格的一前一后),最后全是子进程的打印。如果像上面所说的,同一个 CPU 2 个优先级最高的进程,似乎不应该出现这种结果。有没有人知道我这代码或者思路错在哪里?源代码地址
use nix::sched::sched_yield;
use nix::unistd::{fork, ForkResult};
use std::process::exit;
use std::time::Instant;
fn main() {
// 测量操作系统切换进程时,上下文切换所需时间
// 最终的延时还包含 sched_yield 系统调用的时间
// os: macos
const COUNT: usize = 20000;
// 设置 父子进程在同一个 cpu上,调度策略 FIFO, 优先级 99 最高
affinity::set_affinity(1);
match fork() {
Ok(ForkResult::Parent { child: _ }) => {
let mut total_duration = vec![0; COUNT];
for i in 0..COUNT {
let now = Instant::now();
sched_yield().unwrap();
// 总共 yield 2 次,父进程 1 次,子进程 1 次。也就是 2 次 context switch
total_duration[i] = now.elapsed().as_nanos() / 2;
// println!("parent");
}
total_duration.iter().for_each(|d| {
println!("time = {} ns", d);
});
}
Ok(ForkResult::Child) => {
for _ in 0..COUNT {
sched_yield().unwrap();
// println!("child");
}
}
Err(_) => {
eprintln!("fork failed");
exit(1);
}
}
}
use nix::sys::pthread::{pthread_self, Pthread};
const SCHED_FIFO: c_int = 4;
pub fn set_affinity(core_id: usize) {
let policy_info = thread_affinity_policy {
affinity_tag: core_id as integer_t,
};
unsafe {
let thread = pthread_self();
// 设置 thread affinity, 使用单核CPU
thread_policy_set(
thread as thread_t,
THREAD_AFFINITY_POLICY, // =4 : mach/thread_policy.h
&policy_info,
THREAD_AFFINITY_POLICY_COUNT, // =1 : mach/thread_policy.h
);
// 设置 最高优先级以及先进先出调度策略
// Processes scheduled under one of the real-time policies (SCHED_FIFO, SCHED_RR)
// have a sched_priority value in the range 1 (low) to 99 (high).
let sched_pa = sched_param { // sizeof() = 8 : sched.h
sched_priority: 99,
__opaque: [0; __SCHED_PARAM_SIZE__], // 占位对齐用,无实际意义
};
if pthread_setschedparam(thread, SCHED_FIFO, &sched_pa) != 0 {
exit(1);
};
}
}
评论区
写评论似乎是因为 thread_policy_set() 失败了。目前还不知道怎么解决。
棒