L4Linux实现原理

2008-05-28 02:16
     以下资料全部来自Adam的diploma thesis (http://os.inf.tu-dresden.de/papers_ps/adam-diplom.pdf),如果你有时间,可以多多看看他的毕业论文,应该理解的更清楚。
   L4Linux是基于Fiasco微内核的跟普通Linux二进制完全兼容的Linux内核,目前的版本的是Linux-2.6.25,L4Linux-2.0.x, L4Linux-2.2.x和L4Linux-2.4.x已经不再维护。L4Linux是运行Fiasco用户空间的一个进程,由很多线程组成。而Native Linux则是运行在内核空间,如果不考虑kernel thread的话,那么Native Linux就相当于一个运行在内核空间的大进程,它实现了从处理器调度、内存管理到设备管理等所有一个操作系统的必备的功能。
    L4Linux也是一个Task(Process),由很多L4的线程组成,每一个线程完成一定的功能,如下:
    1)Linux Server Thread,这个线程的功能就是执行Linux kernel code。它是一个idle loop,等待处理来自Linux用户进程的系统调用,Exceptions和Page Faults。所以Linux Server Thread也是所有L4Linux User Process的Pager。
    2)Interrupt Threads。在L4家族系列中,所有的Interrupt都被转成同步的IPC消息,然后这些消息被发送到某个被Attach的线程,这个线程就叫Interrupt Thread,多个Interrupt被attach到某一个Thread是可行的,这个取决与L4Linux编译时候的设置。Hardware Interrupt是有优先级的,所以L4Linux通过赋予Interrupt Thread不同的优先级(软件优先级)来模拟硬件优先级,具体是如何模拟做到的是由Fiasco的中断管理服务器(omega)完成的。 Interrupt Thread的优先级高于Linux Server Thread,这样可以确保较快的中断相应。
    3)Signal Thread. 在Native Linux中,Signal通常是在Process返回到User Mode之前进行处理,所以在Singal发送和接受的过程中,有内核的参与。但是L4Linux Server和普通的L4Linux User process之间并不存在互斥关系,理论上,它们可以并行。为了模拟L4Linux Server和L4Linux User Process之间的这种关系,引入了Signal Thread的概念,Signal Thread位于L4Linux User Process的空间,当有Signal发生时,可以迫使L4Linux User Process放弃执行,进入L4Linux Server。
    4)System Call Emulation. 在X86Linux中,"int 0x80"会产生一个Trap,然后进入内核,执行相关的System Call,但是在L4中,“int 0x80“将会产生一个Exception,然后终止该程序,等待Exception Handler处理。V2和X.0只允许Local Thread Exception Handler,然后把这段代码map到每一个L4Linux User Process的空间。如果采取这种方式,可能需要修改glibc的一些代码。如果采用这种方式,进行系统调用时需要保存Process State,并且发送“system call“消息给L4Linux Server,执行完毕以后,重新载入Process State,继续执行。X.2允许非Native Thread Exception Handler执行,所以可以避免一个共享Exception Handler库,还有一次Kernel Entry(IPC)。另外的方法是修改glibc中关于System Call执行部分的代码。
     5)Scheduling 在Linux系统中,存在两个scheduler,一个是L4的,固定优先级+Robin,调度所有的L4 Thread;一个是L4Linux Server,调度所有的L4Linux User Process。设计的目标是让L4Linux User Process看起来似乎是L4Linux Server调度的。
      6) Memory 前面说过,L4Linux Server是所有的L4Linux User Process的Pager,负责管理所有的L4Linux内部并可以把内存map到L4Linux User Process的地址空间去,L4Linux也需要管理shadow page table,它不用去直接访问物理页表。
      7) Time Accounting Linux可以记录每个Linux User Process在用户空间的驻留时间,比如time命令,可以获取Linux User Process的执行时间长度,Native Linux通过在Timer Interrupt Handler中更新counter来完成,这样,每个Linux User Process就可以从前后两次Switch或者Counnter的时间差来计算总的运行时间。在L4Linux中,因为Timer Interrupt Handler是由单独的线程来完成的,所以需要通过其他办法,比如记录上次Schedulding的Process,这样,两次Process的时间差也可以作为当前Process的TimeAccounting。
     8) Linux Threads Linux可以运行多个Linux User Process(比如fork)在同一个Address Space,但是在L4Linux中,每一个L4Linux User Process都运行在一个单独的Address Space,相当于L4的一个Task。
     以上8个方面是L4Linux移植过程中特别需要关注的问题,也是理解L4Linux的基础,我认为任何Linux-Like的操作系统到L4系统的移植都可以采用如上的基本策略或者其中的某几个。L4跟Linux是完全不同的两种操作系统类型,从基本概念到实现方式上面都有太多的不同,L4到Linux的差距远远大于Minix/Unix/Partikle到Linux的差距。我的意思是说,虽然L4,Minix都是微内核,有一些基本的概念相同,如IPC,但是其余的比如内存管理,进程调度等方面,存在太多的不同。这些方法也是从L4Linux-2.0.x,L4Linux-2.2.x,L4Linux-2.4.x到L4Linux-2.6.x所遵循的基本原则。
      在上面所说的8点之中,有2个问题需要注意,其中有一个是关于Signal的问题,在V2和X.0的L4 API中, l4_thread_ex_regs是不允许修改当前Address Space之外的线程的,这给Signal的实现就带来了一点麻烦,因为不管是Signal的最终完成是需要内核的参与,都需要迫使L4Linux User Process切换到L4Linux Server去执行,在Native Linux下面,内核可以抢占用户进程,但是在L4Linux下面,如何使得一个User Process切换到另一个User Process,假如没有一个可以跨越Address Space进行线程修改的系统调用,Signal的实现只能依靠上面的那种方式,用一个单独的线程作为Signal Thread,去监听是否有新的Signal,如果有,Switch到L4Linux Server。当前,在L4Linux实现的时候,Fiasco不支持X.2API,但是对内核作了一些修改,提供了可以跨越Address Space的类l4_thread_ex_regs系统调用, l4_inter_task_ex_regs
      另一个问题是关于Exception和System Call的问题。在V2和X.0的API中,L4通过一个Thread Local Interrupt Descriptor来处理Exception和System Call,但是这些Thread Local Interrupt Descriptor实际上是L4 Kernel的一部分,L4 Kernel提供一个IDT(interrupt descriptor table),然后一旦发生系统调用,L4 Kernel会把这个IDT map到L4 Thread的地址空间,这个过程被称为“Exception Handler Installation",很显然,这个过程是需要改变privilege level的,而且需要执行一部分Exception Related的内核代码。很显然,如果我们仍然采取以前的策略,对于每一个Linux所触发的Exception或者System Call都采用L4 Kernel来处理,肯定会有较大的performance loss,这里会改进的余地。 V.2API提供了一种机制,称为"Exception IPC“, Exception IPC的本质含义就是可以把Exception Handler的代码放在用户空间,而且Exception Handler Thread可以在任意位置,一般有Exception发生,一个tagged IPC可以到以前指定的Exception Handler,IPC是synchronous,所以可以终止当前线程的执行,去执行Exception Handler,执行完毕,Exception Handler IPC返回的时候,继续执行以前的代码。这么作的好处是显而易见的,执行一个Exception或者System Call的时候,不需要切换完成真正的kernel space<->user space的切换,虽然中间有一个IPC存在,但是L4的IPC是Fast IPC,所需时间应该远远小于privilege switch的时间。
       但是在这个过程中,还有一个问题,就是我们需要从Exception Triggering Thread到Exception Handler发送一定的消息,X.2API提供了一种机制,UTCB(User Thread Control Block),用于传送Thread Control的消息从Exception Triggering Thread到Exception Handler。在这种情况下,每次只能最多传递2个参数的short IPC是不够的,需要Long IPC把UTCB搬过去。Exception IPC是目前L4Linux处理Exception, System Call以及Interrupt的一个比较有效的方式,因为a)避免在当前的Thread中包含Handler代码b)避免L4Linux Server和L4Linux User Process贡献Handler c)避免Addtional Kernel Entry。
       其他的从L4Linux User Process进入L4Linux Server的方法就是Page Fault,很显然,因为Pager是L4Linux Server,所以一旦Page Fault发生,就可以迫使L4Linux User process到L4Linux Server的切换,但是一般发生Page Fault的时候,kernel只是记录发生Page Fault的地址,并不记录发生Page Fault Thread的状态,所以要通过Page Fault的方式实现Signal, Interrupt, exception的处理,就要去修改Microkerne,即对所有的Exception进行类似Page Fault的处理,这样得不偿失(Microkernel对PageFault的处理是个例外), 相对前面的方法,这种方式更为复杂。
       采用Exception IPC也有一定的缺点,因为UTCB会记录当前Thread State有关的所有Register,所以对于相关Register较少的IA-32而言,这种方式较好,但是对于IA-64的很多的Register来说,Copy所有的Register Value是一项比较费时间的工作(我不清楚IA-64和IA-32的区别,不做评论), 而且Thread State相关的Register其实不是很多,所以Copy All也是没有必要的(从这里看出来,X.2可能还会继续update,至少UTCB不是很完美)。另:在PowerPC,IA64等平台下面,Kernel Entry很快(40Cycle,约相当于),所以是不是需要这么介意Kernel Entry的问题,本身也是个问题。但是不管怎么说,目前的L4Linux就是使用Exception IPC来解决Linux Exception问题的。

  前面说过,L4Linux Server同L4Linux User Process都是运行在L4用户空间的普通进程,他们之间的关系从优先级来讲,是平等的;但是Native Linux Kernel跟Linux User Process之间的关系就是上下级关系了,他们的运行是非此即彼的,而且Native Linux Kernel最终决定那个Linux User Process进行调度。因此,要模拟这种decision关系是一个很值得讨论的话题。对于uniprocess来讲,只能有一个L4Linux Server,而且L4Linux server和众多的L4Linux User Process也只能有一个运行;对于SMP系统来讲(Fiasco目前不支持SMP),每一个processor可以对应一个L4Linux Server,然后这些L4Linux Server可以决定其余的L4Linux User Process的运行,因为Fiasco不支持SMP,所以我认为目前的L4Linux支持SMP应该是模拟的,不是实质上的双核。而且本质上的L4Linux User Process也不是由L4Linux Server来调度的,但是为了模拟这种调度形式,必须对Scheduler进行修改,而且每一个L4Linux Server可以阻塞L4Linux User Process的运行。所有的L4Linux User Process都具有同样的静态优先级,这个似乎是由Fiasco决定的,因为Fiasco只是支持静态优先级,不支持动态优先级,而Native Linux是支持动态优先级的。
      所以这部分的关键点就是确保在L4Linux Server处于运行状态下的时候,L4Linux User Prcess必须被Block, 而这个Block状态必须等到L4Linux Server来解除Block。一般来说,这个取决于L4Linux Server被唤醒的方式,如果是因为Page Fault或者Exception,那么引起Page Fault和Exception的L4 Thread将会自动被Block直到这个Page Fault或者Exception被处理完为止;如果是由Interrupt被唤醒的,L4Linux Server要么被唤醒要么sleep,总之就是说,会引起L4Linux Server的状态变化。如果L4Linux Server是睡眠的,那么Interrupt Activity将会使得L4Linux Server组一些新的工作;如果L4Linux User Process正在运行,那么Interrupt Activity将会中断L4Linux User Process的运行,唤醒L4Linux Server;如果没有L4Linux User Process在运行,这个Interrupt Activity必须传递给L4Linux Server。
       这里需要注意的只有一个地方,就是当L4Linux User Process运行的时候,如果Interrupt Activity发生,那么当前的L4Linux User Process将会被L4Linux Server所Block,这个过程可以通过系统调用l4_inter_task_ex_regs来完成,然后进入L4Linux Server执行,这个方法的缺点在于overload,因为一个Interrupt Activity将会导致两次Event,一次是l4_inter_task_ex_regs,一次是Exception Handler。通过这种方式,L4Linux Server和L4Linux User Process的执行就会被序列化,不会出现两者concurrent execution的情况。
      前面讲述如何完成从L4Linux Server到L4Linux User Process的切换以及切换时候需要注意的问题,现在来看多个L4Linux User Process之间执行的问题,在这里,有一些关于Fiasco的Real-Time的问题需要理解,Fiasco的Real-Time问题将会在2,3天(不敢保证)以后写出。简单说来,就是Fiasco里面关于线程的执行有2种,一种是periodic mode,另一种是non-periodic mode,两种模式的区别在于前者会实现resource reservation,这对于Real-Time System将会是非常非常重要的(我们可以用resource reservation 和real-time这两个关键字搜索出很多很多的Paper,这也是Real-Time研究的重点)。L4Linux使用non-periodic mode,在这种模式,通过给每个L4Linux User Process一定的time amount,如果这个time amount用完了,就会产生一个timeout fault,然后这个fault的handler就是L4Linux Server。一旦某一个L4Linux User Process触发了timeout fault,那么这个L4Linux User Process就会被L4Linux Server阻塞,直到下一个timeout被分配到为止。
     在这种环境下,Native Linux所使用的Timer Interrupt驱动的Scheduling机制已经完全不必要了,完全可以采用以上的Timeout Fault Exception来模拟Timer Interrupt。下面关于L4Linux Server如何模拟Timer Interrupt调用的过程用文字似乎不太好说明,代码在(l4linux/arch/l4/kernel/main.c):
      static void __noreturn l4x_server_loop(void)   //这就是L4Linux Server的main函数中最主要的关于调度的部分,其他的部分以后有时间再写出来。
      l4x_server_loop的实现本身就是一个noreturn的loop,不断地用于接收新的IPC,检查这个IPC的TAG,如果表示EXIT,那么这个时候L4Linux就Exit了;如果不是Exit,则进入正常处理函数l4x_default。处理完以后,reply刚才触发IPC的线程。然后进入下一个IPC的处理。在l4x_default部分,则处理Page Fault和Exception(Timeout)。这里又提到了Preemption IPC的问题,以后再来解决。
      3.3.2 Timeout-Driven Flow有些问题还没有搞明白。
      另一个Interrupt L4Linux User Process的方法就是终止这个Process(比如清除这个Process的Ready状态),但是如果这样,依然需要获取这个Process的状态信息,当再次启动的时候,需要利用l4_inter_task_ex_regs 载入这个Process的相关寄存器.
       最后一个是L4 Thread到Linux Thread的Map,这个没有多少可以说的

 

  这部分要说明的是L4Linux的具体实现。
1, Timer emulation
L4内核处理了所有的Hardware Timer,所以在用户空间,IRQ0是不可见的,即使通过L4的IRQ管理程序omega0,也不能attch IRQ0。所以需要一个模式来模拟Timer。具体实现主要在irq_timer.c,Timer emulation如下:
pint = l4lx_kinfo->clock;
for (;;) {
   l4_msgdope_t result;
       l4_umword_t d1, d2;
       pint += 10000;
       if (pint > l4lx_kinfo->clock) {
             l4_rcv_timeout(l4_timeout_abs(pint,L4_TIMEOUT_ABS_V64_ms), &to);
              l4_ipc_receive(me, L4_IPC_SHORT_MSG, &d1, &d2, to, &result);
        }
        l4x_do_IRQ(irq, ctx);
        l4x_smp_broadcast_timer();

     因为Fiasco的scheduler granularity是1ms,所以sleep(10ms)的周期性任务就可以模拟100HZ的cpu timer的发生。
2,    Device interrupts emulation (暂缺)
3,    scheduling (暂缺)
4, L4Linux内存管理,这里所说的内存管理不考虑Linux的内存管理,而说说L4如何提供一定量的内存供Linux管理。(暂缺)
5, Multi-core of L4Linux (暂缺)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值