RTLinux下的一種實時應用通訊機制

rtlinux做爲linux最爲通用的幾種硬實時擴展之一,表現了良好的硬實時性。同時,爲了更有效地爲各類實時應用服務,提供了多種與linux中非實時進行通訊的接口,主要有共享內存、rt_fifo和線程信號驅動機制,三者的應用重點各不相同。其中前兩種較爲經常使用[1]。因爲不的實現機理,這兩種接口的應用範疇各有側重。通過實踐,筆者認爲將以上兩種接口有機地結合,利用共享內存傳送大容量、對讀/寫時序要求不高的數據信息;同時,利用rt_fifo輔助實現對該共享內存的同步控制,可以綜合二者的優點,是rtlinux下一種十分有效的實時應用通訊模式。 linux

1 rtlinux的結構和應用程序開發模式 api

做爲linux的硬實時擴展,rtlinux一個重要的計準則在於:儘量多地利用linux內核所能提供的功能[2]。 多線程

顯示、記錄、設備初始化、阻塞式動態資源分配和模塊化內核管理等無實時要求或者與硬實時性要求相悖的服務均由linux提供。rtlinux內核則主要爲實時任務提供對硬件的直接訪問,使得它們具備最小的延遲和最優先的處理器利用權。 app

基於以上準則,rtlinux中的實時應用程序開發一般具備一個通用的模式,如圖1所示。按照運行環境和對實時要求的嚴格程度分爲實時和非實時兩個模塊。非實時模塊的功能包括結果數據顯示。用戶交互、數據存儲等;實時模塊主要負責響應數據採集外設的中斷,結果數據的採集。二者經過rt_fifo或者共享內存進行通訊,組成一個完整的實時數據採集程序。 異步

2 rtlinux中的兩種通訊接口 模塊化

rtlinux提供了rt_fifo和共享內存兩種標準通訊接口,用於實時任務和非實時任務之間的交互。 函數

2.1 rt_fifo 測試

rt_fifo(first-in-first-out,先進先出)是一種提案隊列機制組織的字符設備。在linux文件系統中,主設備號爲150。一個系統平臺中可以同加載fifo的模塊數rtf_no定義在rt_fifo_new.c中,通常爲64,在文件系統中分別對慶設備文件/dev/rtf0..63。在系統資源容許的狀況下,一個用戶進程所能同時使用的fifo數和每一個fifo的容量是沒限制的。 spa

rt_fifo具備以下特徵: 操作系統

*隊列中的數據傳送採用數據流形式,必須自行定義數據邊界監測機制,尤爲對於不定長度數據的傳輸。

*具有完善的同步阻塞機制,利用同一fifo進行通訊的兩進程間無需自行增長同步控制。

*一種點對點的通訊通道,不支持單生產者、多消費者的使用模式。

做爲一個完善的隊列模塊,rt_fifo的使用簡便易行,具體實現主要包括建立、讀/寫操做、釋放三個步驟。在linux文件系統中,rt_fifo是一個字符設備文件,因此在非實時線程中訪問rt_fifo時,使用標準的字符設備讀/寫函數便可(read、write、open、close,etc)。以上函數的調用方式均爲阻塞式調用:當fifo中有數據可讀時,當即返回;不然,會陷入無限等待之中。

從rt進程中訪問rt_fifo,所涉及到的rtlapi以下:

#include<rtl_fifo.h>

[建立]

int rtf_create(unsigned int fifo,int size);

內核空間中,爲編號fifo的rt_fifo設備分配size字節的緩衝區。fifo對應於所使用rt_fifo的次設備號。

[釋放]

int rtf_destroy(unsigned int fifo);

釋放內核空間中次設備號爲fifo的rt_fifo設備緩衝區。

注意:以上兩個函數涉及到內核空間的緩衝區分配,必須分別在linux的init_module()和cleanup_module()中調用,或者在用戶空間經過psc(the user-level real-time signal library,用戶級實時庫函數)進行調用。

[讀/寫操做]

int rtl_get(unsigned int fifo,char *buf,int count);

從fifo中讀出長度爲count字節的數據,存放buf之中。

int rtf_put(unsigned int fifo,char * buf,int count);

將長度爲count字節的數據寫入fifo中。

int rtf_create_handle(unsigned int fifo,int(&handler)(unsigned int fifo));

建立一個回調函數句柄,當fifo被linux進程讀/寫時,被調用。一般與rtl_get結合使用,用於異步的從linux進程中接收數據,從而避免採用輪詢的方式。

2.2 共享內存

共享內存是指被閒置出來專用於內核空間和用戶空間進行通訊的內存區域。相對於fifo具備以下特色:

