From da58ebc980afb42908fe41f5517f232fe752188e Mon Sep 17 00:00:00 2001
From: jls <2605141046@qq.com>
Date: Sun, 22 Dec 2024 16:00:20 +0800
Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E8=B5=9B=E6=96=87=E6=A1=A3=E6=8F=90?=
 =?UTF-8?q?=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 ...35\350\265\233\346\226\207\346\241\243.md" | 42 +++++++++++++++----
 1 file changed, 34 insertions(+), 8 deletions(-)

diff --git "a/doc/2024_\345\210\235\350\265\233\346\226\207\346\241\243.md" "b/doc/2024_\345\210\235\350\265\233\346\226\207\346\241\243.md"
index 3110c39..0dc1026 100644
--- "a/doc/2024_\345\210\235\350\265\233\346\226\207\346\241\243.md"
+++ "b/doc/2024_\345\210\235\350\265\233\346\226\207\346\241\243.md"
@@ -2,12 +2,12 @@
 
 - [初赛文档](#初赛文档)
   - [1.概述](#1概述)
-  - [2. 设计与实现]()
+  - [2. 设计与实现](#2-设计与实现)
   - [3. 总结与展望](#3-总结与展望)
 
 ## 1.概述
 
-我们保留了原 `xv6-k210` 中的所有文档,新文档中添加了os前缀的是合工大学长的一些工作,仓库为https://gitlab.eduxiji.net/educg-group-14238-914330/853476998-2958.git,由于我们队伍整体操作系统项目开发经验十分不足,因此打算在前辈的基础上进行一些操作系统内核代码编写的学习,其中文档部分将以2024为前缀进行区分,对于文档描述中冲突的部分,以新文档为准。
+我们保留了原 xv6-k210 中的所有文档,由于我们队伍整体操作系统项目开发经验十分不足,因此打算在前辈的基础上进行一些操作系统内核代码编写的学习,其中文档部分将以2024为前缀进行区分,对于文档描述中冲突的部分,以新文档为准。
 
 队员全部来自合肥工业大学,组员以及分工如下:
 
@@ -71,7 +71,7 @@ void yield(void);
 void scheduler(void);
 ```
 
-在trap.c中要确保时钟中断和时间片触发调度,主要在usertrap函数中在which_dev==2的条件中修改为:
+在trap.c中要确保时钟中断和时间片触发调度,主要在usertrap函数中在which_dev==2(代表是定时器中断)的条件中修改为:
 ```c
   if(which_dev == 2){
     if(p && p->state == RUNNING) { // 检查当前运行的进程是否存在并在运行
@@ -149,7 +149,7 @@ scheduler(void)
           sfence_vma(); // 清除 TLB,确保页表切换后内存访问正确
 
           // 切换到选中的进程,保存当前 CPU 的上下文到 c->context,
-          // 并切换到 p->context(用户进程的上下文)
+          // 并切换到 p->context(用户进程的上下文) 因此不在实际需要调用sched函数
           swtch(&c->context, &p->context);
 
           // 从用户进程返回后,切换回内核页表
@@ -164,7 +164,7 @@ scheduler(void)
                 p->queue_level++; // 将进程降级到下一优先级队列
               }
               // 从当前队列移出,将进程加入到对应的低优先级队列
-              mlfq[q][head] = 0; // 当前队列位置置空
+              mlfq[q][head] = NULL; // 当前队列位置置空
               mlfq[p->queue_level][queue_tail[p->queue_level]] = p; // 加入新队列
               queue_tail[p->queue_level] = (queue_tail[p->queue_level] + 1) % NPROC; // 更新新队列尾指针
             }
@@ -182,8 +182,9 @@ scheduler(void)
     
   }
 }
-
-修改yield函数 其作用是当当前运行的进程时间片用完,或者某个进程主动让出 CPU 时,它会将当前进程置为 RUNNABLE 状态,并触发调度器来选择下一个合适的进程运行。在多级反馈队列(MLFQ)调度中,yield 需要考虑进程所在的队列级别,进程的时间片以及是否需要提升或降级队列。 修改后的yield函数如下:
+```
+修改yield函数 其作用是当当前运行的进程时间片用完,或者某个进程主动让出 CPU 时,它会将当前进程置为 RUNNABLE 状态,并触发调度器来选择下一个合适的进程运行。在多级反馈队列(MFQ)调度中,yield 需要考虑进程所在的队列级别,进程的时间片以及是否需要提升或降级队列。 
+修改后的yield函数如下:
 ```c
 
 yield(void)
@@ -216,12 +217,37 @@ yield(void)
         queue_tail[p->queue_level] = (queue_tail[p->queue_level] + 1) % NPROC;  // 更新尾指针
         mlfq[p->queue_level][head] = 0;  // 将当前队列的位置清空
         queue_head[p->queue_level] = (queue_head[p->queue_level] + 1) % NPROC;  // 更新头指针
+        //如果没有此行 进程的time_used就不会在每次yield被调用时递增  从而导致进程的时间片将永远不会增加以及进程无法被降级
+        p->time_used++;  // 如果时间片未用完,增加已用时间
     }
   sched();
   release(&p->lock);
 }
 ```
-
+下面简要介绍一下进程调度过程的工作原理
+##### 1、进程初始化
+当操作系统启动时,调度器 scheduler() 会首先执行。在一个多级反馈队列系统中,多个队列会被初始化,每个队列具有不同的优先级和时间片长度。进程会根据其优先级被放入相应的队列。
+##### 2、fork() 创建子进程:
+当父进程调用 fork() 时,会创建一个新的子进程。父进程和子进程几乎完全相同,但它们是独立的进程,拥有各自的进程控制块(PCB),并有不同的进程 ID 和状态。
+在 fork() 调用后,父进程会继续执行自己的任务,而子进程通常会执行不同的任务(如果它接下来调用 exec()),但也可以通过调用 yield() 或等待调度来进入可运行状态。
+##### 3、调度过程 (scheduler()):
+在任何一个进程执行时,它都可能会被调度器 scheduler() 选中。scheduler() 会遍历各个优先级队列(MFQ 中的队列),查找一个可运行的进程。
+一旦选择了一个进程,调度器会将其状态设置为 RUNNING,并将进程的上下文切换到该进程。此时,进程开始执行。
+##### 4、进程时间片用尽时调用 yield():
+当进程的时间片耗尽时,进程会调用 yield() 来主动让出 CPU 使用权。
+yield() 会先将进程的状态设置为 RUNNABLE,然后将进程放回到其所在的队列中(可能是当前队列,也可能是降级到一个更低优先级的队列)。
+调用 yield() 后,scheduler() 会再次被调用,选择下一个进程执行。
+##### 5、yield() 在时间片内使用:
+如果进程在时间片未用完时执行 yield(),它会将自己重新加入队列尾部,并等待调度器选择下一个要执行的进程。
+这个过程中,yield() 主要用来在某些情况下主动放弃 CPU 使用权,避免饥饿现象或者让其他进程有机会运行。
+##### 6、时间片结束后,进程状态更新:
+当一个进程的时间片用尽时,yield() 函数会将其状态恢复为 RUNNABLE,并检查它是否已经超过当前队列的时间片。
+如果时间片用完且进程未完成,它将被降级到一个更低优先级的队列。之后,它会被放回相应队列中,等待调度器的选择。
+##### 7、调度器选择下一个进程:
+在每次 yield() 或其他事件导致当前进程放弃 CPU 后,scheduler() 会重新选择下一个 RUNNABLE 状态的进程。
+进程调度时,scheduler() 会考虑进程的优先级(MLFQ 的队列等级),并决定哪个队列中的进程将被选择。调度器会先选择高优先级队列中的进程,确保优先级高的进程能更多地获得 CPU 时间。
+##### 8、处理新进程(fork() 创建新进程):
+任何时候,如果父进程调用 fork(),一个新的子进程会被创建。新创建的子进程会被初始化并被放入某个队列(根据其优先级)。这个子进程会成为一个新的候选进程,准备进入调度队列,等待调度器选择。
 
 ### 2.2 文件管理系统
 
-- 
GitLab