|
|
@@ -1,17 +1,39 @@
|
|
|
#include "thread.h"
|
|
|
#include "../lib/string.h"
|
|
|
#include "../kernel/memory.h"
|
|
|
+#include "../kernel/interrupt.h"
|
|
|
+#include "../kernel/debug.h"
|
|
|
+#include "../lib/kernel/list.h"
|
|
|
+#include "../lib/kernel/print.h"
|
|
|
|
|
|
#define PG_SIZE 4096
|
|
|
|
|
|
+struct task_struct *main_thread; // 主线程 PCB
|
|
|
+struct list thread_ready_list; // 就绪队列
|
|
|
+struct list thread_all_list; // 所有任务队列
|
|
|
+static struct list_elem *thread_tag; // 用于保存队列中的线程节点
|
|
|
+
|
|
|
+extern void switch_to(struct task_struct *cur, struct task_struct *next);
|
|
|
+
|
|
|
+// 获取当前线程的 PCB 指针
|
|
|
+struct task_struct *running_thread()
|
|
|
+{
|
|
|
+ uint32_t esp;
|
|
|
+ asm("mov %%esp, %0" : "=g"(esp));
|
|
|
+ // 取 esp 整数部分作为 PCB 的指针
|
|
|
+ return (struct task_struct *)((uintptr_t)esp & 0xfffff000);
|
|
|
+}
|
|
|
+
|
|
|
// 由 kernel_thread 去执行 function(func_arg)
|
|
|
static void kernel_thread(thread_func *function, void *func_arg)
|
|
|
{
|
|
|
+ // 执行 function 前开中断,避免后面的时钟中断被屏蔽,而无法调度其它线程
|
|
|
+ intr_enable();
|
|
|
function(func_arg);
|
|
|
}
|
|
|
|
|
|
// 初始化线程栈 thread_stack
|
|
|
-void thread_create(struct task_struct *pthread, thread_func function, void *func_arg)
|
|
|
+void thread_stack_create(struct task_struct *pthread, thread_func function, void *func_arg)
|
|
|
{
|
|
|
// 先预留中断栈空间
|
|
|
pthread->self_kstack -= sizeof(struct intr_stack);
|
|
|
@@ -28,13 +50,25 @@ void thread_create(struct task_struct *pthread, thread_func function, void *func
|
|
|
// 初始化线程基本信息
|
|
|
void init_thread(struct task_struct *pthread, char *name, int prio)
|
|
|
{
|
|
|
- _memset(pthread, 0, sizeof(*pthread));
|
|
|
+ _memset(pthread, 0, sizeof(*pthread)); // 全部置为 0
|
|
|
_strcpy(pthread->name, name);
|
|
|
- pthread->status = TASK_RUNNING;
|
|
|
- pthread->priority = prio;
|
|
|
- // self_kstack是线程自己在内核态下使用的栈顶地址
|
|
|
- pthread->self_kstack = (uint32_t *)((uintptr_t)pthread + PG_SIZE);
|
|
|
- pthread->stack_magic = 0x19940625; // 自定义的魔数
|
|
|
+
|
|
|
+ // 线程状态, 目前只有两种状态: TASK_RUNNING, TASK_READY
|
|
|
+ if (pthread == main_thread)
|
|
|
+ { // 主线程,把 main 也封装成一个线程,并且它一直是运行的,所以是 TASK_RUNNING
|
|
|
+ pthread->status = TASK_RUNNING;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ pthread->status = TASK_READY;
|
|
|
+ }
|
|
|
+
|
|
|
+ pthread->self_kstack = (uint32_t *)((uintptr_t)pthread + PG_SIZE); // self_kstack是线程自己在内核态下使用的栈顶地址
|
|
|
+ pthread->priority = prio; // 线程优先级
|
|
|
+ pthread->ticks = prio; // 嘀嗒数
|
|
|
+ pthread->elapsed_ticks = 0; // 线程已执行的时间嘀嗒数
|
|
|
+ pthread->pgdir = NULL; // 进程自己页表的虚拟地址, 如果是线程则为 NULL
|
|
|
+ pthread->stack_magic = 0x19940625; // 自定义的魔数
|
|
|
}
|
|
|
|
|
|
// 创建优先级为 prio 的线程, 线程名为 name, 线程所执行的函数是 function(func_arg)
|
|
|
@@ -43,12 +77,68 @@ struct task_struct *thread_start(char *name, int prio, thread_func function, voi
|
|
|
// pcb 线程控制块
|
|
|
struct task_struct *thread = get_kernel_pages(1); // 申请一页内存(内核空间)做为PCB
|
|
|
init_thread(thread, name, prio); // 初始化线程基本信息
|
|
|
- thread_create(thread, function, func_arg); // 初始化线程栈 thread_stack
|
|
|
+ thread_stack_create(thread, function, func_arg); // 初始化线程栈 thread_stack
|
|
|
+
|
|
|
+ ASSERT(!elem_find(&thread_ready_list, &thread->general_tag)); // 保证加入就绪队列的线程不在队列中
|
|
|
+ list_append(&thread_ready_list, &thread->general_tag); // 加入就绪队列
|
|
|
|
|
|
- // ret 指令的作用是弹出栈顶的数据到 eip 寄存器,然后跳转到 eip 寄存器的地址执行
|
|
|
- // 此时栈顶数据是kthread_stack->eip 的值 kernel_thread。
|
|
|
- // 因此,在执行 ret 后,会跳转到 kernel_thread 函数执行。
|
|
|
- asm volatile("movl %0, %%esp; pop %%ebp; pop %%ebx; pop %%edi; pop %%esi; ret" : : "g"(thread->self_kstack) : "memory");
|
|
|
+ ASSERT(!elem_find(&thread_all_list, &thread->all_list_tag)); // 保证加入所有线程队列的线程不在队列中
|
|
|
+ list_append(&thread_all_list, &thread->all_list_tag); // 加入所有线程队列
|
|
|
|
|
|
return thread;
|
|
|
+}
|
|
|
+
|
|
|
+// 将 kernel 中的 main 函数封装成线程
|
|
|
+static void make_main_thread(void)
|
|
|
+{
|
|
|
+ // 因为 main 函数也是一个线程,但它被单独执行,所以要单独处理
|
|
|
+ // 因为 main 函数是操作系统的第一个函数,所以它的 PCB 也是第一个
|
|
|
+ // ! 在 loader.S 中进入内核时 mov esp, 0xc009f000,
|
|
|
+ // ! 所以 main 函数的栈顶是 0xc009f000, PCB 是 0xc009e000
|
|
|
+ main_thread = running_thread();
|
|
|
+ init_thread(main_thread, "main", 31); // 31 是最高优先级
|
|
|
+
|
|
|
+ // main 函数是当前线程,当前线程不在 thread_ready_list 中
|
|
|
+ ASSERT(!elem_find(&thread_all_list, &main_thread->all_list_tag));
|
|
|
+ list_append(&thread_all_list, &main_thread->all_list_tag);
|
|
|
+}
|
|
|
+
|
|
|
+/// @brief 将当前线程换下处理器,并在就绪队列中找出下个可运行的程序,换上处理器
|
|
|
+///! 此过程由 时钟中断 来调用
|
|
|
+void schedule()
|
|
|
+{
|
|
|
+ ASSERT(intr_get_status() == INTR_OFF);
|
|
|
+
|
|
|
+ struct task_struct *cur = running_thread();
|
|
|
+ if (cur->status == TASK_RUNNING)
|
|
|
+ {
|
|
|
+ // 若此线程只是时间片到了,将其加入到就绪队列尾
|
|
|
+ ASSERT(!elem_find(&thread_ready_list, &cur->general_tag));
|
|
|
+ list_append(&thread_ready_list, &cur->general_tag);
|
|
|
+ cur->ticks = cur->priority; // 重置 ticks
|
|
|
+ cur->status = TASK_READY;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // 若此线程需要某事件发生后才能继续上 cpu 运行, 不需要加入队列
|
|
|
+ }
|
|
|
+ // todo: 暂未实现 idle 线程,暂用 assertion 来保障
|
|
|
+ ASSERT(!list_empty(&thread_ready_list));
|
|
|
+ thread_tag = NULL; // thread_tag 清空
|
|
|
+ thread_tag = list_pop(&thread_ready_list); // 弹出队列中的第一个就绪线程
|
|
|
+ struct task_struct *next = elem2entry(struct task_struct, general_tag, thread_tag);
|
|
|
+ // 方法 2: PCB 在自然页的起始地址, 所以 pcb 地址=0xfffff000&(&(PCB.general_tag))
|
|
|
+
|
|
|
+ next->status = TASK_RUNNING;
|
|
|
+ switch_to(cur, next); // 切换线程
|
|
|
+}
|
|
|
+
|
|
|
+// 初始化线程环境
|
|
|
+void thread_init(void)
|
|
|
+{
|
|
|
+ put_str("thread_init start\n");
|
|
|
+ list_init(&thread_ready_list);
|
|
|
+ list_init(&thread_all_list);
|
|
|
+ make_main_thread(); // 将当前 main 函数创建为线程
|
|
|
+ put_str("thread_init end\n");
|
|
|
}
|