*應用程序必須本身定義相應的協議,對於寫入共享數據區域的有數據進行保護,如同步控制等。

*數據能夠既定格式讀/寫,各個數據域的更新十分便易。

*不是點對點的通訊通道,能夠支持多生產者、多消費者的使用模式,可以同時被多個線程訪問。

    在rtlinux下,共享內存的使用可採用如下兩種方式:

(1)利用rtlinux中附帶的mbuff模塊

在使用mbuff以前,要求系統中已經加載了mbuff.o模塊。該模塊中的兩個函數被分別用於分配和釋放所需的內存空間。

#include<mbuff.h>

[分配]

void * mbuff_alloc(const char * name,int size);

從內核空間中分配一塊與name相連,大小爲size字節的內存空間,返回地址指針,設備這塊空間的引用標識爲1。如與name相連的內存空間已經存在,就僅僅返回指向該空間的地址指針,同時將其引用標識加1。

[釋放]

void mbuff_free(const char * name,int size);

將mbuff的引用標識減1。當引用標識被減爲0時,釋放mbuff。

注意:①mbuff_alloc使用了vmalloc函數,因爲分配內核空間的須要,會交換出一系列的內核空間頁面,因此在實時線程、中斷處理線程、定時器中斷線程中調用這個函數是十分危險的。

②在進程結束前,必定要調用mbuff_free函數。mbuff所佔內存空間不會由於其引用進程的結束而自行釋放。

(2)高地址空間物理內存的直接隔離

在系統啓動時,隔離出必定大小的高地址空間物理內存,使其脫離系統運行環境,做爲專用的共享內存區域。

圖4 共享內存互斥操做流程圖

    在linux啓動配置文件中,插入一行以append關鍵字起始的命令行,便可實現高端內存空間的隔離。修改後的/etc/lilo.conf文件以下所示:

image=/boot/zimage

label=rtlinuxx.x

root=/dev/hda2

read_only

append=「mem=xm」

其中,mem的值對應於被隔離空間的起

始地址,能夠由物理內存總容量減去所需共享空間容量獲得。可是必須注意,被隔離出的共享空間的容量必須小於/usr/include/asm/param.h文件中定義的頁面長度。intel pentium系列芯片的頁面長度爲4mb。

對共享內存空間的存取操做經過訪問其基址來實現。必須首先定義共享內存空間的基址。

#define base_address(127×0x100000)

在實時和非實時模塊中有不一樣的基址訪問方法。寫時模塊運行於內核地址空間,能夠直接將基址做爲地址指針進行存取,使用語句以下:

unsigned short * sharemem;

sharemem=(unsigned short *)__va(base_address);

非實時模塊運行於用戶地址空間,必須先將該物理地址映射入該進程虛擬地址空間後,才能對其進行存取。使用命令以下:

#include<unistd.h>

#include<fcntl.h>

#include<sys/mman.h>

int fd;

unsigned short * sharemem;

fd=open("/dev/mem",o_rdwr); ①

sharemem=(unsigned short *)mmap(0,buflen,

prot_read|prot_write,

map_file|map_shared,

fd,base_address); ②

注①:訪問物理內存必須打開與其對應的設備文件/dev/mem。

注②:mmap命令的做用是將設備文件fd中,從當前進程的虛擬地址空間,其返回值可被非實時進程存取。

以上兩種方式在實現機理上的不一樣之處在於,mbuff利用vmalloc從內核地址空間分配的共享內存空間僅僅在邏輯上連續,空間的大小不受實際物理內存空間的限制;而直接隔離物理內存所獲取的緩衝區物理上連續,可是大小受到物理內存空間和當前系統情況的限制。共同之處在於,所得到的內存均被隔離於系統內核的運行環境以外,不會在頁面交換中被換出,因此以上兩種方法均適用於實時應用之中。

3 兩種通訊接口的結合

以上兩種通訊接口具備不一樣的適用範疇,爲了實現一個完整的實時應用,一般須要將二者結合,以一個實時數據採集程序爲例,實時模塊和非實時模塊之間一般須要傳送兩種類型的數據;結果數據和控制信息。

結果數據:由實時模塊週期性產生。非實時模塊用於顯示和存儲,對讀/寫的時序性要求不高,可是一般須要由多個用戶共享,所以,利用共享內存模塊傳輸比較適合。

控制信息:主要用於實現非實時模塊和實時模塊之間的交互控制,數據量小,可是比較注重信號讀/寫的時序性和通訊過程當中實時性,採用rt_fifo實現比較適合。

圖2爲通用的抽象數據流圖。

