《操作系统真象还原》第9章 您所在的位置:网站首页 端口代码52 《操作系统真象还原》第9章

《操作系统真象还原》第9章

2023-03-10 02:56| 来源: 网络整理| 查看: 265

感觉本章所涉及的进程与线程的相关知识是我在学习xv6时学的最薄弱、也是最难搞的一部分,因为里面涉及大量出入栈和栈的变换操作。我觉得栈是OS中的核心,弄懂了栈OS也就手到擒来了。

正如书中所说,“有些陌生的内容需要量变才能到质变”,特将部分内容抄录如下以加深自己的印象。

1.实现内核线程

线程和进程比,进程拥有整个地址空间,从而拥有全部资源,线程没有自己的地址空间,因此没有任何属于自己的资源,需要借助进程的资源“生存”,所以线程被称为轻量级进程。

进程中包含此进程中所有线程所使用的资源,因此线程依赖于进程,存在于进程之中,用表达式来表示:进程=线程+资源。

由于各个进程都拥有自己的虚拟地址空间,正常情况下它们彼此无法访问到对方的内部,因为进程之间的安全性是由操作系统的分页机制来保证的,只要操作系统不要把相同的物理页分配给多个进程就行了。

但进程内的线程可都是共享这同一地址空间的,它们彼此能“见面”,这就暴露出一个问题,既然进程内的所有线程共享同一个地址空间,也就意味着任意一个线程都可以去访问同一进程内其它线程的数据,甚至可以改变它们的数据。

进程只是个资源整合体,它将进程中所有线程运行时用到资源收集在一起,供进程中的所有线程使用,真正上处理器上运行的起始都叫线程,进程中的线程才是一个个的执行实体、执行流,因此,经调度器送上处理器执行的程序都是线程。

执行流、调度单位、运行实体等概念都是针对线程而言的,线程才是解决问题的思路、步骤,它是具有能动性的指令,因此只有它才能上处理器运行,即一切执行流起始都是线程,因为任何时候进程中都至少存在一个线程。

进程的状态描述的是进程中有关“动作”的执行流部分,即线程,而不包括静止的资源部分。把上述需要等待外界条件的状态称为“阻塞态”,把外界条件成立时,进程可以随时准备运行的状态称为“就绪态”,把正在处理器上运行的进程的状态称为“运行态”。

 步骤:

1.简单的PCB及线程的实现

2.核心数据结构——双向链表

3.多线程调度

1.简单的PCB及线程的实现

我们将本章的代码放入thread/下。

①thread/thread.h:

