简介
实验.多线程调度
内核线程
text
1.在时钟中断函数中处理中,减少当前线程pcb的tick,tick为0则启动调度
2.调度,把当前线程pcb放入就绪对立队尾,把就绪线程队首拿出来执行
主要代码
引导
省略
内核
list.h
c
#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
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
x86asm
; 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
c
#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
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
c
#ifndef __DEVICE_TIME_H
#define __DEVICE_TIME_H
#include "stdint.h"
void timer_init(void);
#endif
timer.c
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
c
#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
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
c
#ifndef __KERNEL_INIT_H
#define __KERNEL_INIT_H
void init_all(void);
#endif
init.c
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
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
shell
#/bin/bash
# 文件: start.sh
# 描述: 启动bochs
# 时间: 2024-07-19
# 来自: ccj
set -x
bin/bochs -f bochsrc.disk