自從用上緩沖,通信不再破功
元朝末年,黃河泛濫,瘟疫流行,加之官僚腐敗,漸至于民不聊生,老百姓為了活命只得揭竿而起。一時(shí)間,風(fēng)云變幻,狼煙四起。在一眾草莽英雄中,朱元璋采納謀士朱升的九字真言:“深挖洞、廣積糧、緩稱王”,韜光養(yǎng)晦,積蓄力量,最終定鼎天下,平定四方。
本文引用地址:http://www.ex-cimer.com/article/202005/412945.htm再后來,中蘇交惡時(shí)期,毛主席也振聾發(fā)聵地提出“深挖洞、廣積糧、不稱霸”的號(hào)召。
兩朝太祖都是百年一遇的政治家、軍事家,英雄所見略同,他們深知戰(zhàn)略儲(chǔ)備的力量,曉得唯有建設(shè)深厚的國家儲(chǔ)備,才不至于陣亡于暗暗長夜而等不來那終將來臨的天光。
就拿現(xiàn)在來說吧,我輩吃瓜群眾有福氣,可以好整以暇地看美國各個(gè)州的州長和特朗普在推特上罵來罵去地打嘴炮,其實(shí)這還不是因?yàn)槊绹?lián)邦政府儲(chǔ)備的醫(yī)療物資消耗殆盡,沒有應(yīng)急的儲(chǔ)備造成的?
這些儲(chǔ)備平時(shí)躺在倉庫里睡大覺,還要付出長期維護(hù)和定期更換的高昂成本,但是它們在關(guān)鍵時(shí)刻能救命,疫情連三月,呼吸機(jī)抵萬金吶!當(dāng)需求高峰期來時(shí),這些平時(shí)沒啥用的儲(chǔ)備可以贏得寶貴的時(shí)間,挽救脆弱的生命!
不過,立國不到三百年的美國人哪有這種歷史感悟?
通過建立“空間”縱深,以應(yīng)對時(shí)間密集型的突發(fā)需求,拿空間換時(shí)間,這就是“儲(chǔ)備”的意義。
在程序員的世界里,這種儲(chǔ)備叫做“緩沖”。今天,筆者就跟大家分享一個(gè)多年前發(fā)生在自己身上的案例,一個(gè)因?yàn)闆]有使用儲(chǔ)備導(dǎo)致“數(shù)據(jù)丟失”的故事。
一
那正是天寒地凍的時(shí)節(jié)。
窗外狂風(fēng)席卷,人影難覓,只有一面冷颼颼的月亮像瑤臺(tái)的鏡子,遠(yuǎn)遠(yuǎn)地掛在云端。那天,甚是高遠(yuǎn),似穹廬,籠蓋在一座小樓的上方。
那樓里只亮著一盞燈,亮燈的房間里只有一個(gè)人。
天高云淡,這個(gè)房間很孤單,這個(gè)人也很孤單。
這個(gè)人,就是在下!
月黑風(fēng)高夜,正是捉蟲時(shí),沒錯(cuò),別看在下枯坐已久,但腦袋卻在轉(zhuǎn)個(gè)不停,在下正在對著電腦屏幕找bug!
當(dāng)時(shí),項(xiàng)目組正在做一款藍(lán)牙娛樂設(shè)備,概而言之,這款設(shè)備插上U盤能放歌,接上藍(lán)牙能免提,連上手機(jī)還能用音頻流播放手機(jī)里的音樂。
現(xiàn)在說來這些都不算啥,但是在十余年前,那可算是個(gè)新概念。
這個(gè)設(shè)備的開發(fā)采用了雙處理器方案,概而言之,一顆主控處理器處理人機(jī)接口,主要功能是以按鍵和顯示屏的方式管理播放列表、通話和音樂播放,還有一顆藍(lán)牙單芯片處理和手機(jī)的藍(lán)牙通信,主要功能是把來電請求、通話狀態(tài)發(fā)給主控處理器,同時(shí)接收來自主控的接聽/掛斷電話指令、音樂控制指令等。
主控處理器是個(gè)32位的單片機(jī),藍(lán)牙芯片選用CSR集成了藍(lán)牙基帶的單芯片,兩者通過SCI接口進(jìn)行通信。
在下不才,在里面負(fù)責(zé)藍(lán)牙單芯片的開發(fā)。
二
如前所述,這兩顆單片機(jī)以SCI接口進(jìn)行通信。為了更順口一些,還是說串口吧,只不過,大多數(shù)人印象中的串口是RS232,它主要用于設(shè)備間的通信,而筆者這里是同一個(gè)設(shè)備電路中的通信,沒有走RS232電平,直接走TTL電平。
有串口通信就有通信協(xié)議,為了減輕主控開發(fā)人員的負(fù)擔(dān),在下自告奮勇地承擔(dān)了協(xié)議的制定工作,卻不成想,這倒成了我后來“背鍋”的原因。
剛開始,我和負(fù)責(zé)主控芯片軟件開發(fā)的李工一邊喝著茶水磨牙拌嘴,一邊“你打你的,我打我的”地加班加點(diǎn),偶有串口聯(lián)調(diào)通信,也是一切順利,萬事大吉,直到突如其來的數(shù)據(jù)丟失把這種歲月靜好打成了滿地狼藉。
那是將要起風(fēng)的一天傍晚,同事們都各自歸家,游戲人間煙火去了,獨(dú)獨(dú)剩下我和李工還在苦逼地寫代碼。
北島說:如果你是一條船,漂泊就是你的命運(yùn),可別靠岸。領(lǐng)導(dǎo)說:如果你是一個(gè)工程師,加班就是你的命運(yùn),可別想著早下班。
想著那些早下班的同事,我也想到一句話:哪里有什么歲月靜好,只不過我和李工在替你們負(fù)重前行!
辦公室里萬籟俱靜,靜的有些出奇,李工在一旁眉頭緊鎖,間或捏著下巴頦子向我投來深情的一瞥,直讓人起雞皮疙瘩。我在一旁也打起了嘀咕,“這廝有事?”
果然,李工帶著斟酌的語氣開口了,“天雷君,你定的串口通信協(xié)議莫不是有問題?感覺丟數(shù)據(jù)呢!”
原來,從今天下午起,李工做通話管理那部分程序時(shí),有時(shí)候得不到正確順序的數(shù)據(jù)。比方說,手機(jī)來電話了,用戶直接在手機(jī)上接通了,藍(lán)牙芯片這邊本來會(huì)按順序發(fā)過來“來電請求、接通等待、接通通話”,可是有的時(shí)候,沒有“來電請求”就直接把“接通等待”這個(gè)報(bào)文發(fā)過來了。
而通話管理程序?qū)嶋H上是一個(gè)狀態(tài)機(jī),按照這幾條報(bào)文跳轉(zhuǎn)通話狀態(tài),現(xiàn)在報(bào)文次序不對,狀態(tài)機(jī)自然就亂套了。
問題是顯然的,原因似乎也是呼之欲出的。按李工的說法,是藍(lán)牙鏈路的射頻通信干擾了串口通信,導(dǎo)致報(bào)文里的數(shù)據(jù)出錯(cuò),按照李工的提議,應(yīng)該修改串口通信協(xié)議,每條報(bào)文應(yīng)該連發(fā)兩次,這樣才能保證出錯(cuò)的幾率大大降低。
那時(shí)我還年輕,慣于把別人甩的鍋?zhàn)杂X地戴在自己頭上。于是,我默默地收起內(nèi)心的驕傲,采納了他的意見。
不曾想,待我把報(bào)文發(fā)送改成連發(fā)兩次后,問題出現(xiàn)的幾率似乎更高了??!
于是,李工給我判了刑,要求大改通信協(xié)議,然后就拂袖回家了。
三
我嘗遍世間冷暖炎涼,但依然愿在薄情的世界里深情地活著——這才是生活。
李工走后,偌大的辦公室只剩下我一個(gè)人了。
月亮漸漸爬上樹梢,寒風(fēng)在窗外咆哮,月亮很孤單,我也很孤單。
我孤獨(dú)地看著李工留給我的代碼,在這蕭殺的寂靜里,捕捉著不知藏身何處的bug。
是的,“嚴(yán)于律人、寬以待己”的我可沒覺得自己有哪里不對,‘通信協(xié)議有什么好改的?’我一邊在鼻尖哼著氣,一邊看李工寫的代碼。
在李工的程序里,是通過中斷接收串口發(fā)送的字節(jié),然后在一個(gè)單獨(dú)的任務(wù)解析報(bào)文的,解析出一條完整的報(bào)文后,再根據(jù)報(bào)文的含義向其它相應(yīng)的任務(wù)里發(fā)消息。
我看了看李工為串口報(bào)文解析任務(wù)設(shè)定的優(yōu)先級(jí),居然是最低的!
其實(shí),當(dāng)時(shí)我也不知道該怎么設(shè)置任務(wù)的優(yōu)先級(jí),但是,聯(lián)想到之前李工指控我通信協(xié)議有問題的情景,我就是覺得:怎么能夠把這么‘重要’的任務(wù)設(shè)置成最低的優(yōu)先級(jí)呢?
我一邊在鼻孔哼著氣,一邊改了任務(wù)的優(yōu)先級(jí)。三下五除二,再調(diào)試一把,還別說,果然好多了,測了好幾遍,沒問題!
既如此,我釋然了。根本不是通信協(xié)議的事兒,而是報(bào)文一股腦地發(fā)過來時(shí),主控這邊處理報(bào)文不及時(shí),導(dǎo)致“來電請求”報(bào)文還沒解析完時(shí),其中的數(shù)據(jù)就被破壞了。
于是,我恍然了。任務(wù)優(yōu)先級(jí)設(shè)置不同,會(huì)導(dǎo)致這么明顯的差異,這實(shí)際上也給我敲響了警鐘:任務(wù)優(yōu)先級(jí)不要隨便動(dòng)!
可是,我又再度默然了。我這么貿(mào)然地改了優(yōu)先級(jí),是不是可能會(huì)有很多其它功能出現(xiàn)莫名其妙的故障呢?于是,我一個(gè)激靈,默默地把優(yōu)先級(jí)改了回去。
四
歲月如水,撥動(dòng)著墻上的鐘表指針,也撥動(dòng)著我的心弦。
看著眼前剩下的半杯水,我生起了哲學(xué)的深思:“人生,過一天少一天,可是我們并不急著把這一輩子過完;水杯,喝一口少一口,可是我們并不想著一口氣喝光。壽命、水杯都是一種緩沖型的容器,讓我們可以安步當(dāng)車,不疾不徐。”
那串口接收豈非也是如此?只要給它一個(gè)足夠的緩沖,即便短時(shí)期內(nèi)來了好幾條報(bào)文,也不妨礙我們一條一條地處理了!
開了竅的我,帶著興奮的心情,在李工的代碼里實(shí)現(xiàn)了一個(gè)環(huán)形緩沖器。拿出512個(gè)字節(jié),開個(gè)數(shù)組,建立兩個(gè)索引,分別標(biāo)記讀取位置和寫入位置,這些索引到了512后自動(dòng)歸零。剛開始時(shí),自然是寫入索引大于讀取索引,數(shù)據(jù)就這樣魚貫而入魚貫而出。
慢慢地,只要寫入索引的回零次數(shù)不大于(讀取索引回零次數(shù)+1),即使出現(xiàn)了數(shù)據(jù)堆積,只要假以時(shí)間,也能準(zhǔn)確無誤地把數(shù)據(jù)消費(fèi)完。而一旦出現(xiàn)寫入索引回零次數(shù)大于讀取索引回零次數(shù)+1,就表示數(shù)據(jù)出現(xiàn)了溢出,此時(shí)調(diào)大緩沖區(qū)的大小即可。
問題就這么順利地解決了,自從用上了緩沖,后面的通信便沒有再破過功。
至于李工,他看完被我改造過的代碼,再次給我投來深情的一瞥。我回以嫣然一笑,對著他帥氣的臉龐吐出兩句詩:桃花潭水深千尺,不及我跟李工情吶!
評論