实验15.多线程调度

avatar
作者
猴君
阅读量:0

简介

实验.多线程调度

内核线程

    1.在时钟中断函数中处理中,减少当前线程pcb的tick,tick为0则启动调度     2.调度,把当前线程pcb放入就绪对立队尾,把就绪线程队首拿出来执行 

主要代码

引导

省略 

内核

list.h

#ifndef __LIB_KERNEL_LIST_H #define __LIB_KERNEL_LIST_H  #include "global.h" #include "stdint.h"  /// 拿到menber相当于struct_type的偏移 /// struct_type 结构体 // member 结构体的属性 #define offset(struct_type, member) (int)(&((struct_type*)0)->member)  ///  返回这个属性地址的结构体地址 /// struct_type 结构体 /// struct_member_name 结构体属性 /// elem_ptr 结构体的属性的地址 #define elem2entry(struct_type, struct_member_name, elem_ptr) \     (struct_type*)((int)elem_ptr - offset(struct_type, struct_member_name))  /// @brief 链表元素 struct list_elem {     struct list_elem* prev;  // 前躯元素     struct list_elem* next;  // 后继元素 };  /// @brief 双向链表 struct list {     // 这两个就如同两堵墙,在墙中间的才是元素     struct list_elem head;  // 队首 注意: 第一个元素是head.next!!!     struct list_elem tail;  // 队尾 };  /// @brief 自定义函数类型function typedef bool(function)(struct list_elem*, int arg);  void list_init(struct list*); void list_insert_before(struct list_elem* before, struct list_elem* elem); void list_push(struct list* plist, struct list_elem* elem); void list_iterate(struct list* plist); void list_append(struct list* plist, struct list_elem* elem); void list_remove(struct list_elem* pelem); struct list_elem* list_pop(struct list* plist); bool list_empty(struct list* plist); uint32_t list_len(struct list* plist); struct list_elem* list_traversal(struct list* plist, function func, int arg); bool elem_find(struct list* plist, struct list_elem* obj_elem); #endif  

list.c

#include "list.h" #include "interrupt.h"  /// @brief 初始化双向链表 /// @param list 链表指针 void list_init(struct list* list) {     list->head.prev = NULL;     list->head.next = &list->tail;     list->tail.prev = &list->head;     list->tail.next = NULL; }  /// @brief 把链表元素elem插入在元素before之前 /// @param before 链表元素 /// @param elem  链表元素 void list_insert_before(struct list_elem* before, struct list_elem* elem) {     // 关闭中断     enum intr_status old_status = intr_disable();      // 将before前驱元素的后继元素更新为elem, 暂时使before脱离链表     before->prev->next = elem;     // 更新elem自己的前驱结点为before的前驱     elem->prev = before->prev;     // 更新elem自己的后继结点为before, 于是before又回到链表     elem->next = before;     // 更新before的前驱结点为elem     before->prev = elem;      // 恢复原来的中断状态     intr_set_status(old_status); }  /// @brief 添加元素到列表队首,类似栈push操作 /// @param plist 队列 /// @param elem 元素 void list_push(struct list* plist, struct list_elem* elem) {     list_insert_before(plist->head.next, elem); }  /// @brief 追加元素到链表队尾 /// @param plist /// @param elem void list_append(struct list* plist, struct list_elem* elem) {     list_insert_before(&plist->tail, elem); }  /// @brief 使元素pelem脱离链表 /// @param pelem 元素 void list_remove(struct list_elem* pelem) {     // 关闭中断     enum intr_status old_status = intr_disable();      pelem->prev->next = pelem->next;     pelem->next->prev = pelem->prev;      // 恢复中断状态     intr_set_status(old_status); }  /// @brief 将链表第一个元素弹出并返回,类似栈的pop操作 /// @param list 双向链表 struct list_elem* list_pop(struct list* plist) {     struct list_elem* elem = plist->head.next;     list_remove(elem);     return elem; }  /// @brief 从链表中查找元素obj_elem, /// @param plist /// @param obj_elem /// @return 成功时返回true,失败时返回false bool elem_find(struct list* plist, struct list_elem* obj_elem) {     struct list_elem* elem = plist->head.next;     while (elem != &plist->tail) {         if (elem == obj_elem) { return true; }         elem = elem->next;     }     return false; }  /// @brief 遍历列表内所有元素,逐个判断是否有符合条件的元素 /// @param list 链表 /// @param function 判断函数,返回true位满足条件,false位不满足条件 /// @param int 参数个数 struct list_elem* list_traversal(struct list* plist, function func, int arg) {     struct list_elem* elem = plist->head.next;     if (list_empty(plist)) return NULL;      while (elem != &plist->tail) {         if (func(elem, arg)) {  // func返回ture则认为该元素在回调函数中符合条件,直接返回             return elem;         }         elem = elem->next;     }     return NULL; }  /// @brief 返回链表长度 /// @param plist 链表 /// @return 链表长度 uint32_t list_len(struct list* plist) {     struct list_elem* elem = plist->head.next;     uint32_t length = 0;     while (elem != &plist->tail) {         length++;         elem = elem->next;     }     return length; }  /// @brief 判断链表是否为空 /// @param plist 链表 /// @return 空时返回true,否则返回false bool list_empty(struct list* plist) {  // 判断队列是否为空     return (plist->head.next == &plist->tail ? true : false); }  

