基於mykernel完成時間片輪詢多道進程的簡單內核
阿新 • • 發佈:2019-03-12
完成 null timer state 啟動進程 全局 led put clas
基於mykernel完成時間片輪詢多道進程的簡單內核
學號:282
原創作品轉載請註明出處 + https://github.com/mengning/linuxkernel/
一、實驗目的
基於mykernel,完成一個簡單的時間片輪轉多道程序內核代碼並分析。
二、實驗步驟
1. 環境搭建
環境為 Linux QEMU,實驗樓網站中直接打開即可食用。
在實驗樓終端中執行下列命令對內核進行打補丁操作
cd LinuxKernel/linux-3.9.4 rm mykernel -rf patch -p1 < ../mykernel_for_linux3.9.4sc.patch
編譯源碼
make allnoconfig make
編譯成功後,使用QEMU工具來顯示代碼執行過程。內核成功啟動!
2. 基於mykernel實現時間片輪詢多道進程
通過上面的步驟,我們將孟寧老師GitHub上面myinterrput.c和mymain.c 替換掉mykernel中的文件,將mypcb.h移動到相同文件夾下。
git克隆源碼
git clone https://github.com/mengning/mykernel.git
替換原代碼
重新編譯和運行
3. 源碼分析
mypcb.h, 此頭文件主要用於定義進程控制塊
/* * linux/mykernel/mypcb.h * * Kernel internal PCB types * * Copyright (C) 2013 Mengning * */ #define MAX_TASK_NUM 4 #define KERNEL_STACK_SIZE (unsigned long)1024*2 /* CPU-specific state of this task */ struct Thread { unsigned long ip; unsigned long sp; }; typedef struct PCB{ // 進程號 int pid; // -1 unrunnable, 0 runnable, >0 stopped volatile long state; // 進程堆棧 unsigned long stack[KERNEL_STACK_SIZE]; // CPU-specific state of this task struct Thread thread; // 入口事件 unsigned long task_entry; // 下一個pcb塊的位置 struct PCB *next; }tPCB; void my_schedule(void);
由代碼可知,mypcb.h主要通過結構體的方式定義了進程的基本信息:進程狀態、進程id以及進程的堆棧信息等,其余信息已在註釋中表明。
myinterrupt.c
/* * linux/mykernel/myinterrupt.c * * Kernel internal my_timer_handler * * Copyright (C) 2013 Mengning * */ #include <linux/types.h> #include <linux/string.h> #include <linux/ctype.h> #include <linux/tty.h> #include <linux/vmalloc.h> #include "mypcb.h" extern tPCB task[MAX_TASK_NUM]; extern tPCB * my_current_task; extern volatile int my_need_sched; volatile int time_count = 0; /* * Called by timer interrupt. * it runs in the name of current running process, * so it use kernel stack of current running process */ void my_timer_handler(void) { #if 1 if(time_count%1000 == 0 && my_need_sched != 1) { printk(KERN_NOTICE ">>>my_timer_handler here<<<\n"); my_need_sched = 1; } time_count ++ ; #endif return; } 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( "pushl %%ebp\n\t" /* save ebp */ "movl %%esp,%0\n\t" /* save esp */ "movl %2,%%esp\n\t" /* restore esp */ "movl $1f,%1\n\t" /* save eip */ "pushl %3\n\t" "ret\n\t" /* restore eip */ "1:\t" /* next process start here */ "popl %%ebp\n\t" : "=m" (prev->thread.sp),"=m" (prev->thread.ip) : "m" (next->thread.sp),"m" (next->thread.ip) ); } return; }
由以上代碼可知,myinterrupt.c主要實現兩個函數:my_time_handler和my_schedule。
- my_time_handler(), 每1000毫秒產生一個時鐘中斷,同時設置my_need_sched=1,mymain.c中的函數就會調用my_schedule()以執行進程切換的操作。
- my_schedule(),用於進程的切換操作。
- 聲明next,pre指針執行下一個需要調度的進程和上一個進程
- 判斷下一個需要調度的進程的狀態值,為0則切換
- 通過匯編實現進程的切換。保存現場,將當前線程相關內容入棧,上一線程相關內容出棧,然後切換線程ip;
mymain.c,
/* * linux/mykernel/mymain.c * * Kernel internal my_start_kernel * * Copyright (C) 2013 Mengning * */ #include <linux/types.h> #include <linux/string.h> #include <linux/ctype.h> #include <linux/tty.h> #include <linux/vmalloc.h> #include "mypcb.h" tPCB task[MAX_TASK_NUM]; tPCB * my_current_task = NULL; volatile int my_need_sched = 0; void my_process(void); void __init my_start_kernel(void) { int pid = 0; int i; /* 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]; /*fork more process */ for(i=1;i<MAX_TASK_NUM;i++) { memcpy(&task[i],&task[0],sizeof(tPCB)); task[i].pid = i; //*(&task[i].stack[KERNEL_STACK_SIZE-1] - 1) = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1]; 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]; } /* start process 0 by task[0] */ pid = 0; my_current_task = &task[pid]; asm volatile( "movl %1,%%esp\n\t" /* set task[pid].thread.sp to esp */ "pushl %1\n\t" /* push ebp */ "pushl %0\n\t" /* push task[pid].thread.ip */ "ret\n\t" /* pop task[pid].thread.ip to eip */ : : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/ ); } int i = 0; 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); } } }
mymain.c中,主要是進程的初始化並負責啟動進程:
- 初始化一個進程 。為其分配進程編號、進程狀態state、進程堆棧、線程、任務實體等,並將其next指針指向自己。
- 初始化更多的進程 。根據第一個進程的部分資源,包括內存拷貝函數的運用,將0號進程的信息進行了復制,修改pid等信息。
- 設置當前進程 。因為是初始化,所以當前進程就決定給0號進程了,通過執行嵌入式匯編代碼,開始執行mykernel內核。
- my_process函數。每10000000次,打印當前進程的pid,全局變量my_need_sched,通過對my_need_sched進行判斷,若為1,則通知正在執行的進程執行調度程序,然後打印調度後的進程pid。
三、實驗總結
操作系統是指控制和管理整個計算機系統的硬件和軟件資源,並合理地組織調度計算機的工作和資源的分配,以提供給用戶和其他軟件方便的接口和環境的程序集合。
通過本次實驗:
- 為什麽要進行處理機調度?
在多道程序系統中,進程的數量往往多於處理機的個數,進程爭用處理機的情況在所難免。處理機調度是對處理機進行分配,按照一定的算法選擇一個進程並將處理機分配給它運行,以實現進程並發執行。 - 什麽時時間片輪轉調度算法?
系統將所有就緒進程按到達時間的先後次序排成一個隊列,進程調度總是選擇就緒隊列中的第一個進程執行,即先來先服務,但僅能運行一個時間片,如100ms。在使用完一個進程片後,即使進程還未完成,它也必須釋放處理機給下一個就緒的進程,然會回到就緒隊列的末尾重新排隊。 - 操作系統(內核)是如何工作的?
- Linux是一個多進程的操作系統,所以,其他的進程必須等到正在運行的進程空閑CPU後才能運行。
- 進程是動態執行的實體,內核是進程的管理者。進程不但包括程序的指令和數據,而且包括程序計數器和CPU的所有寄存器以及存儲臨時數據的進程堆棧。
- 系統有一個進程控制表(process table),一個進程就是其中的一項。
基於mykernel完成時間片輪詢多道進程的簡單內核