丁 遠(yuǎn)
(1.中煤科工集團(tuán)沈陽研究院有限公司,遼寧 撫順113122;2.煤礦安全技術(shù)國家重點實驗室,遼寧 撫順113122)
數(shù)據(jù)緩存隊列是為了協(xié)調(diào)不同吞吐速度設(shè)備之間數(shù)據(jù)通信而采用的技術(shù)[1]。在基于無主、多主通信方式煤礦監(jiān)控系統(tǒng)[2]中,當(dāng)同一分站下的多臺傳感器同一時間內(nèi)向分站發(fā)送數(shù)據(jù),會導(dǎo)致監(jiān)控分站軟件結(jié)構(gòu)中數(shù)據(jù)處理模塊短時間內(nèi)處理多幀數(shù)據(jù),由于數(shù)據(jù)處理模塊需要完成解包、解析、封包上傳的工作,導(dǎo)致因為數(shù)據(jù)接收與數(shù)據(jù)處理速度不匹配造成的數(shù)據(jù)包丟失問題。常用的環(huán)形隊列策略使用之前需要預(yù)設(shè)固定尺寸的隊列空間[3],另外在環(huán)形隊列寫空間滿時,接收的新數(shù)據(jù)因為隊列不能實時擴(kuò)展,所以不能及時進(jìn)入緩存需丟棄,導(dǎo)致數(shù)據(jù)丟失,針對此問題參考了生產(chǎn)者/消費者模型,設(shè)計了雙緩存隊列結(jié)構(gòu),解決不可動態(tài)擴(kuò)展、數(shù)據(jù)溢出丟失問題。此外,為了提升監(jiān)控分站運(yùn)行速度,監(jiān)控分站軟件中使用多線程技術(shù),緩存隊列被多個線程讀寫,各個線程之間存在競爭資源問題,如果考慮不全,會出現(xiàn)饑餓現(xiàn)象,嚴(yán)重的會導(dǎo)致死鎖發(fā)生,為了避免此問題出現(xiàn),將多線程安全技術(shù)融入到數(shù)據(jù)處理隊列中,解決資源競爭。
數(shù)據(jù)結(jié)構(gòu)理論定義了一種“一端入一端出”的先進(jìn)先出(FIFO)線性表稱為隊列[4],數(shù)據(jù)插入端為隊尾,數(shù)據(jù)讀取端為隊頭。線性表在物理結(jié)構(gòu)上分為順序存儲和鏈?zhǔn)酱鎯?,順序存儲在物理層上可以看做? 個大數(shù)組操作,鏈?zhǔn)酱鎯t是對分散內(nèi)存的操作。
順序存儲結(jié)構(gòu)隊列在創(chuàng)建之初需要向內(nèi)存申請1 塊連續(xù)存儲空間,隊列的操作實際是對這個內(nèi)存空間的操作,因為內(nèi)存在隊列創(chuàng)建之初大小已經(jīng)固定,所以當(dāng)進(jìn)隊數(shù)據(jù)量大于內(nèi)存長度時,會出現(xiàn)隊列滿,無法進(jìn)隊,丟棄該數(shù)據(jù),此現(xiàn)象稱為假溢出。算法設(shè)計者常將順序結(jié)構(gòu)的隊列頭、尾連接,并且設(shè)計了頭、尾指針以及相關(guān)算法,使隊列從結(jié)構(gòu)上看形成環(huán)形,稱為環(huán)形隊列(有關(guān)環(huán)形隊列原理本文不多敘述),但是環(huán)形隊列中如寫速度大于讀速度會出現(xiàn)覆蓋現(xiàn)象。相對于順序存儲固定內(nèi)存,鏈?zhǔn)酱鎯稍诓迦胫艾F(xiàn)申請內(nèi)存,可以在物理結(jié)構(gòu)上解決因隊列長度引起的問題,但是鏈?zhǔn)酱鎯Υ嬖趦?nèi)存碎片、增刪改查等操作的時間復(fù)雜度遠(yuǎn)大于順序存儲等問題,鑒于此本文所設(shè)計的雙緩存技術(shù)建立在鏈?zhǔn)酱鎯Y(jié)構(gòu)上,融入環(huán)形隊列優(yōu)點,避免過多內(nèi)存碎片產(chǎn)生基礎(chǔ)上實現(xiàn)隊列空間充裕,數(shù)據(jù)不丟失。
由數(shù)據(jù)緩存隊列與數(shù)據(jù)溢出隊列構(gòu)成的雙緩存隊列[5]中,數(shù)據(jù)緩存隊列負(fù)責(zé)實現(xiàn)基于生產(chǎn)者/消費者模型算法完成數(shù)據(jù)源輸入與源數(shù)據(jù)處理模塊之間數(shù)據(jù)通信功能,其數(shù)據(jù)結(jié)構(gòu)采用鏈?zhǔn)疥犃薪M建成單鏈表環(huán)形結(jié)構(gòu)。此數(shù)據(jù)結(jié)構(gòu)結(jié)合了順序存儲、鏈?zhǔn)酱鎯Ω髯詢?yōu)點,既具有鏈?zhǔn)酱鎯蓜討B(tài)申請內(nèi)存實現(xiàn)隊列空間動態(tài)擴(kuò)展,又避免了因頻繁申請釋放內(nèi)存帶來的內(nèi)存碎片。數(shù)據(jù)溢出隊列存儲當(dāng)數(shù)據(jù)緩存隊列中入隊數(shù)據(jù)大于出隊數(shù)據(jù)時的隊列溢出數(shù)據(jù),當(dāng)數(shù)據(jù)緩存隊列空間足夠時,同步溢出隊列中數(shù)據(jù)到數(shù)據(jù)緩存隊列。溢出隊列屬于數(shù)據(jù)緩存隊列的補(bǔ)充,使用頻率較少,使用時申請內(nèi)存,同步數(shù)據(jù)后釋放空間,單鏈表模式。
雙緩存隊列的數(shù)據(jù)結(jié)構(gòu)分為隊列整體結(jié)構(gòu)以及隊列節(jié)點結(jié)構(gòu),其中隊列節(jié)點結(jié)構(gòu)包含于隊列整體結(jié)構(gòu)中,做隊列整體結(jié)構(gòu)的節(jié)點域。隊列整體結(jié)構(gòu)由頭指針head、尾指針rear 以及節(jié)點計數(shù)器cnt 組成,head 與rear 用于進(jìn)行入隊、出隊,cnt 記錄當(dāng)前入隊數(shù)量,因為隊列中的節(jié)點是可復(fù)用的,無法通過數(shù)據(jù)域本身自帶信息判斷是否可寫入,在隊列被并發(fā)訪問時,cnt 不等于0 時,代表隊列不為空,有數(shù)據(jù)可讀;當(dāng)cnt 小于隊列預(yù)設(shè)最大節(jié)點數(shù)量時,代表有空余空間可以入隊,因此可以通過對變量cnt 操作來保證出隊、入隊安全型。節(jié)點結(jié)構(gòu)由數(shù)據(jù)域data、指針域next 構(gòu)成。
緩存隊列的數(shù)據(jù)結(jié)構(gòu)圖如圖1。
圖1 緩存隊列數(shù)據(jù)結(jié)構(gòu)圖Fig.1 data structure of cache queue
為了同時與多路傳感器進(jìn)行數(shù)據(jù)通信,系統(tǒng)采用了多線程技術(shù)[6]實現(xiàn)系統(tǒng)并發(fā)執(zhí)行。但是多線程對緩存隊列同一時間操作會產(chǎn)生異常發(fā)生,如1 個線程或多個線程同時對隊列進(jìn)行入隊操作時,如果不進(jìn)行臨界區(qū)保護(hù),會導(dǎo)致不可預(yù)知的數(shù)據(jù)損壞,發(fā)生hardfault 中斷,系統(tǒng)崩潰;另外如果1 個線程一直占用臨界區(qū),導(dǎo)致其他資源無法訪問,會導(dǎo)致其他線程發(fā)生饑餓[7]現(xiàn)象。因此多線程訪問臨界資源時,必須進(jìn)行同步控制。常用方法為信號量、條件鎖、互斥量等。
綜合各個方法優(yōu)勢,考慮每個方法可能導(dǎo)致的問題,設(shè)計如下方法:
1)條件不滿足時,掛起線程。
2)條件滿足時通過信號量釋放通知進(jìn)程訪問臨界區(qū)。
3)條件等待是線程間同步的一種手段,如果只有1 個線程,條件不滿足,一直等下去都不會滿足,所以必須要有1 個線程通過某些操作,改變共享變量,使原先不滿足的條件變得滿足,并且友好的通知等待在條件變量上的線程。
4)條件不會無緣無故的突然變得滿足了,必然會牽扯到共享數(shù)據(jù)的變化。所以一定要用互斥鎖來保護(hù)。沒有互斥鎖就無法安全的獲取和修改共享數(shù)據(jù)。
入隊操作時首先判斷緩沖隊列是否滿隊,如果隊列滿,將待入隊數(shù)據(jù)插入溢出隊列,否則插入緩沖隊列。當(dāng)緩沖隊列非滿并且溢出隊列為空,通過對cnt 值確定是否回收緩沖隊列冗余空間;若緩沖隊列非滿并且溢出隊列非空,將溢出隊列數(shù)據(jù)同步到緩沖隊列中。出隊時需要先判斷隊列是否為空,若非空說明隊列有數(shù)據(jù)可讀。在預(yù)設(shè)長度為L 的隊列中,保持head 與read 的相對空間為2 個節(jié)點數(shù)據(jù)長度,每入隊1 個數(shù)據(jù)head 循環(huán)加1,每讀出1 個數(shù)據(jù)tail 循環(huán)加1。讀寫數(shù)據(jù)時,讀寫指針以不對的速度環(huán)繞著緩沖隊列旋轉(zhuǎn),head 在前tail 在后。在數(shù)據(jù)輸入的初始階段,頭指針更新。如果指針指向緩沖區(qū)末尾部分,它就會重新回到初始位置,當(dāng)寫入抵達(dá)尾指針,就會停止,在此,緩沖區(qū)的數(shù)據(jù)量已達(dá)到頂峰,數(shù)據(jù)無法錄入。當(dāng)其指向數(shù)據(jù)區(qū)尾端,且發(fā)送工作結(jié)束,進(jìn)行位置刷新;假使其抵達(dá)緩沖區(qū)尾部,那么指針就會回到初始位置,一旦與頭指針相遇,就表明數(shù)據(jù)傳輸工作完成。隊列操作示意圖如圖2。
定義數(shù)據(jù)緩存對列為Qbuf, 溢出隊列為Qof,將Qbuf 的rear 和rear->next 分別隊列Qof 的head 與rear,為了獲取Qbuf 的隊尾節(jié)點地址,需要將Qbuf->rear->next 指向Qof 的rear,Qbuf->rear 指向Qof->head。為了實現(xiàn)多線程安全,同步過程避免緩存隊列各個數(shù)據(jù)節(jié)點次序打亂,在同步數(shù)據(jù)之前需確保緩存隊列中至少有1 個節(jié)點可寫入數(shù)據(jù)。當(dāng)Qbuf 隊列滿隊時進(jìn)行數(shù)據(jù)同步,根據(jù)Qbuf->rear 的位置可以判斷有隊列數(shù)據(jù)要出隊,如果此時暫停入隊操作將Qof 數(shù)據(jù)同步到Qbuf 隊尾后進(jìn)行出隊操作,Qbuf->head 指針將異常偏移,導(dǎo)致緩存隊列節(jié)點數(shù)據(jù)間順序錯亂。
當(dāng)緩存隊列節(jié)點cnt 不等于0 時,緩存隊列存在冗余節(jié)點,可對Qbuf 進(jìn)行冗余空間回收[8],常用算法為申請臨時節(jié)點指針t,并且t 指向待回收節(jié)點,改變rear 指針指向Qbuf->rear->next->next,釋放t,并且cnt 減1。在回收操作之前,要保證緩沖隊列至少有2 個可以寫入的空間,如果只有1 個節(jié)點可以寫入數(shù)據(jù),此時進(jìn)行回收空間操作,根據(jù)當(dāng)前的head 位置可知Qbuf 正在進(jìn)行出隊操作,如果此時暫停,將t 指向待回收節(jié)點,并且釋放。當(dāng)恢復(fù)出隊操作時,由于原待出隊節(jié)點已經(jīng)釋放,Qbuf->head 已經(jīng)下移,該指針域未知,導(dǎo)致指針越界,這是類似多路數(shù)據(jù)采集與處理系統(tǒng)[9]中最為關(guān)鍵嚴(yán)重的問題。
以沈陽研究院KJ1177X 煤礦監(jiān)控系統(tǒng)為平臺,該系統(tǒng)是全國首家符合AQ 6201—2019 行業(yè)標(biāo)準(zhǔn)[10]的煤礦監(jiān)控系統(tǒng),該系統(tǒng)下的每臺監(jiān)控分站可接32個傳感器。在實驗室采用與現(xiàn)場井工礦一致的線纜布置搭建采煤工作面環(huán)境后,其中傳感器以甲烷傳感器,風(fēng)速風(fēng)向傳感器,開停傳感器為主,并且接入少于5 臺斷電器后,配置監(jiān)控分站為采煤面煤與瓦斯突出斷電與報警模式后進(jìn)行超限斷電、故障斷線、突出預(yù)警實驗以及穩(wěn)定性試驗。使用Wireshark 工具在PC 端進(jìn)行以太網(wǎng)異常數(shù)據(jù)監(jiān)測,在監(jiān)控分站軟件中的接收傳感器數(shù)據(jù)以及上傳數(shù)據(jù)接口中分別打印相關(guān)傳感器數(shù)據(jù),人工隨機(jī)調(diào)整傳感器數(shù)據(jù)以及制造傳感器斷線故障,觀察相關(guān)斷電器是否執(zhí)行閉鎖操作,并且監(jiān)控PC 與監(jiān)控分站端所對應(yīng)的數(shù)據(jù)狀態(tài)是否一致。在閉鎖試驗后,系統(tǒng)進(jìn)行不間斷40 d 穩(wěn)定試驗,查看監(jiān)測軟件,無異常中斷與數(shù)據(jù),并且在此期間無監(jiān)控分站上傳的傳感器通信中斷指令,表明監(jiān)控分站到傳感器之間無數(shù)據(jù)丟失發(fā)生。
針對煤礦監(jiān)控系統(tǒng)中,監(jiān)控分站既需要處理中心站軟件的指令,又要與大量傳感器進(jìn)行數(shù)據(jù)通信,如果處理傳感器數(shù)據(jù)不及時或數(shù)據(jù)丟失可能判斷傳感器通信故障,導(dǎo)致相關(guān)聯(lián)斷電器斷電的問題;設(shè)計了帶有多線程安全的雙數(shù)據(jù)緩存技術(shù)的緩存隊列,監(jiān)控分站先將傳感器數(shù)據(jù)幀插入隊列存放后,再進(jìn)行解析處理,另外考慮避免饑餓或死鎖發(fā)生。實際應(yīng)用證明該數(shù)據(jù)緩存隊列軟件架構(gòu)清晰,占用內(nèi)存小,易實現(xiàn),提升了系統(tǒng)數(shù)據(jù)處理能力,解決了分站對中心站指令、傳感器數(shù)據(jù)處理不及時、丟失等問題。