<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > RTLinux下的一種實時應(yīng)用通信機制

          RTLinux下的一種實時應(yīng)用通信機制

          作者: 時間:2004-12-10 來源:網(wǎng)絡(luò) 收藏
          摘要:程序的開發(fā)模式;詳細(xì)說明兩種在模塊與非模塊之間進行的主要接口的實現(xiàn)和使用方式;提出一種將以上兩種接口有機結(jié)合的實時內(nèi)部,并通過實驗證該方法的可操作性。

          關(guān)鍵詞: 通信接口 實時 共享內(nèi)存 RT_FIFO

          實時性是多任務(wù)嵌入式系統(tǒng)的基本特征之一,主要表現(xiàn)為對重要性各不相同的任務(wù)進行統(tǒng)籌兼顧的合理調(diào)度能力。根據(jù)系統(tǒng)對時限要求的嚴(yán)格程度又分為軟實時和硬實時。

          作為Linux最為通用的幾種硬實時擴展之一,表現(xiàn)了良好的硬實時性。同時,為了更有效地為各種實時應(yīng)用服務(wù),提供了多種與Linux中非實時進行通信的接口,主要有共享內(nèi)存、RT_FIFO和線程信號驅(qū)動,三者的應(yīng)用重點各不相同。其中前兩種較為常用[1]。由于不的實現(xiàn)機理,這兩種接口的應(yīng)用范疇各有側(cè)重。經(jīng)過實踐,筆者認(rèn)為將以上兩種接口有機地結(jié)合,利用共享內(nèi)存?zhèn)魉痛笕萘?、對讀/寫時序要求不高的數(shù)據(jù)信息;同時,利用RT_FIFO輔助實現(xiàn)對該共享內(nèi)存的同步控制,能夠綜合兩者的優(yōu)勢,是RTLinux下一種十分有效的實時應(yīng)用通信模式。

          1 RTLinux的結(jié)構(gòu)和應(yīng)用程序開發(fā)模式

          作為Linux的硬實時擴展,RTLinux一個重要的計準(zhǔn)則在于:盡可能多地利用Linux內(nèi)核所能提供的功能[2]。

          顯示、記錄、設(shè)備初始化、阻塞式動態(tài)資源分配和模塊化內(nèi)核管理等無實時要求或者與硬實時性要求相悖的服務(wù)均由Linux提供。RTLinux內(nèi)核則主要為實時任務(wù)提供對硬件的直接訪問,使得它們具有最小的延遲和最優(yōu)先的處理器利用權(quán)。

          基于以上準(zhǔn)則,RTLinux中的實時應(yīng)用程序開發(fā)通常具有一個通用的模式,如圖1所示。按照運行環(huán)境和對實時要求的嚴(yán)格程度分為實時和非實時兩個模塊。非實時模塊的功能包括結(jié)果數(shù)據(jù)顯示。用戶交互、數(shù)據(jù)存儲等;實時模塊主要負(fù)責(zé)響應(yīng)數(shù)據(jù)采集外設(shè)的中斷,結(jié)果數(shù)據(jù)的采集。兩者通過RT_FIFO或者共享內(nèi)存進行通信,組成一個完整的實時數(shù)據(jù)采集程序。

          2 RTLinux中的兩種通信接口

          RTLinux提供了RT_FIFO和共享內(nèi)存兩種標(biāo)準(zhǔn)通信接口,用于實時任務(wù)和非實時任務(wù)之間的交互。

          2.1 RT_FIFO

          RT_FIFO(First-In-First-Out,先進先出)是一種提案隊列組織的字符設(shè)備。在Linux文件系統(tǒng)中,主設(shè)備號為150。一個系統(tǒng)平臺中能夠同加載FIFO的模塊數(shù)RTF_NO定義在rt_fifo_new.c中,一般為64,在文件系統(tǒng)中分別對慶設(shè)備文件/dev/rtf0..63。在系統(tǒng)資源允許的情況下,一個用戶進程所能同時使用的FIFO數(shù)和每個FIFO的容量是沒限制的。

          RT_FIFO具有如下特征:

          *隊列中的數(shù)據(jù)傳送采用數(shù)據(jù)流形式,必須自行定義數(shù)據(jù)邊界監(jiān)測機制,尤其對于不定長度數(shù)據(jù)的傳輸。

          *具備完善的同步阻塞機制,利用同一FIFO進行通信的兩進程間無需自行增加同步控制。

          *一種點對點的通信通道,不支持單生產(chǎn)者、多消費者的使用模式。

          作為一個完善的隊列模塊,RT_FIFO的使用簡便易行,具體實現(xiàn)主要包括創(chuàng)建、讀/寫操作、釋放三個步驟。在Linux文件系統(tǒng)中,RT_FIFO是一個字符設(shè)備文件,所以在非實時線程中訪問RT_FIFO時,使用標(biāo)準(zhǔn)的字符設(shè)備讀/寫函數(shù)即可(read、write、open、close,etc)。以上函數(shù)的調(diào)用方式均為阻塞式調(diào)用:當(dāng)FIFO中有數(shù)據(jù)可讀時,立即返回;否則,會陷入無限等待之中。

          從RT進程中訪問RT_FIFO,所涉及到的RTLAPI如下:

          #includertl_fifo.h>

          [創(chuàng)建]

          int rtf_create(unsigned int fifo,int size);

          內(nèi)核空間中,為編號fifo的RT_FIFO設(shè)備分配size字節(jié)的緩沖區(qū)。fifo對應(yīng)于所使用RT_FIFO的次設(shè)備號。

          [釋放]

          int rtf_destroy(unsigned int fifo);

          釋放內(nèi)核空間中次設(shè)備號為fifo的RT_FIFO設(shè)備緩沖區(qū)。

          注意:以上兩個函數(shù)涉及到內(nèi)核空間的緩沖區(qū)分配,必須分別在Linux的init_module()和cleanup_module()中調(diào)用,或者在用戶空間通過PSC(the user-level real-time signal library,用戶級實時庫函數(shù))進行調(diào)用。

          [讀/寫操作]

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

          從FIFO中讀出長度為count字節(jié)的數(shù)據(jù),存放buf之中。

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

          將長度為count字節(jié)的數(shù)據(jù)寫入FIFO中。

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

          創(chuàng)建一個回調(diào)函數(shù)句柄,當(dāng)FIFO被Linux進程讀/寫時,被調(diào)用。通常與rtl_get結(jié)合使用,用于異步的從Linux進程中接收數(shù)據(jù),從而避免采用輪詢的方式。

          2.2 共享內(nèi)存

          共享內(nèi)存是指被閑置出來專用于內(nèi)核空間和用戶空間進行通信的內(nèi)存區(qū)域。相對于FIFO具有如下特點:

          *應(yīng)用程序必須自己定義相應(yīng)的協(xié)議,對于寫入共享數(shù)據(jù)區(qū)域的有數(shù)據(jù)進行保護,如同步控制等。

          *數(shù)據(jù)可以既定格式讀/寫,各個數(shù)據(jù)域的更新十分便易。

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

          在RTLinux下,共享內(nèi)存的使用可采用以下兩種方式:

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

          在使用mbuff之前,要求系統(tǒng)中已經(jīng)加載了mbuff.o模塊。該模塊中的兩個函數(shù)被分別用于分配和釋放所需的內(nèi)存空間。

          #includembuff.h>

          [分配]

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

          從內(nèi)核空間中分配一塊與name相連,大小為size字節(jié)的內(nèi)存空間,返回地址指針,設(shè)備這塊空間的引用標(biāo)識為1。如與name相連的內(nèi)存空間已經(jīng)存在,就僅僅返回指向該空間的地址指針,同時將其引用標(biāo)識加1。

          [釋放]

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

          將mbuff的引用標(biāo)識減1。當(dāng)引用標(biāo)識被減為0時,釋放mbuff。

          注意:①mbuff_alloc使用了vmalloc函數(shù),由于分配內(nèi)核空間的需要,會交換出一系列的內(nèi)核空間頁面,所以在實時線程、中斷處理線程、定時器中斷線程中調(diào)用這個函數(shù)是十分危險的。

          ②在進程結(jié)束前,一定要調(diào)用mbuff_free函數(shù)。Mbuff所占內(nèi)存空間不會因為其引用進程的結(jié)束而自行釋放。

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

          在系統(tǒng)啟動時,隔離出一定大小的高地址空間物理內(nèi)存,使其脫離系統(tǒng)運行環(huán)境,作為專用的共享內(nèi)存區(qū)域。

          圖4 共享內(nèi)存互斥操作流程圖

          在Linux啟動配置文件中,插入一行以append關(guān)鍵字起始的命令行,即可實現(xiàn)高端內(nèi)存空間的隔離。修改后的/etc/lilo.conf文件如下所示:

          image=/boot/zImage

          label=rtlinuxX.X

          root=/dev/hda2

          read_only

          append=“mem=Xm”

          其中,mem的值對應(yīng)于被隔離空間的起始地址,可以由物理內(nèi)存總?cè)萘繙p去所需共享空間容量得到。但是必須注意,被隔離出的共享空間的容量必須小于/usr/include/asm/param.h文件中定義的頁面長度。Intel Pentium系列芯片的頁面長度為4MB。

          對共享內(nèi)存空間的存取操作通過訪問其基址來實現(xiàn)。必須首先定義共享內(nèi)存空間的基址。

          #define BASE_ADDRESS(1270x100000)

          在實時和非實時模塊中有不同的基址訪問方法。寫時模塊運行于內(nèi)核地址空間,可以直接將基址作為地址指針進行存取,使用語句如下:

          unsigned short * sharemem;

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

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

          #includeunistd.h>

          #includefcntl.h>

          #includesys/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); ②

          注①:訪問物理內(nèi)存必須打開與其對應(yīng)的設(shè)備文件/dev/mem。

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

          以上兩種方式在實現(xiàn)機理上的不同之處在于,mbuff利用vmalloc從內(nèi)核地址空間分配的共享內(nèi)存空間僅僅在邏輯上連續(xù),空間的大小不受實際物理內(nèi)存空間的限制;而直接隔離物理內(nèi)存所獲取的緩沖區(qū)物理上連續(xù),但是大小受到物理內(nèi)存空間和當(dāng)前系統(tǒng)狀況的限制。共同之處在于,所獲得的內(nèi)存均被隔離于系統(tǒng)內(nèi)核的運行環(huán)境之外,不會在頁面交換中被換出,所以以上兩種方法均適用于實時應(yīng)用之中。

          3 兩種通信接口的結(jié)合

          以上兩種通信接口具有不同的適用范疇,為了實現(xiàn)一個完整的實時應(yīng)用,通常需要將兩者結(jié)合,以一個實時數(shù)據(jù)采集程序為例,實時模塊和非實時模塊之間通常需要傳送兩種類型的數(shù)據(jù);結(jié)果數(shù)據(jù)和控制信息。

          結(jié)果數(shù)據(jù):由實時模塊周期性產(chǎn)生。非實時模塊用于顯示和存儲,對讀/寫的時序性要求不高,但是通常需要由多個用戶共享,因此,利用共享內(nèi)存模塊傳輸比較適合。

          控制信息:主要用于實現(xiàn)非實時模塊和實時模塊之間的交互控制,數(shù)據(jù)量小,但是比較注重信號讀/寫的時序性和通信過程中實時性,采用RT_FIFO實現(xiàn)比較適合。

          圖2為通用的抽象數(shù)據(jù)流圖。

          3.1 共享內(nèi)存的內(nèi)步控制和RT_FIFO的使用

          由于對共享內(nèi)存的存取通過直接訪問指針來實現(xiàn),操作系統(tǒng)不會為其提供任何同步控制,應(yīng)用程序必須自行提供握手機制,來保證讀/寫進程之間同步。

          實現(xiàn)同步的一種方式是接收方和發(fā)送方利用消息通信來實現(xiàn)握手。接收方對共享內(nèi)存以輪詢的方式監(jiān)測新數(shù)據(jù)的到來,然后發(fā)送接收信息。為了實現(xiàn)握手,發(fā)送方對于每條接收消息都必須回復(fù)一個確認(rèn)消息,新的接收消息只有在收到確認(rèn)消息以后才能發(fā)出。

          這種方式在實時模塊和非實時模塊中均須要采用輪詢的方式監(jiān)測新數(shù)據(jù)和消息的到來,因此會占用較多的處理器資源。所以,可以考慮利用RT_FIFO實現(xiàn)實時模塊和非實時模塊之間對共享內(nèi)存的存取同步。利用RT_FIFO所提供的句柄功能能夠避免實時模塊對接收消息的輪詢監(jiān)測,在一定程度上提高程序運行效率。

          具體實現(xiàn),可以通過利用RT_FIFO實時傳輸當(dāng)前所寫入或被讀出的共享內(nèi)存塊序號,實現(xiàn)實時進程和非實時進程之間的步。因為RT_FIFO是一種單向傳輸隊列,為了實現(xiàn)交互,需要兩個傳輸方向相反的RT_FIFO,連接于兩個模塊之間,如圖3所示。

          圖3中,BufNo為筆者自行定義的隊列。它的使用主要是為了避免由于RT_FIFO引起的實時部分和非實時部分之間的死鎖。

          實時部分和非實時部分的各線程路之間對共享內(nèi)存的訪問為異步進行;同時,RTLinux中對RT_FIFO的進行讀/寫的API函數(shù),為阻塞式操作。當(dāng)FIFO0中目前沒有可讀數(shù)據(jù)時,對rtf_get函數(shù)的調(diào)用會使程序陷入無限等待之中,很容易造成實時模塊和非實時模塊之間的死鎖。

          為了避免這種情況,可以將BufNo作為緩沖區(qū)與FIFO0的句柄結(jié)合使用,臨時存放FIFO0中被非實時線程寫入的塊序號。實時模塊不再對FIFO0進行讀/寫,而是改由BufNo隊列中獲取當(dāng)前有效的共享內(nèi)存序號。如果當(dāng)前無可用數(shù)據(jù),則進入周期等待狀態(tài)。

          3.2 共享內(nèi)存訪問的互斥

          對共享內(nèi)存訪問的互斥操作,包括兩個方面:實時模塊與非實時模塊之間的互斥、非實時模塊中各采集線程之間的互斥。

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

          多線程之間對共享資源訪問的互斥,是操作系統(tǒng)中一個重要的研究分支。但是在實時模塊和非實時模塊之間,問題變得相對簡單。因為,在實時進程和非實時進程之中,實時進程和非實時進程運行的環(huán)境區(qū)別很大。工作于RTLinux環(huán)境下的實時進程具有最高的優(yōu)先級,不可能被非實時進程中斷。所以,在實現(xiàn)互斥時,只須保護非實時進程對共享資源的訪問即可。

          抽象流程如圖4所示。利用共享內(nèi)存區(qū)域的第一個字節(jié)作為訪問標(biāo)識,實現(xiàn)非實時模塊對實時模塊的互斥。

          非實時進程開始訪問共享區(qū)域時,將此標(biāo)識置位;訪問結(jié)束時,復(fù)位。實時進程在訪問共享區(qū)域前先檢測該標(biāo)識,如果標(biāo)識允許訪問,則執(zhí)行寫入操作;反之,掛起等待標(biāo)識位復(fù)位,按既定周期T輪詢。

          實時進程的既定周期T的設(shè)置十分重要,周期過長,會增加發(fā)生沖突后的等時間,導(dǎo)致共享內(nèi)存狀態(tài)改變時,無法被及時寫入;周期過短,增加了系統(tǒng)的輪詢次數(shù),加重實時系統(tǒng)的負(fù)擔(dān)。筆者在已實現(xiàn)的數(shù)據(jù)采集程序中,對T的不同設(shè)置,所獲得的平均數(shù)據(jù)采集率進行了統(tǒng)計,結(jié)果如圖5所示。

          注:以上實驗的測試平臺為PentiumIII 667,5400轉(zhuǎn)普通硬盤,RTLinux3.1、Linux kernel 2.4.4,數(shù)據(jù)流向為數(shù)據(jù)采集外設(shè)至共享內(nèi)存然后存放硬盤,數(shù)據(jù)的產(chǎn)生頻率為10ms。

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

          非實時模塊中異步執(zhí)行的各采集線程之間,可以利用互斥變量的加鎖和解鎖實現(xiàn)對共享內(nèi)存訪問的互斥。由于互斥區(qū)的執(zhí)行體內(nèi),每次只允許一個線程進入,為了保證程序的執(zhí)行效率,在互斥區(qū)中不宜使用耗時較長或阻塞式調(diào)用的函數(shù)。

          4 結(jié)論

          在RTLinux提供的實時模塊和非實時模塊之間的通信接口中,RT_FIFO和共享內(nèi)存較為常用,分別適用于不同的數(shù)據(jù)類型通信。本文提出的這種方法,能充分利用兩者的優(yōu)點,方便地實現(xiàn)實時與非實時之間海量數(shù)據(jù)通信。目前已在rtLinux3.1、Linux kernel 2.4.4系統(tǒng)平臺上成功實現(xiàn),并取得了令人滿意的效果。



          評論


          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();