操作系统是如何运行的(时间片轮转多道程序内核代码)

操作系统是如何运行的(时间片轮转多道程序内核代码)

 

操作系统是如何运行的。

继上一篇计算机是如何运行的

使用实验楼的虚拟机(直接使用3.9.4内核)使用qemu 启动

cd LinuxKernel/linux-3.9.4

qemu -kernel arch/x86/boot/bzImage

 

打开终端

从qemu窗口可以看到my_start_kernel在执行 同时my_timer_handler时钟中断处理程序周期性执行

 

mymain.c和myinterrupt.c

 

vi mymain.c

跳过之前include 头文件(硬件初始化)                   

下面函数 实际为操作系统入口  运行100000次打印一次语句

初始化好的cpu从 my_start_kernel执行

void __init my_start_kernel(void)

{

    inti = 0;

   while(1)

    {

       i++;

       if(i%100000 == 0)

           printk(KERN_NOTICE "my_start_kernel here  %d \n",i);

           

    }

}

 

vi myinterrupt.c  中断发生时进程中断处理 下面函数实现中断处理

即时钟中断机制周期执行

void my_timer_handler(void)

{

         printk(KERN_NOTICE"\n>>>>>>>>>>>>>>>>>my_timer_handlerhere<<<<<<<<<<<<<<<<<<\n\n");

}

 

分析进程的启动和切换机制

 

从github下载 代码

git clone https://github.com/mengning/mykernel

定义文件头 方便多个C文件引用头 达到 公用的目的

1mypcb.h

#defineMAX_TASK_NUM       4

#defineKERNEL_STACK_SIZE  1024*8

/*CPU-specificstate of this task */

structThread{

   unsignedlong                 ip;

   unsignedlong                 sp;

};

 

typedefstructPCB{   //定义进程管理相关的数据结构

   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; //包括一个ip 一个sp

   unsigned long        task_entry;  //调用程序入口

   struct PCB *next; //进程链表

}tPCB;

 

void my_schedule(void);//函数调度器

 

2mymain.c 

 

#include"mypcb.h"

 

tPCBtask[MAX_TASK_NUM];

tPCB*my_current_task = NULL;

volatileintmy_need_sched = 0;

 

void my_process(void);

 

 

void__initmy_start_kernel(void)

{

   int pid = 0;

   int i;

   /* Initialize process 0*/

   task[pid].pid = pid;

   task[pid].state = 0;/* -1 unrunnable, 0runnable, >0 stopped */

   task[pid].task_entry = task[pid].thread.ip= (unsigned long)my_process;

   task[pid].thread.sp = (unsignedlong)&task[pid].stack[KERNEL_STACK_SIZE-1];

   task[pid].next = &task[pid];

分析

  首先启动0号进程

任务的进程当前pid0状态设置为0(运行状态),设置任务的入口为线程Threadip寄存器为my_process函数 定义堆栈的站定,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 = (unsignedlong)&task[i].stack[KERNEL_STACK_SIZE-1];

       task[i].next = task[i-1].next;

       task[i-1].next = &task[i];

   }

fork很多进程  复制0号进程的一些状态过来 不一样的在于state-1未启动

每个进程有自己的堆栈(创建堆栈)

并把上一个task next 指向创建的task,当前的tasknext指向原来tasknext (类似链表中插入一个节点)

 

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

   pid = 0;

//启动0号进程

   my_current_task = &task[pid];

asmvolatile(

      "movl%1,%%esp\n\t"         /* settask[pid].thread.sp toesp */

      "pushl%1\n\t"           /* push ebp */

      "pushl%0\n\t"           /* push task[pid].thread.ip */

      "ret\n\t"               /* poptask[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*/

);

调用嵌入式汇编

1、当前进程SP放入ESP

2EBP压栈(此时是初始情况ebpesp指向同一位置,相当于esp压栈)

3、当前taskip压栈相当于myprocess调用处压栈

ret 后启动0号进程(poptask[pid].thread.ip to eip)开始执行性myprocess

内核初始化工程完成

}  

void my_process(void)

{

   int i = 0;

   while(1)

   {

       i++;

       if(i%10000000 == 0)

       {

           printk(KERN_NOTICE "this isprocess %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);

       }    

   }

}

 

所有进程均执行的均是my_process方法

里面代码含义为执行1千万次查看是否需要调度

 

3myinterrupt.c

#include"mypcb.h"

 引入公用结构体

externtPCBtask[MAX_TASK_NUM];

externtPCB* my_current_task;

externvolatileint my_need_sched;

volatileinttime_count = 0;

引入全局变量

 

/*

 *Called by timer interrupt.

 *it runs in the name of current runningprocess,

 *so it use kernel stack of current runningprocess

 */

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;       

}

 执行1000次判断是否为不需要调度切换需要调度状态

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, 0runnable, >0 stopped */

   {

      /*switch to next process */

      asmvolatile(       

           "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 %dto%d<<<\n",prev->pid,next->pid);     

   }

   else

   {

       next->state = 0;

       my_current_task = next;

       printk(KERN_NOTICE">>>switch %dto%d<<<\n",prev->pid,next->pid);

      /*switch to new process */

      asmvolatile(       

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

}

判断当前进程的下一个进程是否已经启动

来分别执行两段不同的嵌入式汇编

如果下个进程的状态为已运行

则保存当前进程的EBP,保所espthreadsp

把下个进程的ESP放到当前的ESP

报标号为1的位置保存到eip

压下个进程的eip入栈

ret  下个进程开始执行

如果下个进程为新进程从未运行过

首先进程状态置为运行状态,把这个进程作为当前执行的进程

然后执行嵌入式汇编

保存ebp 保存esp  因为没有执行过这个进程 EBPESP 指向同一个位置,所以把要切换的的ESP分别存入当前进程的ESP EBP

SAVE  EIP

要切换的进程入口保存起来

ret 开始执行要切换的进程的入口

 

 

 

嵌入式汇编主要是进程状态的切换保存当前进程上下文切换下一个进程到当前进程,切换执行完后可以出栈再切换回来

 

对比计算机意见三大法宝,存储程序计算机,函数堆栈调用,中断

操作系统也有两大法宝 就是保存现场和回复现场(中断上下文和进程上下文切换),

作者:李嘉

原创作品

转载请注明出处 《Linux内核分析》MOOC课

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值