3.1 共享內存的內步控制和rt_fifo的使用

因爲對共享內存的存取經過直接訪問指針來實現,操做系統不會爲其提供任何同步控制,應用程序必須自行提供握手機制,來保證讀/寫進程之間同步。

實現同步的一種方式是接收方和發送方利用消息通訊來實現握手。接收方對共享內存以輪詢的方式監測新數據的到來,而後發送接收信息。爲了實現握手,發送方對於每條接收消息都必須回覆一個確認消息,新的接收消息只有在收到確認消息之後才能發出。

這種方式在實時模塊和非實時模塊中均需要採用輪詢的方式監測新數據和消息的到來,所以會佔用較多的處理器資源。因此,能夠考慮利用rt_fifo實現實時模塊和非實時模塊之間對共享內存的存取同步。利用rt_fifo所提供的句柄功能可以避免實時模塊對接收消息的輪詢監測,在必定程度上提升程序運行效率。

具體實現,能夠經過利用rt_fifo實時傳輸當前所寫入或被讀出的共享內存塊序號,實現實時進程和非實時進程之間的步。由於rt_fifo是一種單向傳輸隊列,爲了實現交互,須要兩個傳輸方向相反的rt_fifo,鏈接於兩個模塊之間,如圖3所示。

圖3中,bufno爲筆者自行定義的隊列。它的使用主要是爲了不因爲rt_fifo引發的實時部分和非實時部分之間的死鎖。

實時部分和非實時部分的各線程路之間對共享內存的訪問爲異步進行;同時,rtlinux中對rt_fifo的進行讀/寫的api函數,爲阻塞式操做。當fifo0中目前沒有可讀數據時,對rtf_get函數的調用會使程序陷入無限等待之中,很容易形成實時模塊和非實時模塊之間的死鎖。

爲了不這種狀況,能夠將bufno做爲緩衝區與fifo0的句柄結合使用,臨時存放fifo0中被非實時線程寫入的塊序號。實時模塊再也不對fifo0進行讀/寫,而是改由bufno隊列中獲取當前有效的共享內存序號。若是當前無可用數據,則進入週期等待狀態。

3.2 共享內存訪問的互斥

對共享內存訪問的互斥操做,包括兩個方面:實時模塊與非實時模塊之間的互斥、非實時模塊中各採集線程之間的互斥。

(1)實時模塊與非實時模塊之間的互斥

多線程之間對共享資源訪問的互斥,是操做系統中一個重要的研究分支。可是在實時模塊和非實時模塊之間,問題變得相對簡單。由於,在實時進程和非實時進程之中,實時進程和非實時進程運行的環境區別很大。工做於rtlinux環境下的實時進程具備最高的優先級,不可能被非實時進程中斷。因此,在實現互斥時,只須保護非實時進程對共享資源的訪問便可。

抽象流程如圖4所示。利用共享內存區域的第一個字節做爲訪問標識,實現非實時模塊對實時模塊的互斥。

非實時進程開始訪問共享區域時,將此標識置位;訪問結束時,復位。實時進程在訪問共享區域前先檢測該標識,若是標識容許訪問,則執行寫入操做;反之,掛起等待標識位復位,按既定週期t輪詢。

實時進程的既定週期t的設置十分重要,週期過長,會增長髮生衝突後的等時間,致使共享內存狀態改變時,沒法被及時寫入;週期太短,增長了系統的輪詢次數,加劇實時系統的負擔。筆者在已實現的數據採集程序中,對t的不一樣設置,所得到的平均數據採集率進行了統計,結果如圖5所示。

注:以上實驗的測試平臺爲pentiumiii 667,5400轉普通硬盤,rtlinux3.一、linux kernel 2.4.4,數據流向爲數據採集外設至共享內存而後存放硬盤,數據的產生頻率爲10ms。

(2)非實時模塊之間的互斥

非實時模塊中異步執行的各採集線程之間,能夠利用互斥變量的加鎖和解鎖實現對共享內存訪問的互斥。因爲互斥區的執行體內,每次只容許一個線程進入,爲了保證程序的執行效率,在互斥區中不宜使用耗時較長或阻塞式調用的函數。

4 結論

在rtlinux提供的實時模塊和非實時模塊之間的通訊接口中,rt_fifo和共享內存較爲經常使用,分別適用於不一樣的數據類型通訊。本文提出的這種方法,能充分利用二者的優勢,方便地實現實時與非實時之間海量數據通訊。目前已在rtlinux3.一、linux kernel 2.4.4系統平臺上成功實現,並取得了使人滿意的效果。

相關文章
相關標籤/搜索