mykernel 2.0源码详解mymain.c中的进程初始化与任务调度流程【免费下载链接】mykernelmykernel 2.0: Develop your own OS kernel by reusing Linux infrastructure, based on x86-64/Linux Kernel 5.4.34.项目地址: https://gitcode.com/gh_mirrors/my/mykernelmykernel 2.0是一个基于x86-64架构和Linux Kernel 5.4.34的开源项目它允许开发者通过复用Linux基础设施来构建自己的操作系统内核。本文将深入解析mymain.c文件中的进程初始化与任务调度流程帮助新手理解操作系统内核的核心工作机制。进程控制块PCB的数据结构在了解进程初始化之前我们首先需要了解进程控制块PCB的结构。mykernel中的PCB定义在mypcb.h文件中它包含了进程的所有关键信息。typedef struct PCB{ int pid; volatile long state; /* -1 unrunnable, 0 runnable, 0 stopped */ unsigned long stack[KERNEL_STACK_SIZE]; struct Thread thread; unsigned long task_entry; struct PCB *next; }tPCB;其中pid是进程IDstate表示进程状态stack是内核栈thread包含了进程的指令指针ip和栈指针sptask_entry是进程入口函数next用于将PCB链接成循环链表。进程初始化流程mymain.c中的my_start_kernel函数是内核的入口点负责初始化所有进程。1. 初始化0号进程首先系统会初始化0号进程设置其pid为0状态为可运行0并将其入口函数设置为my_process。同时设置进程的栈指针指向内核栈的顶部。/* Initialize process 0*/ task[pid].pid pid; task[pid].state 0;/* -1 unrunnable, 0 runnable, 0 stopped */ task[pid].task_entry task[pid].thread.ip (unsigned long)my_process; task[pid].thread.sp (unsigned long)task[pid].stack[KERNEL_STACK_SIZE-1]; task[pid].next task[pid];2. 创建更多进程接下来系统会循环创建更多的进程最多MAX_TASK_NUM个定义为4。每个新进程都会复制0号进程的PCB信息然后修改其pid和栈指针并将新进程加入到PCB循环链表中。/*fork more process */ for(i1;iMAX_TASK_NUM;i) { memcpy(task[i],task[0],sizeof(tPCB)); task[i].pid i; task[i].thread.sp (unsigned long)(task[i].stack[KERNEL_STACK_SIZE-1]); task[i].next task[i-1].next; task[i-1].next task[i]; }3. 启动0号进程最后系统通过汇编代码启动0号进程将栈指针设置为0号进程的栈指针并跳转到my_process函数执行。asm volatile( movq %1,%%rsp\n\t /* set task[pid].thread.sp to rsp */ pushq %1\n\t /* push rbp */ pushq %0\n\t /* push task[pid].thread.ip */ ret\n\t /* pop task[pid].thread.ip to rip */ : : c (task[pid].thread.ip),d (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/ );任务调度流程mykernel的任务调度主要由my_process函数和my_schedule函数配合完成。1. 进程执行与调度触发my_process函数是每个进程的入口点它是一个无限循环。当循环计数器i达到10000000的倍数时会打印当前进程ID并检查是否需要进行调度my_need_sched是否为1。如果需要调度则调用my_schedule函数。void my_process(void) { while(1) { i; if(i%10000000 0) { printk(KERN_NOTICE this is process %d -\n,my_current_task-pid); if(my_need_sched 1) { my_need_sched 0; my_schedule(); } printk(KERN_NOTICE this is process %d \n,my_current_task-pid); } } }2. 调度时机my_need_sched变量由定时器中断处理函数my_timer_handler设置。当时间计数达到1000的倍数时my_timer_handler会将my_need_sched设置为1触发调度。void my_timer_handler(void) { if(time_count%1000 0 my_need_sched ! 1) { printk(KERN_NOTICE my_timer_handler here\n); my_need_sched 1; } time_count ; return; }3. 进程切换my_schedule函数负责实际的进程切换。它会找到当前进程的下一个进程通过PCB链表的next指针然后保存当前进程的上下文栈指针和指令指针恢复下一个进程的上下文完成进程切换。void my_schedule(void) { tPCB * next; tPCB * prev; if(my_current_task NULL || my_current_task-next NULL) { return; } printk(KERN_NOTICE my_schedule\n); /* schedule */ next my_current_task-next; prev my_current_task; if(next-state 0)/* -1 unrunnable, 0 runnable, 0 stopped */ { my_current_task next; printk(KERN_NOTICE switch %d to %d\n,prev-pid,next-pid); /* switch to next process */ asm volatile( pushq %%rbp\n\t /* save rbp of prev */ movq %%rsp,%0\n\t /* save rsp of prev */ movq %2,%%rsp\n\t /* restore rsp of next */ movq $1f,%1\n\t /* save rip of prev */ pushq %3\n\t ret\n\t /* restore rip of next */ 1:\t /* next process start here */ popq %%rbp\n\t : m (prev-thread.sp),m (prev-thread.ip) : m (next-thread.sp),m (next-thread.ip) ); } return; }总结通过对mymain.c、mypcb.h和myinterrupt.c文件的分析我们了解了mykernel 2.0的进程初始化和任务调度流程。进程初始化通过创建PCB并链接成循环链表来实现而任务调度则通过定时器中断触发在进程间切换上下文来完成。这些核心机制是操作系统内核的基础理解它们对于深入学习操作系统原理至关重要。如果你想进一步探索mykernel的源码可以通过以下命令克隆仓库git clone https://gitcode.com/gh_mirrors/my/mykernel然后查看mymain.c、mypcb.h和myinterrupt.c等文件深入理解每个函数的实现细节。祝你在操作系统内核开发的学习之路上取得进步 【免费下载链接】mykernelmykernel 2.0: Develop your own OS kernel by reusing Linux infrastructure, based on x86-64/Linux Kernel 5.4.34.项目地址: https://gitcode.com/gh_mirrors/my/mykernel创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考