完成一个简单的时间片轮转多道程序内核

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

linux内核分析的第二课:完成一个简单的时间片轮转多道程序内核代码

使用下面的程序来进行分析,在实验楼的环境下操作:

mypcb.h:

  1. #define MAX_TASK_NUM 4
  2. #define KERNEL_STACK_SIZE 1024*8
  3. structTread{
  4. unsignedlong eip;
  5. unsignedlong esp;
  6. };
  7. typedef struct PCB{
  8. long pid;
  9. long state;
  10. char stack[KERNEL_STACK_SIZE];
  11. long task_entry;
  12. structTread thread;
  13. struct PCB*next;
  14. }PCB_t;
  15. void my_schedule(void);

mymain.c:

  1. #include<linux/types.h>
  2. #include<linux/module.h>
  3. #include<linux/proc_fs.h>
  4. #include<linux/kernel.h>
  5. #include<linux/syscalls.h>
  6. #include<linux/stackprotector.h>
  7. #include<linux/string.h>
  8. #include<linux/ctype.h>
  9. #include<linux/delay.h>
  10. #include<linux/ioport.h>
  11. #include<linux/init.h>
  12. #include<linux/initrd.h>
  13. #include<linux/bootmem.h>
  14. #include<linux/acpi.h>
  15. #include<linux/tty.h>
  16. #include<linux/percpu.h>
  17. #include<linux/kmod.h>
  18. #include<linux/vmalloc.h>
  19. #include<linux/kernel_stat.h>
  20. #include<linux/start_kernel.h>
  21. #include<linux/security.h>
  22. #include<linux/smp.h>
  23. #include<linux/profile.h>
  24. #include<linux/rcupdate.h>
  25. #include<linux/moduleparam.h>
  26. #include<linux/kallsyms.h>
  27. #include<linux/writeback.h>
  28. #include<linux/cpu.h>
  29. #include<linux/cpuset.h>
  30. #include<linux/cgroup.h>
  31. #include<linux/efi.h>
  32. #include<linux/tick.h>
  33. #include<linux/interrupt.h>
  34. #include<linux/taskstats_kern.h>
  35. #include<linux/delayacct.h>
  36. #include<linux/unistd.h>
  37. #include<linux/rmap.h>
  38. #include<linux/mempolicy.h>
  39. #include<linux/key.h>
  40. #include<linux/buffer_head.h>
  41. #include<linux/page_cgroup.h>
  42. #include<linux/debug_locks.h>
  43. #include<linux/debugobjects.h>
  44. #include<linux/lockdep.h>
  45. #include<linux/kmemleak.h>
  46. #include<linux/pid_namespace.h>
  47. #include<linux/device.h>
  48. #include<linux/kthread.h>
  49. #include<linux/sched.h>
  50. #include<linux/signal.h>
  51. #include<linux/idr.h>
  52. #include<linux/kgdb.h>
  53. #include<linux/ftrace.h>
  54. #include<linux/async.h>
  55. #include<linux/kmemcheck.h>
  56. #include<linux/sfi.h>
  57. #include<linux/shmem_fs.h>
  58. #include<linux/slab.h>
  59. #include<linux/perf_event.h>
  60. #include<linux/file.h>
  61. #include<linux/ptrace.h>
  62. #include<linux/blkdev.h>
  63. #include<linux/elevator.h>
  64. #include<asm/io.h>
  65. #include<asm/bugs.h>
  66. #include<asm/setup.h>
  67. #include<asm/sections.h>
  68. #include<asm/cacheflush.h>
  69. #include"mypcb.h"
  70. #ifdef CONFIG_X86_LOCAL_APIC
  71. #include<asm/smp.h>
  72. #endif

  1. PCB_t task[MAX_TASK_NUM];
  2. PCB_t* my_current = NULL;
  3. int my_need_schedule=0;
  4. void my_process(void);
/*my_start_kernel首先初始化任务0的PCB,然后在初始化其他任务的PCB*/
  1. void __init my_start_kernel(void)
  2. {
  3. int pid =0;
  4. /* initail task 0*/
  5. task[0].pid=0;
  6. task[0].state=0;
  7. task[0].task_entry= task[0].thread.eip=(unsignedlong)my_process;
  8. task[0].thread.esp=(unsignedlong)&task[0].stack;
  9. task[0].next=&task[0];
  10. /*fork task*/
  11. int i;
  12. for(i=1;i<MAX_TASK_NUM;i++){
  13. memcpy(&task[i],&task[0],sizeof(task[0]));
  14. task[i].pid= i;
  15. task[i].state=-1;
  16. task[i].thread.esp=(unsignedlong)&task[i].stack;
  17. task[i].next= task[i-1].next;
  18. task[i-1].next=&task[i];
  19. }
  20. pid=0;
  21. my_current=&task[0];
  22. /*这段内嵌汇编代码很重要,是用来启动任务0的*/
  23. asmvolatile(
  24. "movl %1,%%esp\n\t"//这里恢复任务0的内核栈
  25. "pushl %1\n\t" //保存任务0的内核栈
  26. "pushl %0\n\t" //把EIP入栈
  27. "ret\n\t" //执行完这条指令,eip寄存器的值就执行了任务0的入口(即my_process)
  28. "popl %%ebp\n\t" //正常情况下,是不会运行到这里的
  29. :
  30. :"c"(task[0].thread.eip),"d"(task[0].thread.esp)
  31. :
  32. );
  33. }
