完成一個簡單的時間片輪轉多道程序內核

注:袁帥+ 原創作品轉載請註明出處 + 《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. 這裏只是簡單的用一個鏈表來連接各個進程,並沒有涉及到調度算法。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章