Sfoglia il codice sorgente

在内核空间实现线程

simon 10 mesi fa
parent
commit
fb678cdf75
4 ha cambiato i file con 172 aggiunte e 6 eliminazioni
  1. 19 4
      kernel/main.c
  2. 5 2
      makefile
  3. 54 0
      thread/thread.c
  4. 94 0
      thread/thread.h

+ 19 - 4
kernel/main.c

@@ -2,6 +2,9 @@
 #include "debug.h"
 #include "init.h"
 #include "memory.h"
+#include "../thread/thread.h"
+
+void thread_a_func(void *);
 
 int main(void)
 {
@@ -10,12 +13,24 @@ int main(void)
     // asm volatile("sti"); // 使能中断
     // ASSERT(1 == 2);
 
-    void *addr = get_kernel_pages(4);
-    put_str("\n  get_kernel_page start vaddr is ");
-    put_int((uintptr_t)addr);
-    put_str("\n");
+    // void *addr = get_kernel_pages(4);
+    // put_str("\n  get_kernel_page start vaddr is ");
+    // put_int((uintptr_t)addr);
+    // put_str("\n");
+
+    thread_start("thread_a", 31, thread_a_func, "argA ");
 
     while (1)
         ;
     return 0;
+}
+
+void thread_a_func(void *arg)
+{
+    // 用 void 来表示通用类型,这样就可以传递任意类型的参数
+    char *para = arg;
+    while (1)
+    {
+        put_str(para);
+    }
 }

+ 5 - 2
makefile

@@ -5,7 +5,7 @@ DIS_IMG = ./hd30M.img
 AS = nasm
 CC = i386-elf-gcc
 LD = i386-elf-ld
-LIB = -I lib/ -I lib/kernel -I lib/user/ -I kernel/ -I device/
+LIB = -I lib/ -I lib/kernel -I lib/user/ -I kernel/ -I device/ -I thread/
 BOOTLIB = -I boot/include/
 ASFLAGS = -f elf
 CFLAGS = -Wall $(LIB) -c -fno-builtin -W -Wstrict-prototypes -Wmissing-prototypes
@@ -13,7 +13,7 @@ LDFLAGS = -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_BIN_DIR)/kernel.map
 OBJS = $(BUID_O_DIR)/main.o $(BUID_O_DIR)/init.o $(BUID_O_DIR)/interrupt.o  \
 	$(BUID_O_DIR)/timer.o $(BUID_O_DIR)/kernel.o $(BUID_O_DIR)/print.o   \
 	$(BUID_O_DIR)/debug.o $(BUID_O_DIR)/string.o $(BUID_O_DIR)/bitmap.o  \
-	$(BUID_O_DIR)/memory.o
+	$(BUID_O_DIR)/memory.o $(BUID_O_DIR)/thread.o
 
 ################################ C 代码编译 ################################
 $(BUID_O_DIR)/main.o: kernel/main.c lib/kernel/print.h lib/stdint.h kernel/init.h
@@ -40,6 +40,9 @@ $(BUID_O_DIR)/bitmap.o: lib/kernel/bitmap.c lib/kernel/bitmap.h lib/stdint.h lib
 $(BUID_O_DIR)/memory.o: kernel/memory.c kernel/memory.h lib/stdint.h lib/kernel/bitmap.h lib/string.h lib/kernel/print.h
 	$(CC) $(CFLAGS) $< -o $@
 
+$(BUID_O_DIR)/thread.o: thread/thread.c thread/thread.h
+	$(CC) $(CFLAGS) $< -o $@
+
 ################################ 汇编代码编译 ################################
 $(BUID_O_DIR)/kernel.o: kernel/kernel.S
 	$(AS) $(ASFLAGS) $< -o $@

+ 54 - 0
thread/thread.c

@@ -0,0 +1,54 @@
+#include "thread.h"
+#include "../lib/string.h"
+#include "../kernel/memory.h"
+
+#define PG_SIZE 4096
+
+// 由 kernel_thread 去执行 function(func_arg)
+static void kernel_thread(thread_func *function, void *func_arg)
+{
+    function(func_arg);
+}
+
+// 初始化线程栈 thread_stack
+void thread_create(struct task_struct *pthread, thread_func function, void *func_arg)
+{
+    // 先预留中断栈空间
+    pthread->self_kstack -= sizeof(struct intr_stack);
+    // 再预留线程栈空间,线程栈位于中断栈顶
+    pthread->self_kstack -= sizeof(struct thread_stack);
+
+    struct thread_stack *kthread_stack = (struct thread_stack *)pthread->self_kstack;
+    kthread_stack->eip = kernel_thread;
+    kthread_stack->function = function;
+    kthread_stack->func_arg = func_arg;
+    kthread_stack->ebp = kthread_stack->ebx = kthread_stack->edi = kthread_stack->esi = 0;
+}
+
+// 初始化线程基本信息
+void init_thread(struct task_struct *pthread, char *name, int prio)
+{
+    _memset(pthread, 0, sizeof(*pthread));
+    _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; // 自定义的魔数
+}
+
+// 创建优先级为 prio 的线程, 线程名为 name, 线程所执行的函数是 function(func_arg)
+struct task_struct *thread_start(char *name, int prio, thread_func function, void *func_arg)
+{
+    // pcb 线程控制块
+    struct task_struct *thread = get_kernel_pages(1); // 申请一页内存(内核空间)做为PCB
+    init_thread(thread, name, prio);                  // 初始化线程基本信息
+    thread_create(thread, function, func_arg);        // 初始化线程栈 thread_stack
+
+    // 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");
+
+    return thread;
+}

