自然flw

linux内核学习之二 一个精简内核的分析(基于时间片轮转)

一   实验过程及效果

1.准备好相关的代码,分别是mymain.c,mypcb.h,myinterrupt.c ,如下图,make

 

make成功:

 在qemu创建的虚拟环境下的运行效果:(使用的命令如上图所示)

效果分析:可以看到进程在不断切换,分别有进程0,1,2,3,每隔一段时间就进行一次切换。

二  具体代码

mypcb.h

#define MAX_TASK_NUM  4                            //定义最大任务(进程数)  
#define KERNEL_STACK_SIZE 1024*8            //定义内核堆栈的大小

struct Thread{
    unsigned long   ip;
    unsigned long   sp;
};

typedef struct PCB{                                        //定义进程控制块
     int pid;
     volatile long state;
     char stack[KERNEL_STACK_SIZE];

     struct Thread thread;
     unsigned long task_entry;
     struct PCB *next;

}tPCB;




void my_schedule(void);                                 //声明调度函数
mypcb.h

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 
 65 #include <asm/io.h>
 66 #include <asm/bugs.h>
 67 #include <asm/setup.h>
 68 #include <asm/sections.h>
 69 #include <asm/cacheflush.h>
 70 
 71 #ifdef CONFIG_X86_LOCAL_APIC
 72 #include <asm/smp.h>
 73 #endif
 74 
 75 #include"mypcb.h"
 76 
 77 tPCB task[MAX_TASK_NUM];
 78 tPCB * my_current_task = NULL;
 79 volatile int my_need_sched = 0;                        //定义调度标志
 80 
 81 void my_process(void);
 82 
 83 void __init my_start_kernel(void)                     //进程初始化,并使进程0开始运行
 84 {
 85     int pid=0;
 86     int i ;
 87 
 88     /*进程0初始化*/                                                     
 89 
 90     task[pid].pid = pid;
 91     task[pid].state = 0;
 92  task[pid].task_entry = task[pid].thread.ip = (unsigned long) my_process; //指定进程0的入口
 93 task[pid].thread.sp=(unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
 94     task[pid].next = &task[pid];
 95     for(i=1;i<MAX_TASK_NUM;i++)              //初始化进程1,2,3
 96     {
 97        memcpy(&task[i],&task[0],sizeof(tPCB));
 98        task[i].pid = i;
 99        task[i].state = -1;      /* -1 unrunnable, 0 runnable, >0 stopped */
100 task[i].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];
101        task[i].next = task[i-1].next;         //构建进程控制块链表
102        task[i-1].next = &task[i];
103 
104     }
105 
106     pid = 0;
107     my_current_task = &task[pid];
108     asm volatile(                                 //嵌入式汇编代码,使进程0运行起来
109             "movl %1,%%esp\n\t"
110             "pushl %1\n\t"
111             "pushl %0\n\t"
112             "ret \n\t"
113             "popl %%ebp\n\t"
114             :
115             :"c" (task[pid].thread.ip),"d" (task[pid].thread.sp)
116             
117             );
118    /* while(1)
119     {
120         i++;
121         if(i%100000 == 0)
122             printk(KERN_NOTICE "my_start_kernel here  %d \n",i);
123             
124     }*/
125 }
126 
127 void my_process(void)                         //进程的处理任务,每个进程都一样
128 {
129   int i = 0;
130   while(1)                                            //死循环
131        {                                       
132          i++;
133          if(i%10000000 == 0)                  //每10000000个指令周期检测一次是否调度
134          {
135        printk(KERN_NOTICE "THIS IS PROCESS %d -\n ",my_current_task->pid);
136          if(my_need_sched == 1)
137           {
138           my_need_sched =0;
139           my_schedule();                                   //发生调度
140           }
141      printk(KERN_NOTICE "this is process %d +\n",my_current_task->pid);
142 
143          }
144 
145        }
146 }
mymian.c

myinterrupt.c

