Chinaunix首页 | 论坛 | 博客
  • 博客访问: 403879
  • 博文数量: 53
  • 博客积分: 1910
  • 博客等级: 中尉
  • 技术积分: 1130
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-10 14:56
文章分类

全部博文(53)

文章存档

2013年(1)

2012年(17)

2011年(33)

2010年(2)

分类: LINUX

2011-05-09 17:59:27

    native linux(下称nlinux)中,实时进程的调度相对简单,而对于普通进程的调度比较复杂
2.6.23前用的是O(1) 调度,2.6.23 开始和谐了,使用了CFS完全公平调度,关于CFS调度的关键点在于
虚拟运行时钟,网上关于这方面的文章很多,写得都很不错,具体不敢乱讲

    oklinux base 于2.6.24 它的调度算法也是和nlinux一样,但是l4 kernel 作为
一个实时内核,它也有自己的调度算法,在关注他们之间是如何协调的问题前先来看下l4 kernel
的调度:
    api-slides-3.0.pdf 中关于l4 thread 的调度描述如下:
   
     OKL4 uses 256 hard priorities (0 - 255):
        * Priorities are strictly observer
        * The highest-priority runnable thread will always be scheduled
     Round-robin scheduling among threads of highest prio
     Aim is real-time scheduling, not fairness
        * Kernel on its own will never change the priority of a thread
        * Achieving fairness (if desired) is the job of user-level servers
        * Can use Schedule() syscall to adjust priorities
       
   大概意思就是有256个硬优先级, 可运行的最高优先级线程总是被调度(就是说总是占有cpu,真是弱肉强食啊,不过实时os 的要求就是这样),
可运行的一样优先级的线程按Round-robin (轮流运行),然后又说了目标是实时而不是公平
内核不会自己改变thread 的优先级,可以通过系统调用(l4 kernel的)调整优先级,如果
希望公平调度,这个可以是用户级服务来完成这个任务

   上面先放这,再转过来看下oklinux 的调度,首先一点可以肯定,对于普通进程,肯定还是使用
CFS调度,但l4 kernel 的thread 是实时调度,上面pdf中 Achieving fairness (if desired)
is the job of user-level servers 这句话可以看出要公平要自己(l4 user)来完成,
但如果把linux 的优先级设置到l4 thread 中,那肯定不行,这样就变成real time 调度了
从oklinux 进程创建的代码上看, 每个进程或者内核线程在创建l4 thread后,都调用
r  = L4_Set_Priority(task_thread_info(p)->user_tid, 98); /* prio都设置为98 */
按上面pdf 中关于相同prio调度的描述:可运行的一样优先级的线程按Round-robin (轮流运行)
但实际并不是轮流运行的,仍然按照CFS 的调度算法运行,也就是说按nlinux 优先级运行
看下进程的切换就可以了解原因:

   context_switch 开始,
   switch_mm 什么也不用做(除fass外),不用切换页全局目录 ,
            l4 thread  切换时,l4 kernel 会做这件事情
            user 访问不到硬件mmu 
  
   switch_to =>__switch_to=> arch_switch
  
   static inline struct thread_info* arch_switch(struct thread_info *prev, struct thread_info *next)
{
/* printk("switch  to  ip=%p, sp = %p\n", (void*)next->context.pc, (void*)next->context.sp); { */
 register __u32 from asm("r0") = (__u32)prev;
 register __u32 save asm("r1") = (__u32)&prev->context;
 register __u32 rest asm("r2") = (__u32)&next->context;

 __asm__ __volatile__ (
  "add lr, pc, #4   \n"
  "stmia %[save], {r4-r11, sp, lr} \n"
  "ldmia %[rest], {r4-r11, sp, pc} \n"
  : "+r" (from)
  : [rest] "r" (rest), [save] "r" (save)
  : "r3", "lr", "ip"
 );
 return (struct thread_info*)from;
/*}*/
}

   按上前面关于进程创建中所说的,创建进程的单位是l4 thread ,那么切换
进程就是切换l4 thread ,那么应该call l4 thread 切换api ,比如thread_swith,
或者L4_Start_SpIp ,虽然进程创建是有arch_setup_ipsp,但其实只是保持ip,sp
到上下文,实际代码如上,却不是这样的,而是保存prev的上下文,恢复next上下文,
返回点根据add lr, pc, #4, 恢复时运行到return (struct thread_info*)from;
这句,看起来就和nlinux 基本相似, 这样做的原因就是为了解决
l4 kernel 调度和linux 调度的协调问题,解决的方法就是,l4 thread 作为运行单位
但并没有call  thread start 去运行他(不运行就不会有l4 kernel 的调度),
而是通过linux 调度切换时,直接运行其地址来实现的

   所以看上去很简单,基本没变化,这个也是虚拟化所要求的,如果改变很大,那么虚拟

一个系统很复杂,那估计选择的人就少更少了 

  
修正一下:
   上面讲的不完全对, 虽然没有thread_switch ,但是L4_Start_XXX 调用还是有的
   但是L4_Start_SpIpFlags 在main thread中,因为main_thread的进程优先级别为99 ,高于user的98
   所以L4_Start_Sp_Ip 不会引起main thread 让出执行权,
   L4_Start_XXX 发生在两个点
     1.Syscall loop 的
       return_to_user:
       ...
       if (user_need_restart(curinfo)) { 
          L4_Start_SpIpFlags
      
       这之后prior 99的main_thread一l4 wait ,就切到L4_Start_SpIpFlags 设置的点去运行了
      
      2. l4_work_pending_preempt 需要重调度的那个情况
       如果需要重调度, 然后调用schede,返回时current 已经变了,然后L4_Start current
       然后go to retry 回到l4_wait 去让出main thread 运行权,然后运行 前面L4_Start current
       的那个
     
   这样就全了
   
   这样前面(不运行就不会有l4 kernel 的调度的讲法)就错了,那一运行,就同样98优先级的
   就要被l4 kernel Round-robin了, 那么办法就是L4_Stop_xxx,由oklinux 调度来
   L4_Stop_XXX 其他同一优先级别的thread,而选择oklinux 调度运行进程的l4 thread 来执行l4 api
   l4_start_xxx 运行,这样也就受控了
  
   L4_Stop_xxx的发生点:
      1.new_process_handler  创建进程时copy thread设置的stub
        任何新创建的进程的l4 thread 一出场就被停掉了,(l4 kernel 调度器没办法了)
      2. 其他在signal 相关部分,以后分析siganl 的时候再分析
   
  

阅读(1827) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~