linux内核进程调度分析总结

作者:王鹤楼
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

在孟老师给出的mykernel的基础上增加mypcb.h

#define MAX_TASK_NUM 4  //定义总有4个任务/进程
#define KERNEL_STACK_SIZE 1024*8    //每个进程的堆栈大小为8KB

/*用于保有存当前进程的esp和eip*/
struct Thread {
    unsigned long ip; //eip
    unsigned long sp; //esp
};

/*定义进程结构体*/
typedef struct PCB {
    int pid;//进程ID
    volatile long state; //进程状态
    char stack[KERNEL_STACK_SIZE];//进程堆栈
    struct Thread thread;//保存esp、eip
    unsigned long task_entry;//f进程入口
    struct PCB * next;//在进程链表中指向下一个进程
    struct PCB * prev;//在进程链表中指向上一个进程
}tPCB;

/*声明调度函数*/
void my_schedule(void);

mymain.c文件:

#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 = 0;

    /*初始化0号进程*/
    task[pid].pid = pid;
    task[pid].state = 0;
    /*进程入口指向进程处理函数*/
    task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;
    /*指向进程栈进址*/
    task[pid].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE -1];
    /*进程链表中,next指向指向自己*/
    task[pid].next = &task[pid];

    /*创建其他进程*/
    for(i=1; i< MAX_TASK_NUM; i++)
    {
            /*把0号进程的状态拷贝给当前进程,不同成员值再单独赋值*/
            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];
        }

        /* 运行0号进程*/
        pid = 0;
        /*把0号进程的地址赋给当前进程指针*/
        my_current_task = &task[pid];
        /*嵌入式汇编中%0,%1,%2,%3表示下面的参数,编号从0开始*/
        asm volatile(
            "movl %1, %%esp\n\t" /*把当前进程的栈指针赋给esp*/
            "pushl %1\n\t"/*把当前进程的栈指保存到栈中*/
            "pushl %0\n\t"/*保存当前进程的eip到栈中*/
            "ret\n\t"/*返回操作*/
            "popl %%ebp\n\t"/*弹出栈顶内容赋给ebp*/
            :
            : "c" (task[pid].thread.ip), "d" (task[pid].thread.sp)/*ecx=当前进程的eip, edx=当前进程的栈指针*/
        );
        //到此为止,0号进程就启动起来了
        //end
}

void  my_process(void)
{
    int i=0;
    while(1)
    {
        i++;
        if(i%100 == 0) {
            printk(KERN_NOTICE "this is process %d- \n", my_current_task->pid);
            /*判断是否需要切换进程,1需要切换,0不需要切换*/
            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

#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(time_count % 100 == 0 && my_need_sched != 1) {
        printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");
        /*将进程切换标记为需要切换*/
        my_need_sched = 1;
    }
    /* 计数器自加 */
    time_count ++;

    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");

    next = my_current_task->next;
    prev = my_current_task;
    //state:-1表示不可用,0表示正在执行,>0表示没有运行
    if(next->state == 0) {
        /*切换进程*/
        asm volatile (
            "pushl %%ebp\n\t" /*保存当前进程的ebp*/
            "movl %%esp, %0\n\t" /*保存当前的栈指针到上一个进程的sp中*/
            "movl %2, %%esp\n\t" /*把下一个进程的sp赋给esp*/
            "movl $1f, %1\n\t" /*保存当前进程的eip到上进一个进程的ip中*/
            "pushl %3\n\t" /*将下一个进程的ip压到栈中*/
            "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);
        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"
            "popl %%ebp\n\t"
            :"=m" (prev->thread.sp), "=m" (prev->thread.ip)
            :"m" (next->thread.sp), "m" (next->thread.ip)
        );
    }
}

make 重新编译
运行程序:
qemu -kernel arch/x86/boot/bzImage

运行效果图
这里写图片描述
这里写图片描述

总结:
进程切换的关键是保存现场和恢复现场,当切换到下一个进程时要把当前进程用的变量,栈,和相关的寄存器保存到栈中,这一步叫保存现场。当切换回这个进程时再把这些变量,寄存器恢复,这一步叫恢复现场。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值