1 #ifndef __THREAD_THREAD_H 2 #define __THREAD_THREAD_H 3 #include "stdint.h" 4 5 /* 自定义通用函数类型,它将在很多线程程序中作为参数类型 */ 6 typedef void thread_func(void*); 7 8 /* 进程或线程状态 */ 9 enum task_status{ 10 TASK_RUNNING, 11 TASK_READY, 12 TASK_BLOCKED, 13 TASK_WAITING, 14 TASK_HANGING, 15 TASK_DIED 16 }; 17 18 /***************** 中断栈intr_stack ****************** 19 * 此结构用于中断发生时保护程序(线程或进程)的上下文环境 20 * 进程或线程被外部中断或软中断打断时,会按照此结构压入上下文 21 * 寄存器,intr_exit中的出栈操作是此结构的逆操作 22 * 此栈在线程自己的内核栈中的位置固定,所在页的最顶端 23 *****************************************************/ 24 struct intr_stack{ 25 uint32_t vec_no; // kenrel.S宏VECTOR中push %1压入的中断号 26 uint32_t edi; 27 uint32_t esi; 28 uint32_t ebp; 29 uint32_t esp_dummy; // 虽然pushad会压入esp,但esp是不断变化的,所以会被popad忽略 30 uint32_t ebx; 31 uint32_t edx; 32 uint32_t ecx; 33 uint32_t eax; 34 uint32_t gs; 35 uint32_t fs; 36 uint32_t es; 37 uint32_t ds; 38 39 /* 以下由cpu从低特权级进入高特权级时压入 */ 40 uint32_t err_code; // error_code会被压入在eip之后 41 void (*eip)(void); 42 uint32_t cs; 43 uint32_t eflags; 44 void* esp; 45 uint32_t ss; 46 }; 47 48 /*****************线程栈thread_stack**************** 49 * 线程自己的栈,用于存储线程中待执行的函数 50 * 此结构在线程自己的内核栈中位置不固定, 51 * 仅用在switch_to时保存线程环境。 52 * 实际位置取决于实际运行情况。 53 *************************************************/ 54 struct thread_stack{ 55 uint32_t ebp; 56 uint32_t ebx; 57 uint32_t edi; 58 uint32_t esi; 59 60 /* 线程第一次执行时,eip指向待调用的函数kernel_thread 61 * 其它时候,eip是指向switch_to的返回地址 */ 62 void (*eip)(thread_func* func,void* func_arg); 63 64 /****** 以下仅供第一次被调度上cpu时使用 ******/ 65 66 /* 参数unused_ret只为占位置充数为返回地址 */ 67 void (*unused_retaddr); 68 thread_func* function;// 由kernel_thread所调用的函数名 69 void* func_arg; // 由kernel_thread所调用的函数所需的参数 70 }; 71 72 /* 进程或线程的pcb,程序控制块 */ 73 struct task_struct{ 74 uint32_t* self_kstack;// 各内核线程都有自己的内核栈 75 enum task_status status; 76 uint8_t priority; // 线程优先级 77 char name[16]; 78 uint32_t stack_magic; // 栈的边界标记,用于检测栈的溢出 79 }; 80 81 static void kernel_thread(thread_func* function,void* func_arg); 82 void thread_create(struct task_struct* pthread,thread_func function,void* func_arg); 83 void init_thread(struct task_struct* pthread,char* name,int prio); 84 struct task_struct* thread_start(char* name,int prio,thread_func function,void* func_arg); 85 #endif

②thread/thread.c:

1 #include "thread.h" 2 #include "stdint.h" 3 #include "string.h" 4 #include "global.h" 5 #include "memory.h" 6 7 #define PG_SIZE 4096 8 9 /* 由kernel_thread去执行function(func_arg) */ 10 static void kernel_thread(thread_func* function,void* func_arg){ 11 function(func_arg); 12 } 13 14 /* 初始化线程栈thread_stack,将待执行的函数和参数方法到thread_stack中相应的位置 */ 15 void thread_create(struct task_struct* pthread,thread_func function,void* func_arg){ 16 /* 先预留中断使用栈的空间,可见thread.h中定义的结构 */ 17 pthread->self_kstack-=sizeof(struct intr_stack); 18 19 /* 再留出线程栈空间,可见thread.h中定义 */ 20 pthread->self_kstack-=sizeof(struct thread_stack); 21 struct thread_stack* kthread_stack=(struct thread_stack*)pthread->self_kstack; 22 kthread_stack->eip=kernel_thread; 23 kthread_stack->function=function; 24 kthread_stack->func_arg=func_arg; 25 kthread_stack->ebp=kthread_stack->ebx=kthread_stack->esi=kthread_stack->edi=0; 26 } 27 28 /* 初始化线程基本信息 */ 29 void init_thread(struct task_struct* pthread,char* name,int prio){ 30 memset(pthread,0,sizeof(*pthread)); 31 strcpy(pthread->name,name); 32 pthread->status=TASK_RUNNING; 33 pthread->priority=prio; 34 /* self_kstack是线程自己在内核态下使用的栈顶地址 */ 35 pthread->self_kstack=(uint32_t*)((uint32_t)pthread+PG_SIZE); 36 pthread->stack_magic=0x19870916; // 自定义魔数 37 } 38 39 /* 创建一个优先级为prio的线程,线程名为name,线程所执行的函数是funciton(func_arg) */ 40 struct task_struct* thread_start(char* name,int prio,thread_func function,void* func_arg){ 41 /* pcb都位于内核空间,包括用户进程pcb也是在内核空间 */ 42 struct task_struct* thread=get_kernel_pages(1); 43 44 init_thread(thread,name,prio); 45 thread_create(thread,function,func_arg); 46 47 asm volatile("movl %0,%%esp; pop %%ebp; pop %%ebx; pop %%edi; pop %%esi; ret":: "g"(thread->self_kstack): "memory"); 48 return thread; 49 }

