本文共 4672 字,大约阅读时间需要 15 分钟。
对应书P409 9.2节
进程有资源即页表,进程的所有线程公用进程的页表
#ifndef __THREAD_THREAD_H#define __THREAD_THREAD_H#include "stdint.h"typedef void thread_func(void*);/* 进程或线程的状态 */enum task_status { TASK_RUNNING, TASK_READY, TASK_BLOCKED, TASK_WAITING, TASK_HANGING, TASK_DIED};/*********** 中断栈intr_stack *********** * 此结构用于中断发生时保护程序(线程或进程)的上下文环境: * 进程或线程被外部中断或软中断打断时,会按照此结构压入上下文 * 寄存器, 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 { 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时使用 ****//* 参数unused_ret只为占位置充数为返回地址 */ void (*unused_retaddr); thread_func* function; // 由Kernel_thread所调用的函数名 void* func_arg; // 由Kernel_thread所调用的函数所需的参数};/* 进程或线程的pcb,程序控制块 */struct task_struct { uint32_t* self_kstack; // 各内核线程都用自己的内核栈 enum task_status status; char name[16]; uint8_t priority; 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
intr_stack:中断栈,cpu执行中断入口程序时就按此结构顺序压栈保护上下文,位于PCB所在页最高地址。
thread_stack:线程栈,用于保存待运行的函数,地址紧接着中断栈 task_struct:PCB结构#include "thread.h"#include "stdint.h"#include "string.h"#include "global.h"#include "memory.h"/* 由kernel_thread去执行function(func_arg) */static void kernel_thread(thread_func* function, void* func_arg) { function(func_arg); }/* 初始化线程栈thread_stack,将待执行的函数和参数放到thread_stack中相应的位置 */void thread_create(struct task_struct* pthread, thread_func function, void* func_arg) { /* 先预留中断使用栈的空间,可见thread.h中定义的结构 */ pthread->self_kstack -= sizeof(struct intr_stack); /* 再留出线程栈空间,可见thread.h中定义 */ 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); pthread->status = TASK_RUNNING;/* self_kstack是线程自己在内核态下使用的栈顶地址 */ pthread->self_kstack = (uint32_t*)((uint32_t)pthread + PG_SIZE); pthread->priority = prio; pthread->stack_magic = 0x19870916; // 自定义的魔数}/* 创建一优先级为prio的线程,线程名为name,线程所执行的函数是function(func_arg) */struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg) {/* pcb都位于内核空间,包括用户进程的pcb也是在内核空间 */ struct task_struct* thread = get_kernel_pages(1); init_thread(thread, name, prio); thread_create(thread, function, func_arg); asm volatile ("movl %0, %%esp; pop %%ebp; pop %%ebx; pop %%edi; pop %%esi; \ ret ": : "g" (thread->self_kstack) :"memory"); return thread;}
thread_start:在内核池中申请了一页物理页来用作PCB,然后调用了 init_thread将PCB里的成员变量初始化,其中self_kstack保存了PCB所在页最高地址。之后调用了thread_create,在最高地址处预留了中断栈的空间,然后又填写了线程栈结构体中的成员变量,eip被赋值为kernel_thread,最后一句ret指令执行kernel_thread函数,从而执行function
#include "print.h"#include "init.h"#include "thread.h"void k_thread_a(void* );int main(void) { put_str("I am kernel\n"); init_all(); thread_start("k_thread_a", 31, k_thread_a, "argA "); while(1); return 0;}void k_thread_a(void* arg){ char* para = arg; while(1){ put_str(para); } }
thread_start最后执行k_thread_a函数,循环输出“argA”
$(BUILD_DIR)/thread.o: thread/thread.c thread/thread.h lib/stdint.h \ kernel/global.h lib/string.h lib/stdint.h kernel/debug.h \ lib/kernel/print.h kernel/memory.h \ lib/kernel/bitmap.h thread/thread.h $(CC) $(CFLAGS) $< -o $@
还要增加OBJS
$(BUILD_DIR)/thread.o
也要增加
LIB = -I thread/转载地址:http://cyqof.baihongyu.com/