操作系统进程切换简易分析

 linux精简操作系统设计代码

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


一、             准备工作

1、头文件mypcb.h:

/*

 *  linux/mykernel/mypcb.h

 *

 *  Kernel internal PCB types

 *

 *  Copyright (C) 2013  Mengning

 *

 */

 

#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;  /* -1 unrunnable, 0 runnable, >0 stopped */

    charstack[KERNEL_STACK_SIZE];

    /* CPU-specific state ofthis task */

    struct Thread thread;

    unsigned long  task_entry;

    struct PCB *next;

}tPCB;

 

voidmy_schedule(void);

 

2、mymain.c程序模块

/*

 *  linux/mykernel/mymain.c

 *

 *  Kernel internal my_start_kernel

 *

 *  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"

 

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;/* -1unrunnable, 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 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 bytask[0] */

    pid = 0;

    my_current_task =&task[pid];

        asm volatile(

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

{

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

        }    

    }

}

3、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 ofcurrent running process,

 * so it use kernel stack ofcurrent 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 */

    {

    /* 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;    

}

二、进程的启动

进程的启动是在mymain.c模块中的my_start_kernel函数。其中内嵌汇编代码部分是程序启动的关键:

程序刚开始启动时只有0号进程即pid=0.

/* start process 0 by task[0] */

    pid = 0;

    my_current_task =&task[pid];

        asm volatile(

    "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*/

        );

其中movl %1,%%esp\n\t 是将task[0].thread.sp赋给esp寄存器,即将0号进程的栈空间的栈顶指针赋给esp寄存器;

Pushl %1\n\t 是将ebp压栈;

"pushl %0\n\t" 是将task[0].thread.ip压栈,即将my_process函数地址压栈;

"ret\n\t" 是将task[0].thread.ip 弹出栈并赋给eip寄存器,此时eip寄存器指向my_process的地址,程序开始转向my_process函数执行。此时完成了启动0号进程的工作,my_process函数中含有无限循环代码while(1),每循环10000000次执行一次如下代码:“printk(KERN_NOTICE "this is process %d-\n",my_current_task->pid”,然后检验调度函数状态变量my_need_sched,如果该变量为1,则执行调度函数my_schedule,否则继续while循环块。my_need_sched是个volatile型的int变量,被初始化为0状态。my_need_sched变量值的改变是由于时钟中断机制周期性性执my_time_handler中断处理程序,执行完后中断返回总是可以回到my_start_kernel中断的位置继续执行。

"popl %%ebp\n\t" 是将原来ebp内容出栈讲给ebp寄存器。

 

三、进程的切换

当my_need_sched=1时,执行线程切换程序my_schedule函数。当第一次切换至某个进程时,执行my_schedule函数中的else语句,此时保存当前进程的堆栈及执行的下条指令的eip到相应的内存空间。然后重置eip寄存器的值为下个进程的my_process函数的开始地址。之后的进程切换都调用my_schedule 函数中的if分支,同理保存当前进程的堆栈及eip值到相应的内存,然后将下一个进程的上次执行的下一条指令的地值赋给eip寄存器。从而完成了进程的切换。

四、总结

从以上可以看出,进程上下文切换及中断上下文切换在操作系统中占有很重要的地位,以上仅仅分析了进程上下文切换过程。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值