Linux内核分析,完成一个简单的时间片轮转多道程序内核代码

来源:互联网 发布:mm下载软件 编辑:程序博客网 时间:2024/04/24 10:04

                                   路过的小游侠+ 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 

实验二     时间片轮转多道程序

本周学习了一个简单的操作系统内核的工作模式。首先深入理解了函数调用堆栈的原理,函数开始使用enter初始化,结束使用leave恢复之前的状态。

并且总结了计算机操作系统的运行基本原理,即三大法宝:冯洛伊曼结构之存储程序计算机,高级语言基础之函数调用堆栈,以及实现多进程的中断。

实验:简单的时间片轮转多道程序的内核代码的实现

mypcb.h  mymain.c  myinterrupt.c这三个是mengning老师在git上的内核代码。

头文件mypcb.h

#define MAX_TASK_NUM        4
#define KERNEL_STACK_SIZE   1024*8
struct Thread {
    unsigned long ip;
    unsigned long sp;
};
typedef struct PCB{
    int pid;
    volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
    char stack[KERNEL_STACK_SIZE];
    /* CPU-specific state of this task */
    struct Thread thread;
    unsigned long task_entry;
    struct PCB *next;
}tPCB;

void myschedule(void);
头文件定义了结构体Thread:用来保存指令指针eip和栈顶指针esp,PCB中有 pid进程号,state状态(-1未运行,0运行中,大于1停止),栈stack,以及线程thread,

task_entry入口地址以及next指针。

#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];//线程调度状态不变,next指向自身。
/*fork more process */
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];
}
/* start process 0 by task[0] */
pid = 0;
my_current_task = &task[pid];
asm volatile(             //内嵌汇编,进程进栈,然后ret,使eip指向ebp完成进程的启动
"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 */
"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) //运行任务处理函数,循环一千万次,打印出自己pid检查my_need_schedule的标志,决定是否需要切换进程。
{
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);
}
}
}


myinterupt.c 包含了两个函数 my_timer_handler(void)和my_schedule(void),

mytimerhandler 负责时钟中断处理,myschedule负责进程的切换

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; 
}
在timecount为1000倍数时候,把my_need_sched置为1,此时在函数myprocess中符合if条件,调用my_schedule();

void my_schedule(void)

若下一个进程状态是可运行的,即就绪,则保存当前进程的上下文,将 ebp 压栈, esp 和 eip 的内容保存到 Thread 结构体中,从下一个进程控制块的 Thread 中取出 sp 和 ip 放到 esp 和 eip 中,从栈中弹出 ebp,完成进程切换。

若下一个进程不可运行,先要将其标记为可运行,然后再进行进程的上下文切换,这里与上面有一点不同的地方是此进程没有运行过,栈是空的,栈顶指针和栈基指针相同,所以是把 Thread 中的 sp 赋给 ebp。


{
    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 */
    {
    /* 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)
    ); 
    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"    /* save ebp */
        "movl %%esp,%0\n\t" /* save esp */
        "movl %2,%%esp\n\t"     /* restore  esp */
        "movl %2,%%ebp\n\t"     /* restore  ebp */
        "movl $1f,%1\n\t"       /* save eip */
        "pushl %3\n\t" 
        "ret\n\t"            /* restore  eip */
        : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
        : "m" (next->thread.sp),"m" (next->thread.ip)
    );          
    }   
    return;
}

 本次实验 深入了操作系统的内核原理,通过一个简单的操作系统的过程的实现,让我对操作系统的实现理解更加深刻。

0 0
原创粉丝点击