修改 Minix 3 调度器

Modifying Minix 3 Scheduler

我最近购买了这本书,以便更好地了解操作系统的工作原理。我在第二章,我被这个问题困住了,我的 OS 没有用我添加的代码启动。下面的代码被添加到 pic_proc 函数开始处的 proc.c 以尝试修改调度程序。

修改。

int realtime;
clock_t recent_time[NR_TASKS + NR_PROCS];
clock_t stopwatch;

realtime = getuptime();
recent_time[proc_ptr->p_nr + NR_TASKS] = realtime - stopwatch;
stopwatch = realtime;

原码:

    PRIVATE void pick_proc()
    {
    register struct proc *rp;                     /* process to run */
    int q;                                        /* iterate over queues */
    
    /* Modified Code here */

     for (q=0; q < NR_SCHED_QUEUES; q++) {
        if ( (rp = rdy_head[q]) != NIL_PROC) {
              next_ptr = rp;                      
              if (priv(rp)->s_flags & BILLABLE)
                  bill_ptr = rp; 
     return;
      }
}
}

书籍操作系统设计与实现。第 3 版 The Minix Book,P219,#45。 修改 Minix 3 调度程序以跟踪每个用户进程最近有多少 CPU 时间。当没有任务或服务器想要 运行 时,选择具有最小份额的 CPU.

的用户进程

根据你的新代码[和 pick_proc 函数体],唯一可能的失败是我在上面的评论中描述的那些。

也就是说,proc_ptr 必须有效(即非NULL)并且proc_ptr->p_nr必须在范围内以防止UB。

NR_TASKSNR_PROCS 值是多少? recent_time 可以放在堆栈上吗?内核中的堆栈大小通常非常有限。例如,在linux下,你只能有大约4KB的堆栈。

所以,如果 NR_PROCS 是(例如)32767,那么 recent_time 可以 而不是 放在堆栈上。

而且,我看不出有什么理由在堆栈上为此设置一个独立的数组,因为它不会在调用后持续存在。

所以,为了帮助调试,我将 static 添加到 recent_time 定义

您需要添加一些调试代码以查看问题所在。

这是一个示例。调整 printf 等。阿尔。为了适应 minux 内核的打印机制 [假设您 可以 在您的代码被调用的状态下打印]:

#ifdef DEBUG
#define dbgprt(_fmt...) \
    printf(_fmt)
#else
#define dbgprt(_fmt...) \
    do { } while (0)
#endif

PRIVATE void
pick_proc()
{
    register struct proc *rp;           /* process to run */
    int q;                              /* iterate over queues */

    /* Modified Code here */
    int realtime;
    static clock_t recent_time[NR_TASKS + NR_PROCS];
    clock_t stopwatch;
    do {
        // bad pointer
        dbgprt("pick_proc: proc_ptr=%p\n",proc_ptr);
        if (proc_ptr == NULL)
            break;

        // out of range process number
        dbgprt("pick_proc: p_nr=%u\n",proc_ptr->p_nr);
        if (proc_ptr->p_nr >= NR_PROCS)
            break;

        realtime = getuptime();
        dbgprt("pick_proc: realtime=%d\n",realtime);
        recent_time[proc_ptr->p_nr + NR_TASKS] = realtime - stopwatch;
        stopwatch = realtime;
    } while (0);

    for (q = 0; q < NR_SCHED_QUEUES; q++) {
        if ((rp = rdy_head[q]) != NIL_PROC) {
            next_ptr = rp;
            if (priv(rp)->s_flags & BILLABLE)
                bill_ptr = rp;
            return;
        }
    }
}

更新:

我怎么强调都不为过 assert and/or if 声明预先验证您为防止 UB 所做的任何事情的重要性。并且,debugprintf,如:

TRACE(VF_PICKPROC,printf("hello world\n"););

顺便说一句,我从 github:

中提取了 minix 源代码

git 克隆 https://github.com/Stichting-MINIX-Research-Foundation/minix.git

看来您可能有 过时的 来源,因为该回购中的 pick_proc 有更多 SMP 相关代码。

The NR_TASKS + NR_PROCS should be the time quanta taken from the process table. I orginally declared the Array and stopwatch in proc.h but the pic_proc function did not have access to them.

您真的想使用 struct proc 中的现有字段或添加一些您自己的字段,而不是创建一个单独的数组 [按进程编号索引]。

I added some screen shots of code that calls pic_proc. I think i need to modify sched and leave pic_proc alone. The array in pic_proc should be getting the time quanta from each process from the process table

是的,希望每个进程经过的 CPU 时间在 struct proc 之内。如果没有,您将必须添加一个字段。我怀疑 p_user_time [and/or p_sys_time] 可能是你想要的。而且,也许 p_cpu_time_left。其他可能性:p_accounting.time_in_queue。或者,p_cycles

pick_proc 只查看给定 运行 队列的头部。因此,您可能需要更改实际将给定进程插入该队列的代码。 可能enqueueand/ordequeue。它们似乎尊重进程优先级 [p_priority] 和抢占。

我浏览了内核代码,猜测sched_proc 可能很有趣。

但是,我真的认为您需要更仔细地检查内核代码以了解哪些函数实际将进程添加到给定的 运行 队列。并且,他们如何 select 进程以及来自哪个队列。

添加进程时,可能只是追加到尾部。该代码需要扫描队列并[假设优先级是相同],根据最低CPU使用率插入。