switch.s

; switch.s ; 时间: 2024-07-25 ; 来自: ccj ; 描述: 定义切换pcb的函数  ;---切换pcb(cur_pcb,next_pcb) begin--- section .text global switch_to switch_to:     ; 保存当前环境     push esi     push edi     push ebx     push ebp      ; 保存当前环境到cur_pcb     mov eax, [esp + 20] ; 得到栈中的参数cur_pcb, cur_pcb = [esp+20]     mov [eax], esp      ; 保存esp到pcb的 self_kstack =                         ; 结果 curpub.self_kstack = esp      ; 切换当前环境到nex_pcb     mov eax, [esp + 24] ; 得到栈中的参数next_pcb, next_pcb = [esp+24]     mov esp, [eax]      ; esp = next_pcb.self_kstack      ; 恢复当前环境     pop ebp     pop ebx     pop edi     pop esi      ret ;---切换pcb(cur_pcb,next_pcb) end--- 

thread.h

#ifndef __THREAD_THREAD_H #define __THREAD_THREAD_H #include "stdint.h"  typedef void thread_func(void*);  /// @brief 进程或线程的状态 enum task_status {     TASK_RUNNING,  //     TASK_READY,     TASK_BLOCKED,     TASK_WAITING,     TASK_HANGING,     TASK_DIED };  /// @brief 中断栈,中断发生时,保护程序(线程或进程)的上下文环境 /// 进程或线程被外部中断或软中断打断时,会按照此结构压入上下文寄存器 /// kernel.s中intr_exit的出栈操作是此结构的逆操作 /// 中断栈在pcb的最顶端 struct intr_stack {     // kernel.s 宏VECTOR中push %1压入的中断号     uint32_t vec_no;      // kernel.s 中pushad压入的寄存器     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;      // kernel.s 中保存上下文环境压入的寄存器     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; };  /// @brief 线程栈 保存线程的上下文 /// ABI规则:主调函数调用被调函数,被调函数一定要保存ebp、ebx、edi、esi、esp /// eip:线程下一步 struct thread_stack {     uint32_t ebp;     uint32_t ebx;     uint32_t edi;     uint32_t esi;      // 线程第一次执行时,eip指向待调用的函数kernel_thread     // 其它时候,eip是指向switch_to的返回地址     void (*eip)(thread_func* func, void* func_arg);      // 以下仅供第一次被调度上cpu时使用     void(*unused_retaddr);  // 占位置充数为返回地址     thread_func* function;  // 由Kernel_thread所调用的函数名     void* func_arg;         // 由Kernel_thread所调用的函数所需的参数 };  /// @brief 进程或线程的pcb process control block 4096字节 /// 一个pcb包含1个中断栈,1个线程栈, struct task_struct {     uint32_t* self_kstack;    // pcb中线程栈的地址     enum task_status status;  // 状态     uint8_t priority;         // 线程优先级     char name[16];            //     uint32_t stack_magic;     // pcb魔数,用于检测栈的溢出 };  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.c