#include <linux/kernel_stat.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/percpu.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/pid_namespace.h>
#include <linux/notifier.h>
#include <linux/thread_info.h>
#include <linux/time.h>
#include <linux/jiffies.h>
#include <linux/posix-timers.h>
#include <linux/cpu.h>
#include <linux/syscalls.h>
#include <linux/delay.h>
#include <linux/tick.h>
#include <linux/kallsyms.h>
#include <linux/irq_work.h>
#include <linux/sched.h>
#include <linux/sched/sysctl.h>
#include <linux/slab.h>

#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/div64.h>
#include <asm/timex.h>
#include <asm/io.h>

#define CREATE_TRACE_POINTS
#include <trace/events/timer.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.******/

    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");
                                                              //开始调度
         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"
                               "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
   {                   //如果下一任务(进程)不为0,从未被调度过
   next->state = 0;
   my_current_task = next;
   printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
   asm volatile(
           "pushl %%ebp\n\t"
           "movl %%esp,%0\n\t"
           "movl %2,%%esp\n\t"
           "movl %2,%%ebp\n\t"
           "movl $1f,%1\n\t"
           "pushl %3\n\t"
           "ret \n\t"
           : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
           : "m" (next->thread.sp),"m" (next->thread.ip)


        );
   
   }
 return;
}
myinterrupt.c

 

三  代码以及原理分析

    在代码的已经进行了简要的注释,下面进行分析:首先在mypcb.h文件中定义了进程控制块的结构体 ,其中成员pid代表的是进程号(在内核中唯一标识一个进程),state代表进程的状态,为了简化,并没有列出我们在操作系统中学到的所有的进程状态,0代表运行态,-1非运行态,>0停止态,“struct Thread thread;”定义了进程堆栈操作相关的寄存器;“task_entry;”定义了进程的入口,即具体的处理函数;“struct PCB *next;”定义指向下一进程的指针。首先通过“void __init my_start_kernel(void)”进行初始化0号进程,指定了进程0的入口my_process();并且初始化进程1,2,3,构建了进程链表,接下来用嵌入式汇编使进程运行起来,即执行my_process()函数,可以看到函数内部有个while(1)死循环,每10000000个周期打印进程号信息、检测一次是否发生了调度,如果发生了调度,清除调度的标识my_need_sched,(就是使本进程能够被抢占,相当于清信号量的意思)就执行调度函数my_schedule();调度的标识 my_need_sched在哪里发生改变呢?这涉及到定时器中断了——在my_interrupt.c文件可以看到“void my_timer_handler(void)”,这就是定时器中断的处理函数,当定时中断次数达到1000次时,改变一次调度标志 ,表示能够调度了(主动调度),接下来就是调度函数进行调度了,其中的两段嵌入式汇编就是调度的关键,本质就是修改eip,指向要调度的函数入口,并进行进程上下文的保存(堆栈的方式)。后面就是无限循环了, 每10000000个周期检测一次是否发生了调度,这在期间定时器中断修改调度标识,使能发生调度。。。。。。

 

     总结:个人对“操作系统如何工作的理解”

      操作系统是对硬件的抽象,摆脱了用户直接操作硬件(否则想要用电脑之前必须成为编程高手),所以操作系统在电脑大众化的过程中扮演了不可磨灭的角色。同时操作系统是对硬件的最大化利用,在多用户系统中,使每个用户感觉自己在独享电脑;在多任务系统中,使每个进程(任务)感觉自己在霸占cpu。。。闲话不多说,要想要操作系统实现上述的功能(不发生混乱的局面),必须安排好调度机制,一种机制就是时间片轮转机制,就是多用户或者多任务轮流使用cpu,且使用时间有限,时间到了必须让出cpu,但是谁能担当这个安排时间角色呢?这就需要硬件的帮忙——定时器,当设定好规定时间后,当定时的时间到了,就以中断(异步)方式通知cpu作出相应的动作。。。。这是这种机制使计算机有序完成各种任务——任务管理,内存管理,文件管理,I/O设备管理等。

 

声明:1.实验环境基于网易实验平台——实验楼的64位linux环境。 

        2. 代码借鉴了孟宁老师的代码。

        3.错误之处恳请指导~~~

 

 

 

by:方龙伟

原创作品 转载请注明出处

《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 

   

 

  

 

posted on 2016-03-05 16:48  自然flw  阅读(1020)  评论(0编辑  收藏  举报

导航