博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
# 操作系统真象还原实验记录之实验十四:实现内核线程
阅读量:2047 次
发布时间:2019-04-28

本文共 4672 字,大约阅读时间需要 15 分钟。

操作系统真象还原实验记录之实验十四:实现内核线程

对应书P409 9.2节

1.相关基础知识总结

进程有资源即页表,进程的所有线程公用进程的页表

2.实验代码

2.1thread.h

#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结构

2.2 thread.c

#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

2.3 main.c

#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”

2.4 makefile

$(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/

3.实验结果

在这里插入图片描述

转载地址:http://cyqof.baihongyu.com/

你可能感兴趣的文章
php中的declare是干什么的——转载
查看>>
三种常见的排序算法
查看>>
ueditor自定义菜单(转载)
查看>>
php页面meta头设置
查看>>
指针函数和函数指针的区别
查看>>
empty方法
查看>>
What does ‘composer dump-autoload’ do in Laravel?
查看>>
在win7系统中使用nodejs在WebStrom下配置socket.io
查看>>
caffe:用自己的图像数据训练模型
查看>>
ubuntu下clion中配置opencv的CMakeLists.txt
查看>>
什么是卷积 卷积有什么用
查看>>
有趣的机器学习概念纵览:从多元拟合,神经网络到深度学习,给每个感兴趣的人
查看>>
K-近邻算法:KNN
查看>>
solver及其配置
查看>>
图说C++对象模型:对象内存布局详解
查看>>
【Java基础】Java类的加载和对象创建流程的详细分析
查看>>
JAVA多线程之volatile 与 synchronized 的比较
查看>>
Java多线程知识点总结
查看>>
Java集合框架知识梳理
查看>>
java中IO流知识梳理
查看>>