1.1 域間通信工具
爲了提高域之間的作業協作能力,域間通信(Inter-Domains Communication)工具被引入到XtratuM Hypervisor系統中。當前,XtratuM系統中存在兩種數據通信工具,一種是針對數據流的命名管道(FIFO),另外一種是針對塊數據的共享內存。命名管道是一種簡單的流數據傳輸工具,採用先進先出的策略,不同於PIPE(管道),命名管道有固定的存儲介質和名稱,它可以被任何域通過名稱打開和關閉。而管道只有存儲介質,沒有名稱。管道的存在依附於創建管道的任務,當任務退出時,管道也就相應的被銷燬了。而命名管道一經創建將會永久存在,知道用戶明顯的將其刪除[37]。
共享內存是不同於命名管道的數據通信模式,用戶可以像打開普通文件一樣打開共享內存,並且可以指定共享內存大小。與命名管道相比,共享內存是塊設備,即任務可以隨機的訪問共享內存區間的任何地址的數據,並且不會因爲地址的不同而使訪問時間有所差別。但是FIFO類似的流數據通信工具,數據的傳輸和訪問必須有一個先後順序。因此,命名管道與共享內存雖然目的相同,但是在使用時一定要正確評估應用場景。
1.1.1 命名管道
基於XtratuM系統,FIFO/XM V1.0,V2.0,V3.0已經先後被髮布。其中V1.0採用了阻塞性同步競爭條件和系統調用的方法實現,在後續的版本中,V2.0和V3.0就分別針對這兩種技術的缺陷,通過Lock-Free機制和內存映射機制解決相應問題。本節將詳細介紹三個版本的FIFO實現過程以及Lock-Free機制[38]。
1.1.1.1 FIFO/XM V1.0
FIFO/XM主要包含共享FIFO,Linux FIFO設備驅動和PaRTiKle FIFO設備驅動。共享FIFO在V1.0中是被集成到XtratuM模塊裏面的。並且FIFO的訪問是非阻塞型,即當FIFO沒有數據時,如果任務試圖從裏面讀取數據時,任務會直接返回。當FIFO被寫滿後,繼續執行寫操作的任務也會直接返回。FIFO可以是雙向操作,即任何域可以對同一個FIFO進行讀或者寫操作。圖2-10給出了FIFO/XM V 1.0的架構模型。
圖2-1. FIFO/XM V1.0架構模型
從圖2-10中可以清晰的看到Linux和PaRTiKle使用不同的FIFO設備驅動。Linux和普通的域通過不同的接口訪問底層的共享FIFO。在FIFO/XM的開發和測試環境中,PaRTiKle是普通的任務域。
當XtratuM模塊被加載的時候,FIFO內存被靜態分配並且被鎖在物理內存中,這樣可以防止FIFO內存空間的換入或換出。默認情況下,16個大小爲PAGE SIZE的FIFO被建立。Linux系統中FIFO設備驅動通過兩個導出全局函數鏈接訪問FIFO數據。但是,不同於Linux系統任務,PaRTiKle通過XtratuM在Hypercall層提供的兩個Hypercalls對FIFO進行訪問,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 APIs對FIFO設備進行訪問和操作,例如open(),close(),read(),write()等。在Linux和PaRTiKle系統中,FIFO設備的名稱爲/dev/rtf0,…,/dev/rtf15,並且設備的最大號與最小號遵循Linux系統對命名管道的規定。圖2-11和圖2-12分別給出了FIFO/XM在Linux系統和PaRTiKle系統中讀操作的函數調用樹。
圖2-11. Linux讀FIFO/XM設備函數調用樹
圖2-12. PaRTiKle讀FIFO/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的訪問中,並且存在於域內任務對設備的訪問。在Linux和PaRTiKle系統中,爲了避免內部的進程和線程對FIFO設備的競爭,它們採用了不同的函數避免競爭條件。在Linux系統中,FIFO/XM採用讀信號量和寫信號量對FIFO設備的訪問。而PaRTiKle中,由於提供的信號量功能相對比較少,讀寫FIFO設備將會採用同一種信號量。下面分別給出了Linux和PaRTiKle針對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.0和FIFO/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操作在x86和PowerPC平臺的實現,Lock-Free機制在FIFO/XM中的使用和實現將會在下面的部分介紹。