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