[关闭]
@gain 2016-03-06T14:29:48.000000Z 字数 5214 阅读 1421

一个简单的时间片轮转Linux内核分析

Linux内核 操作系统


by 干宇航
原创作品转载请注明出处 《Linux内核分析》MOOC课程

实验环境:windows10 pro + Virtual box 5.14 下 Ubuntu 15.10 64位

采用孟宁老师提供的mykernel环境

环境截图

mypcb.h 代码

  1. /* mykernel--Simple simulation of the linux OS process schedule
  2. *
  3. * linux/mykernel/mypcb.h
  4. *
  5. * Kernel internal my_timer_handler
  6. *
  7. * Copyright (C) 2013 Mengning
  8. *
  9. * Modified 2014 Yunquan Zhang <zhangyunquan@hotmail.com>
  10. *
  11. *
  12. * You can redistribute or modify this program under the terms
  13. * of the GNU General Public License as published by
  14. * the Free Software Foundation.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #define MAX_TASK_NUM 10 // max num of task in system
  20. #define KERNEL_STACK_SIZE 1024*8
  21. #define PRIORITY_MAX 30 //priority range from 0 to 30
  22. /* CPU-specific state of this task */
  23. struct Thread {
  24. unsigned long ip;//point to cpu run address
  25. unsigned long sp;//point to the thread stack's top address
  26. //todo add other attrubte of system thread
  27. };
  28. //PCB Struct
  29. typedef struct PCB{
  30. int pid; // pcb id
  31. volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
  32. char stack[KERNEL_STACK_SIZE];// each pcb stack size is 1024*8
  33. /* CPU-specific state of this task */
  34. struct Thread thread;
  35. unsigned long task_entry;//the task execute entry memory address
  36. struct PCB *next;//pcb is a circular linked list
  37. unsigned long priority;// task priority ////////
  38. //todo add other attrubte of process control block
  39. }tPCB;
  40. //void my_schedule(int pid);
  41. void my_schedule(void);

首先来看mypcb.h里面有什么东西
thread 里面 ip,sp分别是eip和esp
PCB

然后定义了一个调度器
my_schedule

mymain.c代码

  1. /*
  2. * linux/mykernel/mymain.c
  3. *
  4. * Kernel internal my_start_kernel
  5. *
  6. * Copyright (C) 2013 Mengning
  7. *
  8. */
  9. #include <linux/types.h>
  10. #include <linux/string.h>
  11. #include <linux/ctype.h>
  12. #include <linux/tty.h>
  13. #include <linux/vmalloc.h>
  14. #include "mypcb.h"
  15. tPCB task[MAX_TASK_NUM];
  16. tPCB * my_current_task = NULL;
  17. volatile int my_need_sched = 0;//是否需要调度
  18. void my_process(void);
  19. void __init my_start_kernel(void)
  20. {
  21. int pid = 0;
  22. int i;
  23. /* Initialize process 0*/
  24. task[pid].pid = pid;
  25. task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */
  26. task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;
  27. task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
  28. task[pid].next = &task[pid];
  29. /*fork more process */
  30. for(i=1;i<MAX_TASK_NUM;i++)
  31. {
  32. memcpy(&task[i],&task[0],sizeof(tPCB));
  33. task[i].pid = i;
  34. task[i].state = -1;
  35. task[i].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];
  36. task[i].next = task[i-1].next;
  37. task[i-1].next = &task[i];
  38. }
  39. /* start process 0 by task[0] */
  40. pid = 0;
  41. my_current_task = &task[pid];
  42. asm volatile(
  43. "movl %1,%%esp\n\t" /* set task[pid].thread.sp to esp */
  44. "pushl %1\n\t" /* push ebp */
  45. "pushl %0\n\t" /* push task[pid].thread.ip */
  46. "ret\n\t" /* pop task[pid].thread.ip to eip */
  47. "popl %%ebp\n\t"
  48. :
  49. : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/
  50. );
  51. }
  52. void my_process(void)
  53. {
  54. int i = 0;
  55. while(1)
  56. {
  57. i++;
  58. if(i%10000000 == 0)
  59. {
  60. printk(KERN_NOTICE "this is process %d -\n",my_current_task->pid);
  61. if(my_need_sched == 1)
  62. {
  63. my_need_sched = 0;
  64. my_schedule();
  65. }
  66. printk(KERN_NOTICE "this is process %d +\n",my_current_task->pid);
  67. }
  68. }
  69. }

