1. 程式人生 > >基於mykernel完成多進程的簡單內核

基於mykernel完成多進程的簡單內核

包括 sig ctype 可用 基於 重新編譯 try fork tar

原創作品轉載請註明出處 https://github.com/mengning/linuxkernel/

學號末三位:168


實驗要求:完成一個簡單的時間片輪轉多道程序內核代碼,參考代碼見mykernel版本庫

實驗環境:實驗樓中的虛擬機。


實驗步驟:

一、首先使用實驗樓的虛擬機打開shell,按照如下步驟運行

技術分享圖片

技術分享圖片

可以看到一個簡單的kernel已經運行起來了,在一直不停的循環輸出字符串。

二、進入mykernel文件夾,查看mymain.c和myinterrupt.c這兩個文件的內容

技術分享圖片

技術分享圖片

技術分享圖片

由以上文件中註釋可知,mymain.c文件用於執行進程,此處只是不停循環輸出字符串,myinterrupt.c用來寫時間片中斷程序,此處只是不停的輸出字符串。

三、從mykernel版本庫中下載對應的mymain.c和myinterrupt.c文件覆蓋當前目錄下的兩個文件,並下載進程結構體定義文件mypcb.c

使用maken重新編譯後,重新運行程序查看輸出。

技術分享圖片

由輸出內容可知,目前已實現進程切換的功能。


代碼分析:

一、mypcb.h文件

 1 /*
 2  *  linux/mykernel/mypcb.h
 3  *
 4  *  Kernel internal PCB types
 5  *
 6  *  Copyright (C) 2013  Mengning
 7  *
 8  */
 9 
10 #define
MAX_TASK_NUM 4 11 #define KERNEL_STACK_SIZE 1024*2 # unsigned long 12 /* CPU-specific state of this task */ 13 struct Thread { 14 unsigned long ip; 15 unsigned long sp; 16 }; 17 18 typedef struct PCB{ 19 int pid; 20 volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped
*/ 21 unsigned long stack[KERNEL_STACK_SIZE]; 22 /* CPU-specific state of this task */ 23 struct Thread thread; 24 unsigned long task_entry; 25 struct PCB *next; 26 }tPCB; 27 28 void my_schedule(void);

該文件為進程塊的描述文件,定義了進程的最大執行數為4,進程結構體中有進程號、進程狀態、下一進程的地址等信息。

二、myinterrupt.c文件

 1 /*
 2  *  linux/mykernel/myinterrupt.c
 3  *
 4  *  Kernel internal my_timer_handler
 5  *
 6  *  Copyright (C) 2013  Mengning
 7  *
 8  */
 9 #include <linux/types.h>
10 #include <linux/string.h>
11 #include <linux/ctype.h>
12 #include <linux/tty.h>
13 #include <linux/vmalloc.h>
14 
15 #include "mypcb.h"
16 
17 extern tPCB task[MAX_TASK_NUM];
18 extern tPCB * my_current_task;
19 extern volatile int my_need_sched;
20 volatile int time_count = 0;
21 
22 /*
23  * Called by timer interrupt.
24  * it runs in the name of current running process,
25  * so it use kernel stack of current running process
26  */
27 void my_timer_handler(void)
28 {
29 #if 1
30     if(time_count%1000 == 0 && my_need_sched != 1)
31     {
32         printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");
33         my_need_sched = 1;
34     } 
35     time_count ++ ;  
36 #endif
37     return;      
38 }
39 
40 void my_schedule(void)
41 {
42     tPCB * next;
43     tPCB * prev;
44 
45     if(my_current_task == NULL 
46         || my_current_task->next == NULL)
47     {
48         return;
49     }
50     printk(KERN_NOTICE ">>>my_schedule<<<\n");
51     /* schedule */
52     next = my_current_task->next;
53     prev = my_current_task;
54     if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
55     {        
56         my_current_task = next; 
57         printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);  
58         /* switch to next process */
59         asm volatile(    
60             "pushl %%ebp\n\t"         /* save ebp */
61             "movl %%esp,%0\n\t"     /* save esp */
62             "movl %2,%%esp\n\t"     /* restore  esp */
63             "movl $1f,%1\n\t"       /* save eip */    
64             "pushl %3\n\t" 
65             "ret\n\t"                 /* restore  eip */
66             "1:\t"                  /* next process start here */
67             "popl %%ebp\n\t"
68             : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
69             : "m" (next->thread.sp),"m" (next->thread.ip)
70         ); 
71     }  
72     return;    
73 }