/*每执行10000000次判断一下是否需要调度*/
  1. void my_process(void)
  2. {
  3. int i=0;
  4. while(1){
  5. i++;
  6. if(i%10000000==0){
  7. i=0;
  8. printk("this is process %ld -\n",my_current->pid);
  9. if(my_need_schedule==1){
  10. my_need_schedule=0;
  11. my_schedule();
  12. }
  13. printk("this is process %ld +\n",my_current->pid);
  14. }
  15. }
  16. }
myinterrupt.c:

  1. #include<linux/kernel_stat.h>
  2. #include<linux/export.h>
  3. #include<linux/interrupt.h>
  4. #include<linux/percpu.h>
  5. #include<linux/init.h>
  6. #include<linux/mm.h>
  7. #include<linux/swap.h>
  8. #include<linux/pid_namespace.h>
  9. #include<linux/notifier.h>
  10. #include<linux/thread_info.h>
  11. #include<linux/time.h>
  12. #include<linux/jiffies.h>
  13. #include<linux/posix-timers.h>
  14. #include<linux/cpu.h>
  15. #include<linux/syscalls.h>
  16. #include<linux/delay.h>
  17. #include<linux/tick.h>
  18. #include<linux/kallsyms.h>
  19. #include<linux/irq_work.h>
  20. #include<linux/sched.h>
  21. #include<linux/sched/sysctl.h>
  22. #include<linux/slab.h>
  23. #include<asm/uaccess.h>
  24. #include<asm/unistd.h>
  25. #include<asm/div64.h>
  26. #include<asm/timex.h>
  27. #include<asm/io.h>
  28. #define CREATE_TRACE_POINTS
  29. #include<trace/events/timer.h>
  30. #include"mypcb.h"
  31. /*
  32. * Called by timer interrupt.
  33. */
  34. volatileint time_counter=0;
  35. externint my_need_schedule;
  36. externPCB_t* my_current;
  37. externPCB_t task[MAX_TASK_NUM];
  38. /*这是时钟中断程序,用来更新进程的时间片*/
  39. void my_timer_handler(void)
  40. {
  41. if((time_counter%1000==0)&&(my_need_schedule==0)){
  42. my_need_schedule=1;
  43. printk(">>>>time_hander here<<<<<\n");
  44. }
  45. time_counter++;
  46. }

  47. /*这是进程调度程序*/
  48. void my_schedule(void)
  49. {
  50. PCB_t* next;
  51. PCB_t* prev;
  52. if(my_current==NULL|| my_current->next==NULL){
  53. return;
  54. }
  55. printk(">>>here my_schedule<<<\n");
  56. next= my_current->next;
  57. prev= my_current;
  58. if(next->state==0){ //如果进程不是首次调度的
  59. asmvolatile(
  60. "pushl %%ebp\n\t" //保存当前进程的内核栈
  61. "movl %%esp,%1\n\t" //保存当前进程内核栈
  62. "movl $1f,%0\n\t" //保存当前进程执行位置
  63. "movl %3,%%esp\n\t" //恢复即将调度进程的内核栈
  64. "pushl %2\n\t" //把即将调度进程的eip压栈
  65. "ret\n\t" //把刚才压入的eip弹出到eip寄存器
  66. "1:"
  67. "popl %%ebp\n\t" //每次调度进程时都从这里开始执行
  68. :"=m"(prev->thread.eip),"=m"(prev->thread.esp)
  69. :"m"(next->thread.eip),"m"(next->thread.esp)
  70. :
  71. );
  72. my_current= next;
  73. printk("switch %ld to %ld\n",prev->pid,next->pid);
  74. }
  75. else //进程不是首次调度
  76. {
  77. next->state=0;
  78. printk("switch %ld to %ld\n",prev->pid,next->pid);
  79. asmvolatile(
  80. "pushl %%ebp\n\t" //保存一下ebp
  81. "movl %%esp,%1\n\t"//保存当前进程的内核栈
  82. "movl $1f,%0\n\t"//保存当前进程执行位置
  83. "movl %3,%%esp\n\t"//恢复即将调度进程的内核栈
  84. "movl %3,%%ebp\n\t"
  85. "pushl %2\n\t" //恢复执行环境
  86. "ret\n\t"
  87. "1:"
  88. "popl %%ebp\n\t"
  89. :"=m"(prev->thread.eip),"=m"(prev->thread.esp)
  90. :"m"(next->thread.eip),"m"(next->thread.esp)
  91. :
  92. );
  93. }
  94. }
  95. 总结:这段程序是对linux进程切换的模拟,进程切换总结起来就那么一句话,保存当前进程的上下文,恢复被调度进程的上下文,
  96. 这里只是简单的用一个链表来连接各个进程,并没有涉及到调度算法。




猜你喜欢

转载自blog.csdn.net/huibian1234/article/details/60333781