UNIX/LINUX,2018-2019-1 20189205 《Linux 內核原理與分析》第三周作業

 2023-10-06 阅读 27 评论 0

摘要:操作系統是如何工作的 內核實現 本周學習內容是實現一個簡單的時間片輪轉多道程序內核。 首先需要在Linux虛擬機中再構建一個虛擬的x86CPU硬件平臺。這個平臺的構建我們利用了部分Linux 3.9.4版本源代碼以及網上的內核源代碼。 內核搭建好后其啟動效果如下: 而后࿰

操作系統是如何工作的

內核實現

本周學習內容是實現一個簡單的時間片輪轉多道程序內核。
首先需要在Linux虛擬機中再構建一個虛擬的x86CPU硬件平臺。這個平臺的構建我們利用了部分Linux 3.9.4版本源代碼以及網上的內核源代碼。
內核搭建好后其啟動效果如下:

1510297-20181028213250937-1275212402.png

而后,我們需要在搭建好的內核的基礎上,修改內核的源程序,構建我們所需的程序內核。
首先我們增加一個mypcb.h頭文件,用于定義進程控制塊(PCB)的數據結構。我們知道,在操作系統中,每一個進程都依賴于一個PCB,以用于進程調度,來實現進程的并發執行。因此,我們的PCB結構定義如下:
struct Thread {unsigned long       ip;        //進程eipunsigned long       sp;        //進程esp
};typedef struct PCB{int pid;                                //進程idvolatile long state;                    //進程狀態char stack[KERNEL_STACK_SIZE];          //進程堆棧struct Thread thread;                   //unsigned long   task_entry;             //進程的起始入口地址struct PCB *next;                       //指向下一個進程
}tPCB;void my_schedule(void);                     //進行進程調度
然后,我們修改一下mymain.c文件,此文件為內核的入口,用于內核各組件的初始化。我們通過這段程序來啟動內核的0號進程,其代碼如下:
#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 = 0void my_process(void);void __init my_start_kernel(void)
{int pid = 0int i;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];for(i=1;i<MAX_TASK_NUM;i++){memcpy(&task[i],&task[0],sizeof(tPCB));task[i].pid = i;task[i].state = -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];}pid = 0;my_current_task = &task[pid];asm volatile("movl %1,%%esp\n\t"   "pushl %1\n\t"          "pushl %0\n\t"        "ret\n\t"      "popl %%ebp\n\t": : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)   );
}void my_process(void)
{int i = 0;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);}     }
}
另外還要修改myinterrupt.c文件,增加進程調度函數my_schedule(void),以進行進程切換。其代碼如下:
#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;void my_timer_handler(void)
{
#if 1if(time_count%10000 == 0 && my_need_sched != 1){printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");my_need_sched = 1;} time_count ++ ;  
#endifreturn;     
}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 */{asm volatile(   "pushl %%ebp\n\t"       "movl %%esp,%0\n\t"  "movl %2,%%esp\n\t"   "movl $1f,%1\n\t"         "pushl %3\n\t" "ret\n\t"         "1:\t"                 "popl %%ebp\n\t": "=m" (prev->thread.sp),"=m" (prev->thread.ip): "m" (next->thread.sp),"m" (next->thread.ip)); my_current_task = next; printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);      }else{next->state = 0;my_current_task = next;printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);/* switch to new process */asm volatile(   "pushl %%ebp\n\t"      "movl %%esp,%0\n\t"    "movl %2,%%esp\n\t"    "movl %2,%%ebp\n\t"     "movl $1f,%1\n\t"         "pushl %3\n\t" "ret\n\t"          : "=m" (prev->thread.sp),"=m" (prev->thread.ip): "m" (next->thread.sp),"m" (next->thread.ip));          }   return; 
}
可以看出,整個內核的運行過程是:
首先啟動內核,啟動0號進程進行初始化,其中0號進程的啟動部分使用了內嵌匯編代碼編寫:
    asm volatile(  "movl %1,%%esp\n\t"     /*將進程原堆棧棧頂的地址(這里是初始化的值)存入ESP寄存器 */  "pushl %1\n\t"          /* 將當前EBP寄存器值入棧 */  "pushl %0\n\t"          /* 將當前進程的EIP(這里是初始化的值)入棧*/  "ret\n\t"               /* ret命令正好可以讓入棧的進程EIP保存到EIP寄存器中*/  "popl %%ebp\n\t"       /*這里永遠不會被執行,知識與前面push指令結對出現,是一種編碼習慣*/:   : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)   ); 
當進程0的時間片用完(my_process中的i累加到100000000),將利用進程調度函數,運行進程1,其調度過程執行如下:
if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */{asm volatile(   "pushl %%ebp\n\t"       "movl %%esp,%0\n\t"  "movl %2,%%esp\n\t"   "movl $1f,%1\n\t"         "pushl %3\n\t" "ret\n\t"         "1:\t"                 "popl %%ebp\n\t": "=m" (prev->thread.sp),"=m" (prev->thread.ip): "m" (next->thread.sp),"m" (next->thread.ip)); my_current_task = next; printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);      }else{next->state = 0;my_current_task = next;printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);/* switch to new process */asm volatile(   "pushl %%ebp\n\t"      "movl %%esp,%0\n\t"    "movl %2,%%esp\n\t"    "movl %2,%%ebp\n\t"     "movl $1f,%1\n\t"         "pushl %3\n\t" "ret\n\t"          : "=m" (prev->thread.sp),"=m" (prev->thread.ip): "m" (next->thread.sp),"m" (next->thread.ip));}
由于進行1并未被執行過,其調度過程中執行else部分的進程調度代碼。
同理,當進程1的時間片用完,將再次利用調度函數將運行進程0,此時進程0是從新被執行,將使用if部分的進程調度代碼。
由此,每過一個時間片(通過my_process中i的不斷模100000000累加),內核將不斷在進程0與進程1之間切換。

問題與解決

本周在搭建內核環境時,在make的過程中,gcc報錯。

1510297-20181028223919446-524386126.png
原因是因為找不到compiler-gcc7.h頭文件,進入到文件目錄下,發現確實沒有該文件,只有compiler-gcc.h、compiler-gcc3.h、compiler-gcc4.h三個文件,猜測可能是因為下載的Linux源文件版本不同的原因。在網上下載compiler-gcc7.h文件后重新編譯,內核可正常運行。

轉載于:https://www.cnblogs.com/hzj20189205/p/9867412.html

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/2/119081.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息