// 文件: thread.c // 时间: 2024-07-23 // 来自: ccj // 描述: 申请了一个物理页做pcb,在pcb中定义了下一步kernel_thread,然后切换到该pcb的环境,运行  #include "thread.h" #include "stdint.h" #include "string.h" #include "global.h" #include "memory.h"  #define PG_SIZE 4096  /// @brief 由kernel_thread去执行function(func_arg) /// @param function 函数指针 /// @param func_arg 函数参数 static void kernel_thread(thread_func* function, void* func_arg) { function(func_arg); }  /// @brief 初始化pcb,将待执行的函数和参数放到pcb中相应的位置 /// @param pthread pcb /// @param function 待执行的函数 /// @param func_arg 函数参数 void thread_create(struct task_struct* pthread, thread_func function, void* func_arg) {     // 先预留中断使用栈的空间,可见thread.h中定义的结构     pthread->self_kstack -= sizeof(struct intr_stack);      // 再留出线程栈空间     pthread->self_kstack -= sizeof(struct thread_stack);     // 此时的self_kstack看作线程栈的首地址     struct thread_stack* kthread_stack = (struct thread_stack*)pthread->self_kstack;     kthread_stack->eip = kernel_thread;  // 指向kernel_thread     kthread_stack->function = function;  // 设置函数     kthread_stack->func_arg = func_arg;  // 设置参数     kthread_stack->ebp = kthread_stack->ebx = kthread_stack->esi = kthread_stack->edi = 0; }  /// @brief 初始化线程基本信息 /// @param pthread pcb /// @param name 线程名 /// @param prio 优先级 void init_thread(struct task_struct* pthread, char* name, int prio) {     // 清0     memset(pthread, 0, sizeof(*pthread));      // 设置名字、状态、优先级     strcpy(pthread->name, name);     pthread->status = TASK_RUNNING;     pthread->priority = prio;      // self_kstack是线程自己在内核态下使用的栈顶地址     pthread->self_kstack = (uint32_t*)((uint32_t)pthread + PG_SIZE);     pthread->stack_magic = 0x19870916;  // 自定义的魔数 }  /// @brief 创建一优先级为prio的线程,线程名为name,线程所执行的函数是function(func_arg) /// @param name 线程名 /// @param prio 优先级 /// @param function 线程要执行的函数 /// @param void* 函数参数 struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg) {     // 申请1个物理页来存放pcb     struct task_struct* thread = get_kernel_pages(1);      // 初始化pcb     init_thread(thread, name, prio);      // 创建线程     thread_create(thread, function, func_arg);      // 切换到thread的上下文,并执行thread     asm volatile("movl %0, %%esp; pop %%ebp; pop %%ebx; pop %%edi; pop %%esi; ret"                  :                  : "g"(thread->self_kstack)                  : "memory");     // movl %0, %%esp;     // 把thread->self_kstack的值给esp      // pop %%ebp; pop %%ebx; pop %%edi; pop %%esi;     // 从thread->self_kstack中把这几个属性值给到寄存器      // ret     // 由于堆栈和这些关键寄存器已被设置为新线程的值,这会导致执行流跳转到新线程的上下文中继续执行     return thread; }  }  /// @brief 初始化线程环境 /// @param void thread_init(void) {     put_str("[thread] thread_init start\n");      list_init(&thread_ready_list);     list_init(&thread_all_list);      // 将当前main函数创建为线程     make_main_thread();     put_str("[thread] thread_init done\n"); } 

timer.h

#ifndef __DEVICE_TIME_H #define __DEVICE_TIME_H  #include "stdint.h"  void timer_init(void); #endif  

timer.c

// 文件: timer.c // 时间: 2024-07-23 // 来自: ccj // 描述: 调快时钟,调快时钟、注册时钟中断来调度线程  #include "timer.h" #include "io.h" #include "print.h" #include "interrupt.h" #include "thread.h" #include "debug.h"  #define IRQ0_FREQUENCY   100 #define INPUT_FREQUENCY  1193180 #define COUNTER0_VALUE   INPUT_FREQUENCY / IRQ0_FREQUENCY #define CONTRER0_PORT    0x40 #define COUNTER0_NO      0 #define COUNTER_MODE     2 #define READ_WRITE_LATCH 3 #define PIT_CONTROL_PORT 0x43  uint32_t ticks;  // 中断开始,开始计数  /* 把操作的计数器counter_no、读写锁属性rwl、计数器模式counter_mode写入模式控制寄存器并赋予初始值counter_value  */ static void frequency_set(uint8_t counter_port,                           uint8_t counter_no,                           uint8_t rwl,                           uint8_t counter_mode,                           uint16_t counter_value) {     /* 往控制字寄存器端口0x43中写入控制字 */     outb(PIT_CONTROL_PORT, (uint8_t)(counter_no << 6 | rwl << 4 | counter_mode << 1));     /* 先写入counter_value的低8位 */     outb(counter_port, (uint8_t)counter_value);     /* 再写入counter_value的高8位 */     outb(counter_port, (uint8_t)counter_value >> 8); }  /* 时钟的中断处理函数 */ static void intr_timer_handler(void) {     struct task_struct* cur_thread = running_thread();      ASSERT(cur_thread->stack_magic == 0x19870916);  // 检查栈是否溢出      cur_thread->elapsed_ticks++;  // 记录此线程占用的cpu时间嘀     ticks++;                      // 记录总时钟数      if (cur_thread->ticks == 0) {  // 若进程时间片用完就开始调度新的进程上cpu         schedule();     } else {  // 将当前进程的时间片-1         cur_thread->ticks--;     } }  /* 初始化PIT8253 */ void timer_init(void) {     put_str("[timer] timer_init start\n");      /* 设置8253的定时周期,也就是发中断的周期 */     frequency_set(CONTRER0_PORT, COUNTER0_NO, READ_WRITE_LATCH, COUNTER_MODE, COUNTER0_VALUE);      // 注册时钟中断,用来调度线程     register_handler(0x20, intr_timer_handler);     put_str("[timer] timer_init done\n"); }  