③kernel/main.c:

1 #include "print.h" 2 #include "init.h" 3 #include "thread.h" 4 5 void k_thread_a(void*); 6 7 int main(void){ 8 put_str("Welcome,\nI am kernel!\n"); 9 init_all(); 10 11 thread_start("k_thread_a",31,k_thread_a,"argA"); 12 13 while(1); 14 return 0; 15 } 16 17 /* 在线程中运行的函数 */ 18 void k_thread_a(void* arg){ 19 /* 用void*来通用表示参数,被调用的函数知道自己需要什么类型的参数,自己转换再用 */ 20 char* para=arg; 21 while (1){ 22 put_str(para); 23 } 24 }

④makefile:

1 BUILD_DIR = ./build 2 ENTRY_POINT = 0xc0001500 3 AS = nasm 4 CC = gcc 5 LD = ld 6 LIB = -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/ -I thread/ 7 ASFLAGS = -f elf 8 CFLAGS = -Wall -m32 -fno-stack-protector $(LIB) -c -fno-builtin -W -Wstrict-prototypes -Wmissing-prototypes 9 LDFLAGS = -m elf_i386 -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map 10 OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o \ 11 $(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o \ 12 $(BUILD_DIR)/debug.o $(BUILD_DIR)/string.o $(BUILD_DIR)/memory.o \ 13 $(BUILD_DIR)/bitmap.o $(BUILD_DIR)/thread.o 14 ############## c代码编译 ############### 15 $(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \ 16 lib/stdint.h kernel/init.h lib/string.h kernel/memory.h \ 17 thread/thread.h 18 $(CC) $(CFLAGS) $< -o $@ 19 20 $(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \ 21 lib/stdint.h kernel/interrupt.h device/timer.h kernel/memory.h 22 $(CC) $(CFLAGS) $< -o $@ 23 24 $(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \ 25 lib/stdint.h kernel/global.h lib/kernel/io.h lib/kernel/print.h 26 $(CC) $(CFLAGS) $< -o $@ 27 28 $(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/stdint.h\ 29 lib/kernel/io.h lib/kernel/print.h 30 $(CC) $(CFLAGS) $< -o $@ 31 32 $(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \ 33 lib/kernel/print.h lib/stdint.h kernel/interrupt.h 34 $(CC) $(CFLAGS) $< -o $@ 35 36 $(BUILD_DIR)/string.o: lib/string.c lib/string.h \ 37 kernel/debug.h kernel/global.h 38 $(CC) $(CFLAGS) $< -o $@ 39 40 $(BUILD_DIR)/memory.o: kernel/memory.c kernel/memory.h \ 41 lib/stdint.h lib/kernel/bitmap.h kernel/debug.h lib/string.h 42 $(CC) $(CFLAGS) $< -o $@ 43 44 $(BUILD_DIR)/bitmap.o: lib/kernel/bitmap.c lib/kernel/bitmap.h \ 45 lib/string.h kernel/interrupt.h lib/kernel/print.h kernel/debug.h 46 $(CC) $(CFLAGS) $< -o $@ 47 48 $(BUILD_DIR)/thread.o: thread/thread.c thread/thread.h \ 49 lib/stdint.h lib/string.h kernel/global.h kernel/memory.h 50 $(CC) $(CFLAGS) $< -o $@ 51 52 ############## 汇编代码编译 ############### 53 $(BUILD_DIR)/kernel.o: kernel/kernel.S 54 $(AS) $(ASFLAGS) $< -o $@ 55 56 $(BUILD_DIR)/print.o: lib/kernel/print.S 57 $(AS) $(ASFLAGS) $< -o $@ 58 59 ############## 链接所有目标文件 ############# 60 $(BUILD_DIR)/kernel.bin: $(OBJS) 61 $(LD) $(LDFLAGS) $^ -o $@ 62 63 .PHONY : mk_dir hd clean all 64 65 mk_dir: 66 if [ ! -d $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fi 67 68 hd: 69 dd if=$(BUILD_DIR)/kernel.bin \ 70 of=hd60M.img \ 71 bs=512 count=200 seek=9 conv=notrunc 72 73 clean: 74 cd $(BUILD_DIR) && rm -f ./* 75 76 build: $(BUILD_DIR)/kernel.bin 77 78 all: mk_dir build hd

如图,满屏的argA,初战告捷:

2.核心数据结构——双向链表

①lib/kernel/list.h:

1 #ifndef __LIB_KERNEL_LIST_H 2 #define __LIB_KERNEL_LIST_H 3 #include "global.h" 4 5 #define offset(struct_type,member)(int)(&((struct_type*)0)->member) 6 #define elem2entry(struct_type,struct_member_name,elem_ptr)\ 7 (struct_type*)((int)elem_ptr-offset(struct_type,struct_member_name)) 8 9 /************ 定义链表结点成员结构 *********** 10 * 结点中不需要数据成员,只要求前驱和后继结点指针*/ 11 struct list_elem{ 12 struct list_elem* prev; // 前驱结点 13 struct list_elem* next; // 后继结点 14 }; 15 16 /* 链表结构,用来实现队列 */ 17 struct list{ 18 /* head是队首,是固定不变的,不是第1个元素,第一个元素是head.next */ 19 struct list_elem head; 20 /* tail是队尾,同样是固定不变的 */ 21 struct list_elem tail; 22 }; 23 24 /* 自定义函数类型function,用于在list_traversal中做回调函数 */ 25 typedef bool (function)(struct list_elem*,int arg); 26 27 void list_init(struct list*); 28 void list_insert_before(struct list_elem* before,struct list_elem* elem); 29 void list_push(struct list* plist); 30 void list_iterate(struct list* plist); 31 void list_append(struct list* plist,struct list_elem* elem); 32 void list_remove(struct list_elem* pelem); 33 struct list_elem* list_pop(struct list* plist); 34 bool list_empty(struct list* plist); 35 uint32_t list_len(struct list* plist); 36 struct list_elem* list_traversal(struct list* plist,function func,int arg); 37 bool elem_find(struct list* plist,struct list_elem* obj_elem);

②lib/kernel/list.c:

1 #include "list.h" 2 #include "interrupt.h" 3 4 /* 初始化双向链表list */ 5 void list_init(struct list* list){ 6 list->head.prev=NULL; 7 list->head.next=&list->tail; 8 list->tail.prev=&list->head; 9 list->tail.next=NULL; 10 } 11 12 /* 把链表元素elem插入在元素before之前 */ 13 void list_insert_before(struct list_elem* before,struct list_elem* elem){ 14 enum intr_status old_status=intr_disable(); 15 16 /* 将before前驱元素的后继元素更新为elem,暂时使before脱离链表 */ 17 before->prev->next=elem; 18 19 /* 更新elem自己的前驱结点为before的前驱, 20 * 更新elem自己的后继结点为before,于是before又回到链表 */ 21 elem->prev=before->prev; 22 elem->next=before; 23 24 /* 更新before的前驱结点为elem */ 25 before->prev=elem; 26 27 intr_set_status(old_status); 28 } 29 30 /* 添加元素到列表队首,类似栈push操作 */ 31 void list_push(struct list* plist,struct list_elem* elem){ 32 list_insert_before(plist->head.next,elem); // 在队头后面插入elem 33 } 34 35 /* 追加元素到列表队尾,类似于队列的先进先出操作 */ 36 void list_append(struct list* plist,struct list_elem* elem){ 37 list_insert_before(plist->tail,elem); // 在队尾前面插入elem 38 } 39 40 /* 使元素pelem脱离链表 */ 41 void list_remove(struct list_elem* pelem){ 42 enum intr_status old_status=intr_disable(); 43 44 pelem->prev->next=pelem->next; 45 pelem->next->prev=pelem->prev; 46 47 intr_set_status(old_status); 48 } 49 50 /* 将链表第一个元素弹出并返回,类似栈的pop操作 */ 51 struct list_elem* list_pop(struct list* plist){ 52 struct list_elem* elem=plist->head.next; 53 list_remove(elem); 54 return elem; 55 } 56 57 /* 从链表中查找元素obj_elem,成功时返回true,失败时返回false */ 58 bool elem_find(struct list* plist,struct list_elem* obj_elem){ 59 struct list_elem* elem=plist->head.next; 60 while (elem!=&plist->tail){ 61 if (elem==obj_elem){ 62 return true; 63 } 64 elem=elem->next; 65 } 66 return false; 67 } 68 69 /* 把列表plist中的每个元素elem和arg传给回调函数func, 70 * arg给func用来判断elem是否符合条件, 71 * 本函数的功能是遍历列表内所有元素,逐个判断是否有符合条件的元素。 72 * 找到符合条件的元素返回元素指针,否则返回NULL */ 73 struct list_elem* list_traversal(struct list* plist,function func,int arg){ 74 struct list_elem* elem=plist->head.next; 75 if (list_empty(plist)){ 76 return NULL; 77 } 78 79 while (elem!=&plist->tail){ 80 if (func(elem,arg)){ // func返回true,则认为该元素在回调函数中符合条件,命中,故停止继续遍历 81 return elem; 82 } // 若回调函数func返回false,则继续遍历 83 elem=elem->next; 84 } 85 return NULL; 86 } 87 88 /* 返回链表长度 */ 89 uint32_t list_len(struct list* plist){ 90 struct list_elem* elem=plist->head.next; 91 uint32_t length=0; 92 while (elem!=&plist->tail){ 93 ++length; 94 elem=elem->next; 95 } 96 return length; 97 } 98 99 /* 判断链表是否为空,空时返回true,否则返回false */ 100 bool list_empty(struct list* plist){ 101 return plist->head.next==&plist->tail?true:false; 102 }

3.多线程调度

 本节涉及到的代码还是很多的,我还是直接放出来吧:

①thread/thread.h:

1 #ifndef __THREAD_THREAD_H 2 #define __THREAD_THREAD_H 3 #include "stdint.h" 4 #include "list.h" 5 6 /* 自定义通用函数类型,它将在很多线程程序中作为参数类型 */ 7 typedef void thread_func(void*); 8 9 /* 进程或线程状态 */ 10 enum task_status{ 11 TASK_RUNNING, 12 TASK_READY, 13 TASK_BLOCKED, 14 TASK_WAITING, 15 TASK_HANGING, 16 TASK_DIED 17 }; 18 19 /***************** 中断栈intr_stack ****************** 20 * 此结构用于中断发生时保护程序(线程或进程)的上下文环境 21 * 进程或线程被外部中断或软中断打断时,会按照此结构压入上下文 22 * 寄存器,intr_exit中的出栈操作是此结构的逆操作 23 * 此栈在线程自己的内核栈中的位置固定,所在页的最顶端 24 *****************************************************/ 25 struct intr_stack{ 26 uint32_t vec_no; // kenrel.S宏VECTOR中push %1压入的中断号 27 uint32_t edi; 28 uint32_t esi; 29 uint32_t ebp; 30 uint32_t esp_dummy; // 虽然pushad会压入esp,但esp是不断变化的,所以会被popad忽略 31 uint32_t ebx; 32 uint32_t edx; 33 uint32_t ecx; 34 uint32_t eax; 35 uint32_t gs; 36 uint32_t fs; 37 uint32_t es; 38 uint32_t ds; 39 40 /* 以下由cpu从低特权级进入高特权级时压入 */ 41 uint32_t err_code; // error_code会被压入在eip之后 42 void (*eip)(void); 43 uint32_t cs; 44 uint32_t eflags; 45 void* esp; 46 uint32_t ss; 47 }; 48 49 /*****************线程栈thread_stack**************** 50 * 线程自己的栈,用于存储线程中待执行的函数 51 * 此结构在线程自己的内核栈中位置不固定, 52 * 仅用在switch_to时保存线程环境。 53 * 实际位置取决于实际运行情况。 54 *************************************************/ 55 struct thread_stack{ 56 uint32_t ebp; 57 uint32_t ebx; 58 uint32_t edi; 59 uint32_t esi; 60 61 /* 线程第一次执行时,eip指向待调用的函数kernel_thread 62 * 其它时候,eip是指向switch_to的返回地址 */ 63 void (*eip)(thread_func* func,void* func_arg); 64 65 /****** 以下仅供第一次被调度上cpu时使用 ******/ 66 67 /* 参数unused_ret只为占位置充数为返回地址 */ 68 void (*unused_retaddr); 69 thread_func* function;// 由kernel_thread所调用的函数名 70 void* func_arg; // 由kernel_thread所调用的函数所需的参数 71 }; 72 73 /* 进程或线程的pcb,程序控制块 */ 74 struct task_struct{ 75 uint32_t* self_kstack;// 各内核线程都有自己的内核栈 76 enum task_status status; 77 uint8_t priority; // 线程优先级 78 char name[16]; 79 uint8_t ticks; // 每次在处理器上执行的时间滴答数 80 81 /* 此任务自上cpu运行后占用了多少cpu嘀嗒数,也就是此任务执行了多久 */ 82 uint32_t elapsed_ticks; 83 84 /* genernal_tag的作用是用于线程在一般的队列中的结点 */ 85 struct list_elem general_tag; 86 87 /* all_list_tag的作用是用于线程队列thread_all_list中的结点 */ 88 struct list_elem all_list_tag; 89 90 uint32_t* pgdir; // 进程自己页表的虚拟地址 91 uint32_t stack_magic; // 栈的边界标记,用于检测栈的溢出 92 }; 93 94 void kernel_thread(thread_func* function,void* func_arg); 95 void thread_create(struct task_struct* pthread,thread_func function,void* func_arg); 96 void init_thread(struct task_struct* pthread,char* name,int prio); 97 struct task_struct* thread_start(char* name,int prio,thread_func function,void* func_arg); 98 struct task_struct* running_thread(void); 99 void schedule(void); 100 void thread_init(void); 101 #endif View Code

②thread/thread.c:

1 #include "thread.h" 2 #include "stdint.h" 3 #include "string.h" 4 #include "global.h" 5 #include "memory.h" 6 #include "interrupt.h" 7 #include "print.h" 8 #include "debug.h" 9 10 #define PG_SIZE 4096 11 12 struct task_struct* main_thread; // 主线程PCB 13 struct list thread_ready_list; // 就绪队列 14 struct list thread_all_list; // 所有任务队列 15 static struct list_elem* thread_tag;// 用于保存队列中的线程结点 16 17 extern void switch_to(struct task_struct* cur,struct task_struct* next); 18 19 /* 获取当前pcb指针 */ 20 struct task_struct* running_thread(){ 21 uint32_t esp; 22 asm("mov %%esp,%0":"=g"(esp)); 23 /* 取asm整数部分,即pcb起始地址 */ 24 return (struct task_struct*)(esp & 0xfffff000); 25 } 26 27 /* 由kernel_thread去执行function(func_arg) */ 28 void kernel_thread(thread_func* function,void* func_arg){ 29 /* 执行function前要开中断,避免后面的时钟中断被屏蔽,而无法调度其它进程 */ 30 intr_enable(); 31 function(func_arg); 32 } 33 34 /* 初始化线程栈thread_stack,将待执行的函数和参数方法到thread_stack中相应的位置 */ 35 void thread_create(struct task_struct* pthread,thread_func function,void* func_arg){ 36 /* 先预留中断使用栈的空间,可见thread.h中定义的结构 */ 37 pthread->self_kstack-=sizeof(struct intr_stack); 38 39 /* 再留出线程栈空间,可见thread.h中定义 */ 40 pthread->self_kstack-=sizeof(struct thread_stack); 41 struct thread_stack* kthread_stack=(struct thread_stack*)pthread->self_kstack; 42 kthread_stack->eip=kernel_thread; 43 kthread_stack->function=function; 44 kthread_stack->func_arg=func_arg; 45 kthread_stack->ebp=kthread_stack->ebx=kthread_stack->esi=kthread_stack->edi=0; 46 } 47 48 /* 初始化线程基本信息 */ 49 void init_thread(struct task_struct* pthread,char* name,int prio){ 50 memset(pthread,0,sizeof(*pthread)); 51 strcpy(pthread->name,name); 52 53 if (pthread==main_thread){ 54 /* 由于把main函数也封装成一个线程,并且它一直是运行的,故将其直接设为TASK_RUNNING */ 55 pthread->status=TASK_RUNNING; 56 }else{ 57 pthread->status=TASK_READY; 58 } 59 60 /* self_kstack是线程自己在内核态下使用的栈顶地址 */ 61 pthread->self_kstack=(uint32_t*)((uint32_t)pthread+PG_SIZE); 62 pthread->priority=prio; 63 pthread->ticks=prio; 64 pthread->elapsed_ticks=0; 65 pthread->pgdir=NULL; 66 pthread->stack_magic=0x19870916; // 自定义魔数 67 } 68 69 /* 创建一个优先级为prio的线程,线程名为name,线程所执行的函数是funciton(func_arg) */ 70 struct task_struct* thread_start(char* name,int prio,thread_func function,void* func_arg){ 71 /* pcb都位于内核空间,包括用户进程pcb也是在内核空间 */ 72 struct task_struct* thread=get_kernel_pages(1); 73 74 init_thread(thread,name,prio); 75 thread_create(thread,function,func_arg); 76 77 /* 确保之前不再队列中 */ 78 ASSERT(!elem_find(&thread_ready_list,&thread->general_tag)); 79 /* 加入就绪队列 */ 80 list_append(&thread_ready_list,&thread->general_tag); 81 82 /* 确保之前不再队列中 */ 83 ASSERT(!elem_find(&thread_all_list,&thread->all_list_tag)); 84 list_append(&thread_all_list,&thread->all_list_tag); 85 86 return thread; 87 } 88 89 /* 将kernel中的main函数完善为主线程 */ 90 static void make_main_thread(void){ 91 /* 因为main线程早已运行,咱们在loader.S中进入内核时的mov esp,0xc009f000, 92 * 就是为其预留pcb的,因此pcb地址为0xc009e000,不需要勇敢get_kernel_page另分配一页 */ 93 main_thread=running_thread(); 94 init_thread(main_thread,"main",31); 95 96 /* main函数是当前线程,当前线程不在thread_ready_list中, 97 * 所以只将其加在thead_all_list中 */ 98 ASSERT(!elem_find(&thread_all_list,&main_thread->all_list_tag)); 99 list_append(&thread_all_list,&main_thread->all_list_tag); 100 } 101 102 /* 实现任务调度 */ 103 void schedule(){ 104 ASSERT(intr_get_status()==INTR_OFF); 105 struct task_struct* cur=running_thread(); 106 if (cur->status==TASK_RUNNING){ 107 // 若此线程只是cpu时间片到了,将其加入就绪队列队尾 108 ASSERT(!elem_find(&thread_ready_list,&cur->general_tag)); 109 list_append(&thread_ready_list,&cur->general_tag); 110 cur->ticks=cur->priority; 111 // 重新将当前进程的ticks重置为priority 112 cur->status=TASK_READY; 113 }else{ 114 /* 若此线程需要某事件发生后才能继续上cpu运行, 115 * 不需要将其加入队列,因为当前不在就绪队列中 */ 116 } 117 118 ASSERT(!list_empty(&thread_ready_list)); 119 thread_tag=NULL; // thread_tag清空 120 /* 将thread_ready_list队列中的第一个就绪线程弹出,准备将其调度上cpu */ 121 thread_tag=list_pop(&thread_ready_list); 122 struct task_struct* next=elem2entry(struct task_struct,general_tag,thread_tag); 123 next->status=TASK_RUNNING; 124 switch_to(cur,next); 125 } 126 127 /* 初始化线程环境 */ 128 void thread_init(void){ 129 put_str("thread_init start\n"); 130 list_init(&thread_ready_list); 131 list_init(&thread_all_list); 132 /* 将当前main函数创建为线程 */ 133 make_main_thread(); 134 put_str("thread_init done\n"); 135 } View Code

③kernel/interrupt.c:

1 #include "interrupt.h" 2 #include "stdint.h" 3 #include "global.h" 4 #include "io.h" 5 #include "print.h" 6 7 #define PIC_M_CTRL 0x20 // 这里用的可编程中断控制器是8259A,主片的控制端口是0x20 8 #define PIC_M_DATA 0x21 // 主片的数据端口是0x21 9 #define PIC_S_CTRL 0xa0 // 从片的控制端口是0xa0 10 #define PIC_S_DATA 0xa1 // 从片的数据端口是0xa1 11 12 #define IDT_DESC_CNT 0x21 // 目前总共支持的中断数 13 14 #define EFLAGS_IF 0x00000200//eflags寄存器的if位为1 15 #define GET_EFLAGS(EFLAGS_VAR) asm volatile("pushfl; popl %0":"=g"(EFLAGS_VAR)) 16 17 /* 中断门描述符结构体 */ 18 struct gate_desc{ 19 uint16_t func_offset_low_word; 20 uint16_t selector; 21 uint8_t dcount; //此项为双字计数字段,是门描述符中的第4字节。此项固定值,不用考虑 22 uint8_t attribute; 23 uint16_t func_offset_high_word; 24 }; 25 26 // 静态函数声明,非必须 27 static void make_idt_desc(struct gate_desc* p_gdesc,uint8_t attr,intr_handler function); 28 static struct gate_desc idt[IDT_DESC_CNT]; // idt是中断描述符表,本质上就是个中断门描述符数组 29 char* intr_name[IDT_DESC_CNT]; // 用于保存异常的名字 30 31 /************** 定义中断处理程序数组 ************* 32 * 在kernel.S中定义的intrXXentry只是中断处理程序的入口, 33 * 最终调用的是ide_table中的处理程序*/ 34 intr_handler idt_table[IDT_DESC_CNT]; 35 36 /********************************************/ 37 extern intr_handler intr_entry_table[IDT_DESC_CNT]; // 声明引用定义在kernel.S中的中断处理函数入口数组 38 39 /* 初始化可编程中断控制器8259A */ 40 static void pic_init(void){ 41 42 /*初始化主片*/ 43 outb(PIC_M_CTRL,0x11); 44 outb(PIC_M_DATA,0x20); 45 outb(PIC_M_DATA,0x04); 46 outb(PIC_M_DATA,0x01); 47 48 /*初始化从片*/ 49 outb(PIC_S_CTRL,0x11); 50 outb(PIC_S_DATA,0x28); 51 outb(PIC_S_DATA,0x02); 52 outb(PIC_S_DATA,0x01); 53 54 outb(PIC_M_DATA,0xfe); 55 outb(PIC_S_DATA,0xff); 56 57 put_str(" pic_init done\n"); 58 } 59 60 /* 创建中断门描述符 */ 61 static void make_idt_desc(struct gate_desc* p_gdesc,uint8_t attr,intr_handler function){ 62 p_gdesc->func_offset_low_word=(uint32_t)function & 0x0000FFFF; 63 p_gdesc->selector=SELECTOR_K_CODE; 64 p_gdesc->dcount=0; 65 p_gdesc->attribute=attr; 66 p_gdesc->func_offset_high_word=((uint32_t)function & 0xFFFF0000)>>16; 67 } 68 69 /* 初始化中断描述符表 */ 70 static void idt_desc_init(void){ 71 int i=IDT_DESC_CNT-1; 72 for (i=0;i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有