现在的位置: 首页 > 综合 > 正文

嵌入式实时Hypervisor:XtratuM (9)

2018年04月28日 ⁄ 综合 ⁄ 共 4427字 ⁄ 字号 评论关闭

1.1           域间通信工具

为了提高域之间的作业协作能力,域间通信(Inter-Domains Communication)工具被引入到XtratuM Hypervisor系统中。当前,XtratuM系统中存在两种数据通信工具,一种是针对数据流的命名管道(FIFO),另外一种是针对块数据的共享内存。命名管道是一种简单的流数据传输工具,采用先进先出的策略,不同于PIPE(管道),命名管道有固定的存储介质和名称,它可以被任何域通过名称打开和关闭。而管道只有存储介质,没有名称。管道的存在依附于创建管道的任务,当任务退出时,管道也就相应的被销毁了。而命名管道一经创建将会永久存在,知道用户明显的将其删除[37]

共享内存是不同于命名管道的数据通信模式,用户可以像打开普通文件一样打开共享内存,并且可以指定共享内存大小。与命名管道相比,共享内存是块设备,即任务可以随机的访问共享内存区间的任何地址的数据,并且不会因为地址的不同而使访问时间有所差别。但是FIFO类似的流数据通信工具,数据的传输和访问必须有一个先后顺序。因此,命名管道与共享内存虽然目的相同,但是在使用时一定要正确评估应用场景。

1.1.1              命名管道

基于XtratuM系统,FIFO/XM V1.0V2.0V3.0已经先后被发布。其中V1.0采用了阻塞性同步竞争条件和系统调用的方法实现,在后续的版本中,V2.0V3.0就分别针对这两种技术的缺陷,通过Lock-Free机制和内存映射机制解决相应问题。本节将详细介绍三个版本的FIFO实现过程以及Lock-Free机制[38]

1.1.1.1            FIFO/XM V1.0

FIFO/XM主要包含共享FIFOLinux FIFO设备驱动和PaRTiKle FIFO设备驱动。共享FIFOV1.0中是被集成到XtratuM模块里面的。并且FIFO的访问是非阻塞型,即当FIFO没有数据时,如果任务试图从里面读取数据时,任务会直接返回。当FIFO被写满后,继续执行写操作的任务也会直接返回。FIFO可以是双向操作,即任何域可以对同一个FIFO进行读或者写操作。图2-10给出了FIFO/XM V 1.0的架构模型。

图2-1.          FIFO/XM V1.0架构模型

从图2-10中可以清晰的看到LinuxPaRTiKle使用不同的FIFO设备驱动。Linux和普通的域通过不同的接口访问底层的共享FIFO。在FIFO/XM的开发和测试环境中,PaRTiKle是普通的任务域。

XtratuM模块被加载的时候,FIFO内存被静态分配并且被锁在物理内存中,这样可以防止FIFO内存空间的换入或换出。默认情况下,16个大小为PAGE SIZEFIFO被建立。Linux系统中FIFO设备驱动通过两个导出全局函数链接访问FIFO数据。但是,不同于Linux系统任务,PaRTiKle通过XtratuMHypercall层提供的两个HypercallsFIFO进行访问,xm_fifo_read() xm_fifo_write()。当PaRTiKle要访问底层的FIFO设备时,PaRTiKle首先要调用xm_fifo_read()函数,该函数会执行int 0x82指令,从而触发Hypercall异常,系统由PaRTiKle空间陷入XtratuM内核空间,然后调用xm_fifo_read_sys()函数对FIFO进行读操作。熟悉Linux系统调度的读者很容易明确这一部分的执行过程。PaRTiKle系统中的用户态线程和Linux用户态进程都通过POSIX APIsFIFO设备进行访问和操作,例如open()close()read()write()等。在LinuxPaRTiKle系统中,FIFO设备的名称为/dev/rtf0,,/dev/rtf15,并且设备的最大号与最小号遵循Linux系统对命名管道的规定。图2-11和图2-12分别给出了FIFO/XMLinux系统和PaRTiKle系统中读操作的函数调用树。

图2-11.       LinuxFIFO/XM设备函数调用树

图2-12.       PaRTiKleFIFO/XM设备函数调用树

从图2-11和图2-12中可以看到底层从FIFO中读数据函数都是xmf_read(),这也意味着在xmf_read()函数中会出现资源竞争访问,包含实时域和非实时域之间的竞争。因此,为了提高竞争资源的安全性,同步机制被引入到该函数中。在当前XtratuM版本中,除了屏蔽中断,系统没有提供其它的竞争条件方法。为此,新的机制应该被引入到域间资源的竞争中。下面给出了xm_read()函数的算法

int xmf_read(int index, char *dst, int size)

BEGIN

...

hw_save_flags_and_cli(&flags);

read_data();

hw_restore_flags(flags);

...

END