my_timer_handler()函數周期性的向CPU發出中段請求,my_schedule()函數用於完成進程的上下文切換,其中嵌入的匯編代碼段,用於保存進程切換時的相關信息,可用於進程中斷之後的恢復。

三、mymain.c文件

 1 /*
 2  *  linux/mykernel/mymain.c
 3  *
 4  *  Kernel internal my_start_kernel
 5  *
 6  *  Copyright (C) 2013  Mengning
 7  *
 8  */
 9 #include <linux/types.h>
10 #include <linux/string.h>
11 #include <linux/ctype.h>
12 #include <linux/tty.h>
13 #include <linux/vmalloc.h>
14 
15 
16 #include "mypcb.h"
17 
18 tPCB task[MAX_TASK_NUM];
19 tPCB * my_current_task = NULL;
20 volatile int my_need_sched = 0;
21 
22 void my_process(void);
23 
24 
25 void __init my_start_kernel(void)
26 {
27     int pid = 0;
28     int i;
29     /* Initialize process 0*/
30     task[pid].pid = pid;
31     task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */
32     task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;
33     task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
34     task[pid].next = &task[pid];
35     /*fork more process */
36     for(i=1;i<MAX_TASK_NUM;i++)
37     {
38         memcpy(&task[i],&task[0],sizeof(tPCB));
39         task[i].pid = i;
40     //*(&task[i].stack[KERNEL_STACK_SIZE-1] - 1) = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];
41     task[i].thread.sp = (unsigned long)(&task[i].stack[KERNEL_STACK_SIZE-1]);
42         task[i].next = task[i-1].next;
43         task[i-1].next = &task[i];
44     }
45     /* start process 0 by task[0] */
46     pid = 0;
47     my_current_task = &task[pid];
48     asm volatile(
49         "movl %1,%%esp\n\t"     /* set task[pid].thread.sp to esp */
50         "pushl %1\n\t"             /* push ebp */
51         "pushl %0\n\t"             /* push task[pid].thread.ip */
52         "ret\n\t"                 /* pop task[pid].thread.ip to eip */
53         : 
54         : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)    /* input c or d mean %ecx/%edx*/
55     );
56 } 
57 
58 int i = 0;
59 
60 void my_process(void)
61 {    
62     while(1)
63     {
64         i++;
65         if(i%10000000 == 0)
66         {
67             printk(KERN_NOTICE "this is process %d -\n",my_current_task->pid);
68             if(my_need_sched == 1)
69             {
70                 my_need_sched = 0;
71                 my_schedule();
72             }
73             printk(KERN_NOTICE "this is process %d +\n",my_current_task->pid);
74         }     
75     }
76 }

mymain.c文件主要用於進程的初始化創建與啟動,每個進程每執行10000000次且my_need_sched的值為1的時候就會調用my_schedule()函數,進行進程的切換。通過該種方法,實現了進程的時間片輪轉調度。


總結:操作系統一個非常重要的功能就是對進程的管理,對進程進行管理的算法有很多,包括時間片輪轉調度算法、優先級調度算法、先來先服務調度算法等。本次實驗實現的時間片輪轉調度算法,主要的挑戰是實現上下文的中斷和進程的切換,處理方法在以上的mymain.c和myinterrupt.c文件中都有體現。

基於mykernel完成多進程的簡單內核