1. 程式人生 > >操作系統工作流程

操作系統工作流程

圖片 time_c 堆棧 store memcpy 封裝 intern 時間片輪轉 基礎上

計算機有三個關鍵性機制:存儲程序計算機,堆棧機制和中斷機制
第一章中已經重點學習了存儲程序的計算機,接下來我們重點學習堆棧機制和中斷機制。

堆棧機制

堆棧機制是高級語言可以實現的基礎機制,是C語言程序運行時必須使用的記錄函數調用路徑和參數存儲的空間,他的具體作用有:記錄函數調用框架,傳遞函數參數,保存返回值的地址,提供函數內部局部變量的存儲空間等
堆棧相關的寄存器有:
1.ESP(堆棧指針寄存器)以及EBP(記錄當前函數調用基址的基址指針寄存器)
2.CS:EIP:總是指向下一條的指令地址。順序執行時,總是指向地址連續的下一條指令;跳轉/分支執行時,CS:EIP的值會根據程序需要被修改。
3.EAX:保存返回值。如果有多個返回值,則返回一個內存地址。
堆棧相關操作:
push:棧頂地址減少4個字節,並將操作數放入棧頂存儲單元
pop:棧頂地址增加四個字節,並將棧頂存儲單元的內容放入操作數
call:將當前CS:EIP的值壓入棧頂,CS;EIP指向被調用函數的入口地址
ret:從棧頂彈出原來保存在這裏的CS:EIP的值,放入CS:EIP的值
enter和leave:一步對函數調用堆棧框架的建立和拆除進行封裝
參數傳遞;從左到右依次壓棧

中斷機制實驗

虛擬X86的cpu硬件平臺搭建

本次實驗在實驗樓環境中進行,實驗代碼如下

cd LinuxKernel/linux-3.9.4
rm -rf mykernel
patch -p1 < ../mykernel_for_linux3.9.4sc.patch
make allnoconfig
make #編譯內核
qemu -kernel arch/x86/boot/bzImage

搭建起來後內核的啟動效果如下
技術分享圖片

在mykernel基礎上完成一個簡單的時間片輪轉多道程序

在前面試驗的基礎上cd mykernel ,增加一個mypcb.h的頭文件
mypcb.h

#define MAX_TASK_NUM        4
#define KERNEL_STACK_SIZE   1024*8
/* CPU-specific state of this task */
struct Thread {
    unsigned long       ip;  
    unsigned long       sp;  
};

typedef struct PCB{
    int pid;                
    volatile long state;   
    char stack[KERNEL_STACK_SIZE];
    /* CPU-specific state of this task */
    struct Thread thread;
    unsigned long   task_entry;  
    struct PCB *next; 
}tPCB;//pcb結構體定義

void my_schedule(void);

修改mymain.c文件如下

#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];  //PCB的數組task
tPCB * my_current_task = NULL; //當前task指針
volatile int my_need_sched = 0; //是否需要調度

void my_process(void); 

void __init my_start_kernel(void) //mykernel內核代碼入口
{
    int pid = 0;
    int i;
    /* 初始化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其他進程 */
    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];
    }
    
    /* 用task[0]開始0號進程 */
    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)   /* input c or d mean %ecx/%edx*/
    );
}   

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文件如下:

/*
 *  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);  
        /* 進程切換 */
        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; 
}

重新make編譯後qemu窗口查看,得到如下結果:
技術分享圖片
技術分享圖片

遇到問題
1.make編譯出錯
2.重新啟動後內核啟動效果沒有變化
解決方法
1.在mykernel目錄下修改代碼
2.換了一組新的內核代碼,最開始使用的代碼切換進程過程不完成

操作系統工作流程