interupt.h

#ifndef __KERNEL_INTERRUPT_H #define __KERNEL_INTERRUPT_H  #include "stdint.h"  typedef void* intr_handler;  void idt_init(void);  /// @brief 中断机制状态 enum intr_status {     INTR_OFF,  // 中断关闭     INTR_ON    // 中断打开 }; enum intr_status intr_enable(void); enum intr_status intr_disable(void); enum intr_status intr_get_status(void); enum intr_status intr_set_status(enum intr_status); void register_handler(uint8_t vector_no, intr_handler function);  #endif  

interupt.c

// 文件: timer.c // 时间: 2024-07-23 // 来自: ccj // 描述: 调快时钟,调快时钟、注册时钟中断来调度线程  #include "timer.h" #include "io.h" #include "print.h" #include "interrupt.h" #include "thread.h" #include "debug.h"  #define IRQ0_FREQUENCY   100 #define INPUT_FREQUENCY  1193180 #define COUNTER0_VALUE   INPUT_FREQUENCY / IRQ0_FREQUENCY #define CONTRER0_PORT    0x40 #define COUNTER0_NO      0 #define COUNTER_MODE     2 #define READ_WRITE_LATCH 3 #define PIT_CONTROL_PORT 0x43  uint32_t ticks;  // 中断开始,开始计数  /* 把操作的计数器counter_no、读写锁属性rwl、计数器模式counter_mode写入模式控制寄存器并赋予初始值counter_value  */ static void frequency_set(uint8_t counter_port,                           uint8_t counter_no,                           uint8_t rwl,                           uint8_t counter_mode,                           uint16_t counter_value) {     /* 往控制字寄存器端口0x43中写入控制字 */     outb(PIT_CONTROL_PORT, (uint8_t)(counter_no << 6 | rwl << 4 | counter_mode << 1));     /* 先写入counter_value的低8位 */     outb(counter_port, (uint8_t)counter_value);     /* 再写入counter_value的高8位 */     outb(counter_port, (uint8_t)counter_value >> 8); }  /* 时钟的中断处理函数 */ static void intr_timer_handler(void) {     struct task_struct* cur_thread = running_thread();      ASSERT(cur_thread->stack_magic == 0x19870916);  // 检查栈是否溢出      cur_thread->elapsed_ticks++;  // 记录此线程占用的cpu时间嘀     ticks++;                      // 记录总时钟数      if (cur_thread->ticks == 0) {  // 若进程时间片用完就开始调度新的进程上cpu         schedule();     } else {  // 将当前进程的时间片-1         cur_thread->ticks--;     } }  /* 初始化PIT8253 */ void timer_init(void) {     put_str("[timer] timer_init start\n");      /* 设置8253的定时周期,也就是发中断的周期 */     frequency_set(CONTRER0_PORT, COUNTER0_NO, READ_WRITE_LATCH, COUNTER_MODE, COUNTER0_VALUE);      // 注册时钟中断,用来调度线程     register_handler(0x20, intr_timer_handler);     put_str("[timer] timer_init done\n"); }  

init.h

#ifndef __KERNEL_INIT_H #define __KERNEL_INIT_H void init_all(void); #endif 

init.c

// 文件: init.c // 时间: 2024-07-22 // 来自: ccj // 描述: 内核所有初始化操作  #include "init.h" #include "print.h" #include "interrupt.h" #include "timer.h" #include "memory.h" #include "thread.h"  /// @brief 内核所有初始化 void init_all() {     put_str("init all\n");      idt_init();    // 初始化中断     timer_init();  // 调快时钟、注册时钟中断来调度线程     mem_init();  // 初始化内存管理系统     thread_init(); }  

main.c

// 文件: main.c // 时间: 2024-07-19 // 来自: ccj // 描述: 内核从此处开始  #include "print.h" #include "init.h" #include "thread.h" #include "interrupt.h"  void k_thread_a(void*); void k_thread_b(void*);  int main(void) {     put_str("I am kernel\n");      init_all();      thread_start("k_thread_a", 4, k_thread_a, "argA\n");     thread_start("k_thread_a", 16, k_thread_b, "argB\n");      intr_enable();  // 打开中断,使时钟中断起作用     while (1) { put_str("Main\n"); };     return 0; }  void k_thread_a(void* arg) {     char* para = arg;     while (1) { put_str(para); } }  void k_thread_b(void* arg) {     char* para = arg;     while (1) { put_str(para); } }  

编译

省略 

运行

start.sh

#/bin/bash # 文件: start.sh # 描述: 启动bochs # 时间: 2024-07-19 # 来自: ccj   set -x  bin/bochs -f bochsrc.disk 

在这里插入图片描述

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!