先把mypcb.h包含进来
先声明一个task的数组
当前的task的指针
声明一个标志 my_need_sched

my_start_kernel

零号进程初始化
状态就绪
起点、入口是my_process
即函数执行的是my_process
堆栈的栈顶,定义了一个stack
指针指向它自己,因为系统只有它自己。

将零号进程的状态,复制过来

每个进程有它自己的堆栈
新fork的进程添加到这个task链表的尾部

开始执行零号进程
有一段汇编代码,比较关键

  1. asm volatile(
  2. "movl %1,%%esp\n\t" /* set task[pid].thread.sp to esp */
  3. "pushl %1\n\t" /* push ebp */
  4. "pushl %0\n\t" /* push task[pid].thread.ip */
  5. "ret\n\t" /* pop task[pid].thread.ip to eip */
  6. "popl %%ebp\n\t"
  7. :
  8. : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/
  9. );

ret之后0号进程启动起来

下面我们来看一下my_process的代码
执行一千万次,之后主动调度

myinterrupt.c

  1. /*
  2. * linux/mykernel/myinterrupt.c
  3. *
  4. * Kernel internal my_timer_handler
  5. *
  6. * Copyright (C) 2013 Mengning
  7. *
  8. */
  9. #include <linux/types.h>
  10. #include <linux/string.h>
  11. #include <linux/ctype.h>
  12. #include <linux/tty.h>
  13. #include <linux/vmalloc.h>
  14. #include "mypcb.h"
  15. extern tPCB task[MAX_TASK_NUM];
  16. extern tPCB * my_current_task;
  17. extern volatile int my_need_sched;
  18. volatile int time_count = 0;
  19. /*
  20. * Called by timer interrupt.
  21. * it runs in the name of current running process,
  22. * so it use kernel stack of current running process
  23. */
  24. void my_timer_handler(void)
  25. {
  26. #if 1
  27. if(time_count%100 == 0 && my_need_sched != 1)
  28. {
  29. printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");
  30. my_need_sched = 1;
  31. }
  32. time_count ++ ;
  33. #endif
  34. return;
  35. }
  36. void my_schedule(void)
  37. {
  38. tPCB * next;
  39. tPCB * prev;
  40. if(my_current_task == NULL
  41. || my_current_task->next == NULL)
  42. {
  43. return;
  44. }
  45. printk(KERN_NOTICE ">>>my_schedule<<<\n");
  46. /* schedule */
  47. next = my_current_task->next;
  48. prev = my_current_task;
  49. if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
  50. {
  51. /* switch to next process */
  52. asm volatile(
  53. "pushl %%ebp\n\t" /* save ebp */
  54. "movl %%esp,%0\n\t" /* save esp */
  55. "movl %2,%%esp\n\t" /* restore esp */
  56. "movl $1f,%1\n\t" /* save eip */
  57. "pushl %3\n\t"
  58. "ret\n\t" /* restore eip */
  59. "1:\t" /* next process start here */
  60. "popl %%ebp\n\t"
  61. : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
  62. : "m" (next->thread.sp),"m" (next->thread.ip)
  63. );
  64. my_current_task = next;
  65. printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
  66. }
  67. else
  68. {
  69. next->state = 0;
  70. my_current_task = next;
  71. printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
  72. /* switch to new process */
  73. asm volatile(
  74. "pushl %%ebp\n\t" /* save ebp */
  75. "movl %%esp,%0\n\t" /* save esp */
  76. "movl %2,%%esp\n\t" /* restore esp */
  77. "movl %2,%%ebp\n\t" /* restore ebp */
  78. "movl $1f,%1\n\t" /* save eip */
  79. "pushl %3\n\t"
  80. "ret\n\t" /* restore eip */
  81. : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
  82. : "m" (next->thread.sp),"m" (next->thread.ip)
  83. );
  84. }
  85. return;
  86. }

再来看myinterrupt的代码

全局变量声明三个变量
定义时间片 time_count

my_timer_handler中
当调度过一次之后,把my_need_sched赋为1

然后来看my_schedule的函数

前面链表相关的操作和检查

下面if 检查是否就绪
然后进行上下文切换。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注