+ 94 - 0
thread/thread.h

@@ -0,0 +1,94 @@
+#ifndef __THREAD_THREAD_H
+#define __THREAD_THREAD_H
+#include "../lib/stdint.h"
+
+// 通用函数类型,它将在很多线程函数中作为参数类型
+typedef void thread_func(void *);
+
+// 线程的状态
+enum task_status
+{
+    TASK_RUNNING,
+    TASK_READY,
+    TASK_BLOCKED,
+    TASK_WAITING,
+    TASK_HANGING,
+    TASK_DIED
+};
+
+/******************** 中断栈 intr_stack **********************************
+ * 些结构用于中断发生时保护程序(线程或进程)的上下文环境:
+ * 进程或线程被外部中断或软中断打断时,需要保护程序(线程或进程)的上下文寄存器,
+ * kernel.S 中的 intr_exit 出栈操作是些结构的逆操作。
+ * 此栈在线程自己的内核栈中位置固定,所在页的最顶端。
+ ***********************************************************************/
+struct intr_stack
+{
+    uint32_t vec_no; // kernel.S 宏VECTOR中push %1压入的中断号
+    uint32_t edi;
+    uint32_t esi;
+    uint32_t ebp;
+    uint32_t esp_dummy; // 虽然pushad会把esp压入,但esp是不断变化的,所以会被popad忽略
+    uint32_t ebx;
+    uint32_t edx;
+    uint32_t ecx;
+    uint32_t eax;
+    uint32_t gs;
+    uint32_t fs;
+    uint32_t es;
+    uint32_t ds;
+
+    // 以下由cpu从低特权级进入高特权级时压入
+    uint32_t err_code; // err_code会被压入在eip之后
+    void (*eip)(void);
+    uint32_t cs;
+    uint32_t eflags;
+    void *esp;
+    uint32_t ss;
+};
+
+/********************* 线程栈thread_stack ********************************
+ * 线程自己的栈,用于存储线程中待执行的函数
+ * 此结构在线程自己的内核栈中位置不固定
+ * 仅用在 switch_to 时保存线程环境
+ * 实际位置取决于实际运行情况
+ ***********************************************************************/
+struct thread_stack
+{
+    // All registers on the Intel386 缸e global and thus visible to both a calling and a called function.
+    // Registers %ebp, %ebx, %edi, %esi, and %esp “ belong” to the calling function.
+    // In other words, a called function must preserve these registers' values for its caller.
+    // Remaining registers “ belong” to the called function.
+    // If a calling function wants to preserve such a register value across a function call,
+    // it must save the value in its local stack frame.
+    uint32_t ebp;
+    uint32_t ebx;
+    uint32_t edi;
+    uint32_t esi;
+
+    // 线程第一次执行时,eip指向待调用的函数kernel_thread
+    // 其他时候,指向switch_to的返回地址
+    void (*eip)(thread_func *func, void *func_arg);
+
+    // 以下仅供第一次被调度上cpu时使用
+    // 参数unused_ret只为占位置充数为返回地址
+    void(*unused_retaddr);
+    thread_func *function; // 由kernel_thread所调用的函数名
+    void *func_arg;        // 由kernel_thread所调用的函数所需的参数
+};
+
+// 进程或线程的pcb,程序控制块(process control block)
+struct task_struct
+{
+    uint32_t *self_kstack;   // 各内核线程都用自己的内核栈
+    enum task_status status; // 线程状态
+    uint8_t priority;        // 线程优先级
+    char name[16];           // 进程或线程的名字
+    uint32_t stack_magic;    // 用这串数字做栈的边界标记,用于检测栈的溢出
+};
+
+void thread_create(struct task_struct *pthread, thread_func function, void *func_arg);
+void init_thread(struct task_struct *pthread, char *name, int prio);
+struct task_struct *thread_start(char *name, int prio, thread_func function, void *func_arg);
+
+#endif // __THREAD_THREAD_H