< 返回版块

ZaynChen 发表于 2020-05-09 21:42

Tags:macos,ffi,上下文切换

最近在学《操作系统导论》(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);
        };
    }
}

References

评论区

写评论
作者 ZaynChen 2020-05-10 10:06

似乎是因为 thread_policy_set() 失败了。目前还不知道怎么解决。

Mike Tang 2020-05-09 22:31

1 共 2 条评论, 1 页