实现内核线程
线程是啥,线程就是执行流,线程也好,进程也好,都是执行流,所谓执行流,就是正在执行的那些指令。线程依附于进程,因为线程没有自己的地址空间,线程是与进程共享对应进程的地址空间的。 线程的实现方式有两个,一个是用户线程,一个是内核线程,其中用户线程是用户进程自自己通过某种调度算法实现的线程,其内所有的线程共享用户进程的时间片,内核不知道这些线程,如果有一个线程被阻塞的话,就相当于阻塞了整个进程。内核线程则是在内核实现的线程,其由内核调度,其是会被单独分配时间片的。其与进程一起被内核调度器一起调度。
在内核空间实现线程
对于实现线程,主要就是需要切换,一开始是一直在main 函数中运行的,每开启一个线程,需要运行线程的话,由于只有一个cpu ,那肯定是需要切换的,对于如何切换,首先对于线程来说是有一个专门的数据结构pcb 来存储内核栈,寄存器映像,这样能够在进程上下文切换时,恢复状态了。 对于上下文切换,主要是在时钟中断的前提上来实现的,时钟中断会调用中断处理函数,当然这里也要压入寄存器,以保护上下文,之后在中断处理函数中会判断每个线程的时间片是否用完来判断是否进行调度,在调度函数中从就绪队列中拿出相乘的pcb ,之后调用switch_to 来切换到next 这个线程中去,其中switch_to 函数如下:
[bits 32]
section .text
global switch_to
switch_to:
;栈中此处是返回地址
push esi
push edi
push ebx
push ebp
mov eax, [esp + 20] ; 得到栈中的参数cur, cur = [esp+20]
mov [eax], esp ; 保存栈顶指针esp. task_struct的self_kstack字段,
; self_kstack在task_struct中的偏移为0,
; 所以直接往thread开头处存4字节便可。
;------------------ 以上是备份当前线程的环境,下面是恢复下一个线程的环境 ----------------
mov eax, [esp + 24] ; 得到栈中的参数next, next = [esp+24]
mov esp, [eax] ; pcb的第一个成员是self_kstack成员,用来记录0级栈顶指针,
; 用来上cpu时恢复0级栈,0级栈中保存了进程或线程所有信息,包括3级栈指针
pop ebp
pop ebx
pop edi
pop esi
ret ; 返回到上面switch_to下面的那句注释的返回地址,
; 未由中断进入,第一次执行时会返回到kernel_thread
这个纯粹用汇编写的swtich_to 函数就是精确的实现了切换,首先调用函数会先把参数先压栈,然后又压入返回地址,此时[esp+20],[esp+24] 便是得到了对应的参数,也就是线程的pcb ,由于pcb 的最开始的存储的数据就是内核栈,所以而内核栈在初始化线程的时候就可以知道存储的是线程栈的地址,所以此时通过互换栈指针栈切换为了对方的线程栈,而线程栈中存储着这几个寄存器以及要运行的函数(kernel_thread )地址和所需的参数,ret 指令就把正好把函数地址复制给eip ,同时又可以得到参数,最后就可以运行函数,也就是内核线程中想要运行的函数。
代码
以下都是我已经写到用户进程的那章的函数
thread.c
#include "thread.h"
#include "../lib/stdint.h"
#include "../lib/string.h"
#include "../kernel/global.h"
#include "../kernel/debug.h"
#include "../kernel/interrupt.h"
#include "../lib/kernel/print.h"
#include "../lib/kernel/memory.h"
#include "../lib/kernel/list.h"
#include "../user/process.h"
#define PG_SIZE 4096
struct task_struct* main_thread;
static struct list_elem* thread_tag;
struct list thread_ready_list;
struct list thread_all_list;
extern void switch_to(struct task_struct* cur, struct task_struct* next);
struct task_struct* running_thread() {
uint32_t esp;
asm ("mov %%esp, %0" : "=g" (esp));
return (struct task_struct*)(esp & 0xfffff000);
}
static void kernel_thread(thread_func* function, void* func_arg) {
intr_enable();
function(func_arg);
}
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->esi = kthread_stack->edi = 0;
}
void init_thread(struct task_struct* pthread, char* name, int prio) {
memset(pthread, 0, sizeof(*pthread));
strcpy(pthread->name, name);
if (pthread == main_thread) {
pthread->status = TASK_RUNNING;
} else {
pthread->status = TASK_READY;
}
pthread->self_kstack = (uint32_t*)((uint32_t)pthread + PG_SIZE);
pthread->priority = prio;
pthread->ticks = prio;
pthread->elapsed_ticks = 0;
pthread->pgdir = NULL;
pthread->stack_magic = 0x19870916;
put_str("init_thread done\n");
}
struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg) {
struct task_struct* thread = get_kernel_pages(1);
init_thread(thread, name, prio);
thread_create(thread, function, func_arg);
ASSERT(!elem_find(&thread_ready_list, &thread->general_tag));
list_append(&thread_ready_list, &thread->general_tag);
ASSERT(!elem_find(&thread_all_list, &thread->all_list_tag));
list_append(&thread_all_list, &thread->all_list_tag);
put_str("thread_start done\n");
return thread;
}
static void make_main_thread(void) {
main_thread = running_thread();
init_thread(main_thread, "main", 0);
ASSERT(!elem_find(&thread_all_list, &main_thread->all_list_tag));
list_append(&thread_all_list, &main_thread->all_list_tag);
}
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;
cur->status = TASK_READY;
} else {
}
ASSERT(!list_empty(&thread_ready_list));
thread_tag = NULL;
thread_tag = list_pop(&thread_ready_list);
struct task_struct* next = elem2entry(struct task_struct, general_tag, thread_tag);
next->status = TASK_RUNNING;
process_activate(next);
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();
put_str("thread_init done\n");
}
void thread_block(enum task_status stat) {
ASSERT(((stat == TASK_BLOCKED) || (stat == TASK_WAITING) || (stat == TASK_HANGING)));
enum intr_status old_status = intr_disable();
struct task_struct* cur_thread = running_thread();
cur_thread->status = stat;
schedule();
intr_set_status(old_status);
}
void thread_unblock(struct task_struct*pthread){
enum intr_status old_status=intr_disable();
ASSERT((pthread->status==TASK_BLOCKED)||(pthread->status==TASK_WAITING) \
||(pthread->status==TASK_HANGING));
if(pthread->status!=TASK_READY){
ASSERT(!elem_find(&thread_ready_list,&pthread->general_tag));
if(elem_find(&thread_ready_list,&pthread->general_tag)){
PANIC("thread_unlock :blocked thread int ready_list ");
}
list_push(&thread_ready_list,&pthread->general_tag);
pthread->status=TASK_READY;
}
intr_set_status(old_status);
}
thread.h
#ifndef __THREAD_THREAD_H
#define __THREAD_THREAD_H
#include "../lib/stdint.h"
#include "../lib/kernel/list.h"
#include "../lib/kernel/bitmap.h"
#include "../lib/kernel/memory.h"
typedef void thread_func(void*);
enum task_status {
TASK_RUNNING,
TASK_READY,
TASK_BLOCKED,
TASK_WAITING,
TASK_HANGING,
TASK_DIED
};
struct intr_stack {
uint32_t vec_no;
uint32_t edi;
uint32_t esi;
uint32_t ebp;
uint32_t esp_dummy;
uint32_t ebx;
uint32_t edx;
uint32_t ecx;
uint32_t eax;
uint32_t gs;
uint32_t fs;
uint32_t es;
uint32_t ds;
uint32_t err_code;
void (*eip) (void);
uint32_t cs;
uint32_t eflags;
void* esp;
uint32_t ss;
};
struct thread_stack {
uint32_t ebp;
uint32_t ebx;
uint32_t edi;
uint32_t esi;
void (*eip) (thread_func* func, void* func_arg);
void (*unused_retaddr);
thread_func* function;
void* func_arg;
};
struct task_struct {
uint32_t* self_kstack;
enum task_status status;
char name[16];
uint8_t priority;
uint8_t ticks;
uint32_t elapsed_ticks;
struct list_elem general_tag;
struct list_elem all_list_tag;
uint32_t* pgdir;
struct virtual_addr userprog_vaddr;
uint32_t stack_magic;
};
extern struct list thread_ready_list;
extern struct list thread_all_list;
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);
struct task_struct* running_thread(void);
void schedule(void);
void thread_init(void);
void thread_block(enum task_status stat);
void thread_unblock(struct task_struct* pthread);
#endif
timer.c
#include "timer.h"
#include "io.h"
#include "print.h"
#include "../kernel/interrupt.h"
#include "../thread/thread.h"
#include "../kernel/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;
static void frequency_set(uint8_t counter_port, \
uint8_t counter_no, \
uint8_t rwl, \
uint8_t counter_mode, \
uint16_t counter_value) {
outb(PIT_CONTROL_PORT, (uint8_t)(counter_no << 6 | rwl << 4 | counter_mode << 1));
outb(counter_port, (uint8_t)counter_value);
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++;
ticks++;
if (cur_thread->ticks == 0) {
schedule();
} else {
cur_thread->ticks--;
}
}
void timer_init() {
put_str("timer_init start\n");
frequency_set(CONTRER0_PORT, COUNTER0_NO, READ_WRITE_LATCH, COUNTER_MODE, COUNTER0_VALUE);
register_handler(0x20, intr_timer_handler);
put_str("timer_init done\n");
}
timer.h
#ifndef __DEVICE_TIME_H
#define __DEVICE_TIME_H
#include "../lib/stdint.h"
void timer_init(void);
#endif
|