【问题标题】:Modifying Minix 3 Scheduler修改 Minix 3 调度器
【发布时间】:2021-06-20 13:50:18
【问题描述】:

我最近购买了这本书,以更好地了解操作系统的工作原理。我在第二章,我被这个问题困住了,我的操作系统无法使用我添加的代码启动。下面的代码是在 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 版,Minix 书,P219,#45。 修改 Minix 3 调度程序以跟踪每个用户进程最近有多少 CPU 时间。当没有任务或服务器想要运行时,选择 CPU 份额最小的用户进程。

【问题讨论】:

  • 如何“不启动”? Segfault,保护异常等?调用您的代码时,proc_ptr 有效 吗?可能是NULLproc_ptr-&gt;p_nr 是否在 0 到 NR_PROC - 1 的范围内?如果没有,你有 UB [未定义的行为] 并且你正在破坏堆栈 [我假设 recent_time 在堆栈上] 因为你的索引超出了 recent_time 的末尾。您应该添加 if 语句检查有效性并添加 [minux equiv of] printk 以显示值。也许realtime 应该是unsigned [和/或clock_t 作为您将stopwatch 设置为它的值]。 recent_time 可以放入堆栈吗?
  • 我已经添加了我修改过的 pic_proc 函数,如果有帮助的话。进行全新安装后,它将编译新映像。当我尝试从该映像启动时,它会死锁,我必须将其关闭并返回到上一个映像文件。

标签: c operating-system minix


【解决方案1】:

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

proc_ptr必须有效(即非NULL)并且proc_ptr-&gt;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 和/或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 相关的代码。

NR_TASKS + NR_PROCS 应该是从进程表中获取的时间量。我最初在 proc.h 中声明了数组和秒表,但 pic_proc 函数无权访问它们。

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

我添加了一些调用 pic_proc 的代码的屏幕截图。我想我需要修改 sched 并单独留下 pic_proc。 pic_proc 中的数组应该是从进程表中获取每个进程的时间量

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

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

我浏览了内核代码,猜测sched_proc 可能会感兴趣。

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

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

【讨论】:

  • 注意:在 2021 年,大多数 linux 系统都有 8kb 的默认堆栈。 (可以提高)
  • @AntoninGAVREL 是的。我是从记忆中做的[当它是 4KB 时]。但是,我想强调资源的有限性。而且,OP 的内核“无法启动”这一事实似乎表明代码在启动过程的早期被调用。
  • NR_TASKS + NR_PROCS 应该是从进程表中获取的时间量。我最初在 proc.h 中声明了数组和秒表,但 pic_proc 函数无权访问它们。
  • 我添加了一些调用 pic_proc 的代码的屏幕截图。我想我需要修改 sched 并单独留下 pic_proc。 pic_proc 中的数组应该是从进程表中获取每个进程的时间量。