竞争条件并不仅仅存在于域之间对FIFO/XM的访问中,并且存在于域内任务对设备的访问。在LinuxPaRTiKle系统中,为了避免内部的进程和线程对FIFO设备的竞争,它们采用了不同的函数避免竞争条件。在Linux系统中,FIFO/XM采用读信号量和写信号量对FIFO设备的访问。而PaRTiKle中,由于提供的信号量功能相对比较少,读写FIFO设备将会采用同一种信号量。下面分别给出了LinuxPaRTiKle针对FIFO设备竞争条件的同步算法。

int linf_read(int fd, char *dest, int size)

BEGIN

...

down_read(&fifo.rw_semphore);

read_data();

up_read(&fifo_rw_semphore);

...

END

 

int linf_write(int fd, const char *src, int size)

BEGIN

...

down_write(&fifo.rw_semphore);

write_data();

up_wirte (&fifo_rw_semphore);

...

END

 

int prtkf_read(int fd, char *dest, int size)

BEGIN

...

sem_wait_sys (&fifo.rw_semphore);

read_data();

sem_post_sys(&fifo_rw_semphore);

...

END

上面的内容已经简单的介绍了FIFO/XM V1.0的实现,希望读者对FIFO设备能够理解。但是,在FIFO/XM V1.0中存在一些缺陷。

l        中断屏蔽机制可以解决多个域对FIFO资源的竞争条件。但是,其余的任务甚至是中断服务程序都无法得到执行。从而很大的影响了系统的实时行为,尤其是当具有低优先级的域首先获取资源时。尤其是在多核系统上,这种策略具有严重缺陷。

l        采用信号量机制解决竞争条件。信号量是一种基于阻塞的机制,并且会引起优先级倒置。并且,信号量的获取和释放会影响系统的性能。因此,新的同步机制需要创建和应用。

l        Linux系统和PaRTiKle系统中,对FIFO数据的访问采用的系统调用或Hypercall,提高了系统延迟。

l        缺乏FIFO控制能力。在FIFO/XM 1.0中,仅仅提供了对FIFO读和写这两种操作。没有相应的控制功能和状态检查功能。

鉴于FIFO/XM 1.0存在的多种缺陷,进一步提高FIFO设备的效率和稳定性,在FIFO/XM V2.0FIFO/XM V3.0中引入了新的方法和技术。

1.1.1.2            Lock-Free机制

当前,有多种传统的方式在同步领域得到实现和应用,例如spin-lock,信号量,BKL等。但是这些机制都是等待阻塞型机制,并且会引起优先级倒置问题。因此,Lock-Free机制应该被引入到FIFO设备访问过程中。Lock-Free机制可以避免由传统基于阻塞的条件竞争解决方案带来的一些问题[39]

l        优先级倒置:当高优先级任务请求被低优先级任务持有的锁时发生[40]

l        死锁问题:不同的任务依照不同的序列试图获取同一个资源集中资源锁时发生;

l        抢占容忍:如果持锁去睡眠,但是另外一个进程要去获取该锁。从而导致新任务去等待睡眠者,也就意味着新任务在无用的运行。

Lock-Free机制的目标是在不阻塞其它任务的前提下可以安全的访问竞争资源,并且要保证资源操作的一致性。为了实现Lock-Free机制,通常采用的基本方法是CAS(Compare-and-Swap)操作。CAS是严重依赖底层计算机平台的一个原子操作。x86平台采用cmpxchg指令实现CAS操作,该指令出现于X486等后继的X86平台中。下面列出了两端代码,一段是用C语言实现的CAS操作算法,另外一段是基于X86平台实现的CAS操作[41]

int cas(int *addr, int old_value, int new_value)

{

if(*addr == old_value) {

*addr = new_value;

return 1;

}

return 0;

}

#define CAS(adr, ov, nv) ({ /

__typeof__(ov) ret; /

__asm__ __volatile__( /

"cmpxchg %3, %1" /

:"=a"(ret),

"+m"(*(volatile unsigned int *)(adr)) /

:"a"(ov),"r"(nv)); /

ret == ov; /

})

PowerPC平台上,由于缺乏cmpxchg指令,为了实现CAS功能,系统采用了Fetch-Store指令,下面是基于PowerPC 405 的伪cmpxchg() 指令代码段。

static __inline__ unsigned long

cmpxchg(volatile int *p, int old, int new)

{

    int prev;

    __asm__ __volatile__ ("/n/

1:  lwarx  %0,0,%2 /n/

    cmpw   0,%0,%3 /n/

    bne 2f /n"

    PPC405_ERR77(0,%2)

"   stwcx. %4,0,%2 /n/

    bne-   1b/n"

"2:"

    : "=&r" (prev), "=m" (*p)

    : "r" (p), "r" (old), "r" (new), "m" (*p)

    : "cc", "memory");

    return prev;

}

上面简单的介绍了Lock-Free机制和CAS操作在x86PowerPC平台的实现,Lock-Free机制在FIFO/XM中的使用和实现将会在下面的部分介绍。

 

抱歉!评论已关闭.