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